diff --git a/app/assets/javascripts/admin/addon/mixins/setting-component.js b/app/assets/javascripts/admin/addon/mixins/setting-component.js
index 8fa9c076c00..fb77bcf3b32 100644
--- a/app/assets/javascripts/admin/addon/mixins/setting-component.js
+++ b/app/assets/javascripts/admin/addon/mixins/setting-component.js
@@ -57,6 +57,7 @@ const DEFAULT_USER_PREFERENCES = [
"default_other_notification_level_when_replying",
"default_other_external_links_in_new_tab",
"default_other_enable_quoting",
+ "default_other_enable_smart_lists",
"default_other_enable_defer",
"default_other_dynamic_favicon",
"default_other_like_notification_frequency",
diff --git a/app/assets/javascripts/discourse/app/components/d-editor.js b/app/assets/javascripts/discourse/app/components/d-editor.js
index fdbaf179d59..a1c863b30fa 100644
--- a/app/assets/javascripts/discourse/app/components/d-editor.js
+++ b/app/assets/javascripts/discourse/app/components/d-editor.js
@@ -374,11 +374,13 @@ export default class DEditor extends Component {
//
// c.f. https://developer.mozilla.org/en-US/docs/Web/API/Element/beforeinput_event
if (this._textarea) {
- this._textarea.addEventListener(
- "beforeinput",
- this.onBeforeInputSmartList
- );
- this._textarea.addEventListener("input", this.onInputSmartList);
+ if (this.currentUser.user_option.enable_smart_lists) {
+ this._textarea.addEventListener(
+ "beforeinput",
+ this.onBeforeInputSmartList
+ );
+ this._textarea.addEventListener("input", this.onInputSmartList);
+ }
this.element.addEventListener("paste", this.textManipulation.paste);
}
@@ -483,11 +485,13 @@ export default class DEditor extends Component {
}
if (this._textarea) {
- this._textarea.removeEventListener(
- "beforeinput",
- this.onBeforeInputSmartList
- );
- this._textarea.removeEventListener("input", this.onInputSmartList);
+ if (this.currentUser.user_option.enable_smart_lists) {
+ this._textarea.removeEventListener(
+ "beforeinput",
+ this.onBeforeInputSmartList
+ );
+ this._textarea.removeEventListener("input", this.onInputSmartList);
+ }
}
this._itsatrap?.destroy();
diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/interface.js b/app/assets/javascripts/discourse/app/controllers/preferences/interface.js
index 1829cae9f76..18fb89220b9 100644
--- a/app/assets/javascripts/discourse/app/controllers/preferences/interface.js
+++ b/app/assets/javascripts/discourse/app/controllers/preferences/interface.js
@@ -68,6 +68,7 @@ export default class InterfaceController extends Controller {
"external_links_in_new_tab",
"dynamic_favicon",
"enable_quoting",
+ "enable_smart_lists",
"enable_defer",
"automatically_unpin_topics",
"allow_private_messages",
diff --git a/app/assets/javascripts/discourse/app/models/user.js b/app/assets/javascripts/discourse/app/models/user.js
index a32d63ca0ba..5d0d6a31675 100644
--- a/app/assets/javascripts/discourse/app/models/user.js
+++ b/app/assets/javascripts/discourse/app/models/user.js
@@ -108,6 +108,7 @@ let userOptionFields = [
"dark_scheme_id",
"dynamic_favicon",
"enable_quoting",
+ "enable_smart_lists",
"enable_defer",
"automatically_unpin_topics",
"digest_after_minutes",
@@ -182,6 +183,7 @@ export default class User extends RestModel.extend(Evented) {
@userOption("mailing_list_mode") mailing_list_mode;
@userOption("external_links_in_new_tab") external_links_in_new_tab;
@userOption("enable_quoting") enable_quoting;
+ @userOption("enable_smart_lists") enable_smart_lists;
@userOption("dynamic_favicon") dynamic_favicon;
@userOption("automatically_unpin_topics") automatically_unpin_topics;
@userOption("likes_notifications_disabled") likes_notifications_disabled;
diff --git a/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs b/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs
index fcc86a0a8e8..f4a04c2ee8a 100644
--- a/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs
+++ b/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs
@@ -177,6 +177,12 @@
data-setting-name="user-enable-quoting"
class="pref-enable-quoting"
/>
+
`);
@@ -1017,6 +1022,23 @@ third line`
});
}
+ testCase(
+ "smart lists - when enable_smart_lists is false pressing enter on a line with a list item starting with *",
+ async function (assert, textarea) {
+ const initialValue = "* first item in list\n";
+ this.set("value", initialValue);
+ setCaretPosition(textarea, initialValue.length);
+ await triggerEnter(textarea);
+
+ assert.strictEqual(
+ this.value,
+ initialValue,
+ "it does not create an empty list item on the next line"
+ );
+ },
+ { enable_smart_lists: false }
+ );
+
testCase(
"smart lists - pressing enter on a line with a list item starting with *",
async function (assert, textarea) {
@@ -1030,7 +1052,8 @@ third line`
initialValue + "* ",
"it creates a list item on the next line"
);
- }
+ },
+ { enable_smart_lists: true }
);
testCase(
@@ -1046,7 +1069,8 @@ third line`
initialValue + "",
"it doesn’t continue the list"
);
- }
+ },
+ { enable_smart_lists: true }
);
testCase(
@@ -1062,7 +1086,8 @@ third line`
initialValue + "* ",
"it continues the list"
);
- }
+ },
+ { enable_smart_lists: true }
);
testCase(
@@ -1073,7 +1098,8 @@ third line`
setCaretPosition(textarea, initialValue.length);
await triggerEnter(textarea);
assert.strictEqual(this.value, initialValue + "- ");
- }
+ },
+ { enable_smart_lists: true }
);
testCase(
@@ -1088,7 +1114,8 @@ third line`
initialValue + "2. ",
"it creates a list item on the next line with an auto-incremented number"
);
- }
+ },
+ { enable_smart_lists: true }
);
testCase(
@@ -1103,7 +1130,8 @@ third line`
"* first item in list\n* \n* second item in list",
"it inserts a new list item on the next line"
);
- }
+ },
+ { enable_smart_lists: true }
);
testCase(
@@ -1118,7 +1146,8 @@ third line`
"1. first item in list\n2. \n3. second item in list",
"it inserts a new list item on the next line and renumbers the rest of the list"
);
- }
+ },
+ { enable_smart_lists: true }
);
testCase(
@@ -1133,7 +1162,8 @@ third line`
"* first item in list with empty line\n",
"it removes the list item"
);
- }
+ },
+ { enable_smart_lists: true }
);
(() => {
diff --git a/app/models/user_option.rb b/app/models/user_option.rb
index d8c45caeb00..a61091c5af2 100644
--- a/app/models/user_option.rb
+++ b/app/models/user_option.rb
@@ -73,6 +73,7 @@ class UserOption < ActiveRecord::Base
self.email_in_reply_to = SiteSetting.default_email_in_reply_to
self.enable_quoting = SiteSetting.default_other_enable_quoting
+ self.enable_smart_lists = SiteSetting.default_other_enable_smart_lists
self.enable_defer = SiteSetting.default_other_enable_defer
self.external_links_in_new_tab = SiteSetting.default_other_external_links_in_new_tab
self.dynamic_favicon = SiteSetting.default_other_dynamic_favicon
@@ -284,6 +285,7 @@ end
# chat_separate_sidebar_mode :integer default(0), not null
# topics_unread_when_closed :boolean default(TRUE), not null
# show_thread_title_prompts :boolean default(TRUE), not null
+# enable_smart_lists :boolean default(TRUE), not null
#
# Indexes
#
diff --git a/app/serializers/current_user_option_serializer.rb b/app/serializers/current_user_option_serializer.rb
index 09810ff33f7..6911a8c026a 100644
--- a/app/serializers/current_user_option_serializer.rb
+++ b/app/serializers/current_user_option_serializer.rb
@@ -4,6 +4,7 @@ class CurrentUserOptionSerializer < ApplicationSerializer
attributes :mailing_list_mode,
:external_links_in_new_tab,
:enable_quoting,
+ :enable_smart_lists,
:dynamic_favicon,
:automatically_unpin_topics,
:likes_notifications_disabled,
diff --git a/app/serializers/user_option_serializer.rb b/app/serializers/user_option_serializer.rb
index 97804335402..466d6046555 100644
--- a/app/serializers/user_option_serializer.rb
+++ b/app/serializers/user_option_serializer.rb
@@ -12,6 +12,7 @@ class UserOptionSerializer < ApplicationSerializer
:dark_scheme_id,
:dynamic_favicon,
:enable_quoting,
+ :enable_smart_lists,
:enable_defer,
:digest_after_minutes,
:automatically_unpin_topics,
diff --git a/app/services/site_setting_update_existing_users.rb b/app/services/site_setting_update_existing_users.rb
index 32a6fecb8f1..32313282862 100644
--- a/app/services/site_setting_update_existing_users.rb
+++ b/app/services/site_setting_update_existing_users.rb
@@ -113,6 +113,7 @@ class SiteSettingUpdateExistingUsers
default_email_previous_replies: "email_previous_replies",
default_email_in_reply_to: "email_in_reply_to",
default_other_enable_quoting: "enable_quoting",
+ default_other_enable_smart_lists: "enable_smart_lists",
default_other_enable_defer: "enable_defer",
default_other_external_links_in_new_tab: "external_links_in_new_tab",
default_other_dynamic_favicon: "dynamic_favicon",
diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb
index a2e28d2d612..3e67a55f761 100644
--- a/app/services/user_updater.rb
+++ b/app/services/user_updater.rb
@@ -24,6 +24,7 @@ class UserUpdater
email_messages_level
external_links_in_new_tab
enable_quoting
+ enable_smart_lists
enable_defer
color_scheme_id
dark_scheme_id
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 4f494b1c655..a8126c5978b 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -1358,6 +1358,7 @@ en:
allow_private_messages: "Allow other users to send me personal messages"
external_links_in_new_tab: "Open all external links in a new tab"
enable_quoting: "Enable quote reply for highlighted text"
+ enable_smart_lists: "Enable smart lists when writing in the composer"
enable_defer: "Enable mark topics as unread"
experimental_sidebar:
enable: "Enable sidebar"
@@ -1427,7 +1428,7 @@ en:
ignored_users: "Ignored"
ignored_users_instructions: "Suppress all posts, notifications, and PMs from these users."
tracked_topics_link: "Show"
- automatically_unpin_topics: "Automatically unpin topics when I reach the bottom."
+ automatically_unpin_topics: "Automatically unpin topics when I reach the bottom"
apps: "Apps"
revoke_access: "Revoke Access"
undo_revoke_access: "Undo Revoke Access"
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 2ed1a64bd31..e03cda120ed 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -2602,6 +2602,7 @@ en:
default_other_notification_level_when_replying: "Global default notification level when the user replies to a topic."
default_other_external_links_in_new_tab: "Open external links in a new tab by default."
default_other_enable_quoting: "Enable quote reply for highlighted text by default."
+ default_other_enable_smart_lists: "Enable smart lists when typing in the composer by default."
default_other_enable_defer: "Enable defer topic functionality by default."
default_other_dynamic_favicon: "Show new/updated topic count on browser icon by default."
default_other_skip_new_user_tips: "Skip new user onboarding tips and badges."
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 012df4fe9ab..44f4b2f3d2c 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -3059,6 +3059,7 @@ user_preferences:
default_other_external_links_in_new_tab: false
default_other_enable_quoting: true
default_other_enable_defer: false
+ default_other_enable_smart_lists: true
default_other_dynamic_favicon: false
default_other_skip_new_user_tips: false
default_other_like_notification_frequency:
diff --git a/db/migrate/20241028021339_add_smart_list_user_preference.rb b/db/migrate/20241028021339_add_smart_list_user_preference.rb
new file mode 100644
index 00000000000..f93c409b58d
--- /dev/null
+++ b/db/migrate/20241028021339_add_smart_list_user_preference.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddSmartListUserPreference < ActiveRecord::Migration[7.1]
+ def change
+ add_column :user_options, :enable_smart_lists, :boolean, default: true, null: false
+ end
+end
diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake
index a7d1e844a50..8cdebd78302 100644
--- a/lib/tasks/import.rake
+++ b/lib/tasks/import.rake
@@ -164,6 +164,7 @@ def insert_user_options
include_tl0_in_digests,
automatically_unpin_topics,
enable_quoting,
+ enable_smart_lists,
external_links_in_new_tab,
dynamic_favicon,
new_topic_duration_minutes,
@@ -187,6 +188,7 @@ def insert_user_options
, #{SiteSetting.default_include_tl0_in_digests}
, #{SiteSetting.default_topics_automatic_unpin}
, #{SiteSetting.default_other_enable_quoting}
+ , #{SiteSetting.default_other_enable_smart_lists}
, #{SiteSetting.default_other_external_links_in_new_tab}
, #{SiteSetting.default_other_dynamic_favicon}
, #{SiteSetting.default_other_new_topic_duration_minutes}
diff --git a/script/bulk_import/base.rb b/script/bulk_import/base.rb
index f054eb3fd98..93ddecf3a1c 100644
--- a/script/bulk_import/base.rb
+++ b/script/bulk_import/base.rb
@@ -577,6 +577,7 @@ class BulkImport::Base
include_tl0_in_digests
automatically_unpin_topics
enable_quoting
+ enable_smart_lists
external_links_in_new_tab
dynamic_favicon
new_topic_duration_minutes
@@ -1306,6 +1307,7 @@ class BulkImport::Base
include_tl0_in_digests: SiteSetting.default_include_tl0_in_digests,
automatically_unpin_topics: SiteSetting.default_topics_automatic_unpin,
enable_quoting: SiteSetting.default_other_enable_quoting,
+ enable_smart_lists: SiteSetting.default_other_enable_smart_lists,
external_links_in_new_tab: SiteSetting.default_other_external_links_in_new_tab,
dynamic_favicon: SiteSetting.default_other_dynamic_favicon,
new_topic_duration_minutes: SiteSetting.default_other_new_topic_duration_minutes,
diff --git a/spec/models/user_option_spec.rb b/spec/models/user_option_spec.rb
index 175747f0f9a..107b5463cc0 100644
--- a/spec/models/user_option_spec.rb
+++ b/spec/models/user_option_spec.rb
@@ -73,6 +73,7 @@ RSpec.describe UserOption do
describe "site settings" do
it "should apply defaults from site settings" do
SiteSetting.default_other_enable_quoting = false
+ SiteSetting.default_other_enable_smart_lists = false
SiteSetting.default_other_enable_defer = true
SiteSetting.default_other_external_links_in_new_tab = true
SiteSetting.default_other_dynamic_favicon = true
@@ -81,6 +82,7 @@ RSpec.describe UserOption do
user = Fabricate(:user)
expect(user.user_option.enable_quoting).to eq(false)
+ expect(user.user_option.enable_smart_lists).to eq(false)
expect(user.user_option.enable_defer).to eq(true)
expect(user.user_option.external_links_in_new_tab).to eq(true)
expect(user.user_option.dynamic_favicon).to eq(true)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 0d62b131305..9c58f4f4ab8 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2163,6 +2163,7 @@ RSpec.describe User do
SiteSetting.default_other_notification_level_when_replying = 3 # immediately
SiteSetting.default_other_external_links_in_new_tab = true
SiteSetting.default_other_enable_quoting = false
+ SiteSetting.default_other_enable_smart_lists = false
SiteSetting.default_other_dynamic_favicon = true
SiteSetting.default_other_skip_new_user_tips = true
@@ -2185,6 +2186,7 @@ RSpec.describe User do
expect(options.email_messages_level).to eq(UserOption.email_level_types[:never])
expect(options.external_links_in_new_tab).to eq(true)
expect(options.enable_quoting).to eq(false)
+ expect(options.enable_smart_lists).to eq(false)
expect(options.dynamic_favicon).to eq(true)
expect(options.skip_new_user_tips).to eq(true)
expect(options.hide_profile_and_presence).to eq(true)
diff --git a/spec/requests/api/schemas/json/user_get_response.json b/spec/requests/api/schemas/json/user_get_response.json
index 6783e532217..263a0f0096f 100644
--- a/spec/requests/api/schemas/json/user_get_response.json
+++ b/spec/requests/api/schemas/json/user_get_response.json
@@ -714,6 +714,9 @@
"enable_quoting": {
"type": "boolean"
},
+ "enable_smart_lists": {
+ "type": "boolean"
+ },
"enable_defer": {
"type": "boolean"
},
@@ -820,6 +823,7 @@
"dark_scheme_id",
"dynamic_favicon",
"enable_quoting",
+ "enable_smart_lists",
"enable_defer",
"digest_after_minutes",
"automatically_unpin_topics",