diff --git a/app/assets/javascripts/discourse/app/components/user-nav/preferences-nav.hbs b/app/assets/javascripts/discourse/app/components/user-nav/preferences-nav.hbs index 29e1c35c5d1..45a007b6c3f 100644 --- a/app/assets/javascripts/discourse/app/components/user-nav/preferences-nav.hbs +++ b/app/assets/javascripts/discourse/app/components/user-nav/preferences-nav.hbs @@ -4,58 +4,58 @@ <span>{{i18n "user.preferences_nav.account"}}</span> </LinkTo> </li> + <li class="nav-security"> <LinkTo @route="preferences.security"> {{d-icon "lock"}} <span>{{i18n "user.preferences_nav.security"}}</span> </LinkTo> </li> + <li class="nav-profile"> <LinkTo @route="preferences.profile"> {{d-icon "user"}} <span>{{i18n "user.preferences_nav.profile"}}</span> </LinkTo> </li> + <li class="nav-emails"> <LinkTo @route="preferences.emails"> {{d-icon "envelope"}} <span>{{i18n "user.preferences_nav.emails"}}</span> </LinkTo> </li> + <li class="nav-notifications"> <LinkTo @route="preferences.notifications"> {{d-icon "bell"}} <span>{{i18n "user.preferences_nav.notifications"}}</span> </LinkTo> </li> + {{#if @model.can_change_tracking_preferences}} - <li class="indent nav-categories"> - <LinkTo @route="preferences.categories"> - {{d-icon "folder"}} - <span>{{i18n "user.preferences_nav.categories"}}</span> + <li class="nav-tracking"> + <LinkTo @route="preferences.tracking"> + {{d-icon "plus"}} + <span>{{i18n "user.preferences_nav.tracking"}}</span> </LinkTo> </li> {{/if}} + <li class="indent nav-users"> <LinkTo @route="preferences.users"> {{d-icon "users"}} <span>{{i18n "user.preferences_nav.users"}}</span> </LinkTo> </li> -{{#if (and @model.can_change_tracking_preferences @siteSettings.tagging_enabled)}} - <li class="indent nav-tags"> - <LinkTo @route="preferences.tags"> - {{d-icon "tag"}} - <span>{{i18n "user.preferences_nav.tags"}}</span> - </LinkTo> - </li> -{{/if}} + <li class="nav-interface"> <LinkTo @route="preferences.interface"> {{d-icon "desktop"}} <span>{{i18n "user.preferences_nav.interface"}}</span> </LinkTo> </li> + {{#if @siteSettings.enable_experimental_sidebar_hamburger}} <li class="indent nav-sidebar"> <LinkTo @route="preferences.sidebar"> @@ -64,11 +64,6 @@ </LinkTo> </li> {{/if}} + <PluginOutlet @name="user-preferences-nav-under-interface" @connectorTagName="div" @args={{hash model=@model}} /> -<li class="nav-apps"> - <LinkTo @route="preferences.apps"> - {{d-icon "mobile-alt"}} - <span>{{i18n "user.preferences_nav.apps"}}</span> - </LinkTo> -</li> <PluginOutlet @name="user-preferences-nav" @connectorTagName="li" @args={{hash model=@model}} /> diff --git a/app/assets/javascripts/discourse/app/components/user-preferences/allow-private-messages.hbs b/app/assets/javascripts/discourse/app/components/user-preferences/allow-private-messages.hbs new file mode 100644 index 00000000000..82afce8d817 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/user-preferences/allow-private-messages.hbs @@ -0,0 +1,6 @@ +<div class="control-group private-messages"> + <label class="control-label">{{i18n "user.private_messages"}}</label> + <div class="controls"> + <PreferenceCheckbox @labelKey="user.allow_private_messages" @checked={{@model.user_option.allow_private_messages}} /> + </div> +</div> diff --git a/app/assets/javascripts/discourse/app/components/user-preferences/categories.hbs b/app/assets/javascripts/discourse/app/components/user-preferences/categories.hbs new file mode 100644 index 00000000000..db5cb241df9 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/user-preferences/categories.hbs @@ -0,0 +1,53 @@ +<div class="control-group category-notifications"> + <label class="control-label">{{i18n "user.categories_settings"}}</label> + + <div class="controls tracking-controls tracking-controls--watched-categories"> + <label>{{d-icon "d-watching"}} {{i18n "user.watched_categories"}}</label> + {{#if @canSee}} + <a class="show-tracking" href={{@model.watchingTopicsPath}}>{{i18n "user.tracked_topics_link"}}</a> + {{/if}} + <CategorySelector @categories={{@model.watchedCategories}} @blockedCategories={{@selectedCategories}} @onChange={{action (mut @model.watchedCategories)}} /> + </div> + <div class="instructions">{{i18n "user.watched_categories_instructions"}}</div> + + <div class="controls tracking-controls tracking-controls--tracked-categories"> + <label>{{d-icon "d-tracking"}} {{i18n "user.tracked_categories"}}</label> + {{#if @canSee}} + <a class="show-tracking" href={{@model.trackingTopicsPath}}>{{i18n "user.tracked_topics_link"}}</a> + {{/if}} + <CategorySelector @categories={{@model.trackedCategories}} @blockedCategories={{@selectedCategories}} @onChange={{action (mut @model.trackedCategories)}} /> + </div> + <div class="instructions">{{i18n "user.tracked_categories_instructions"}}</div> + + <div class="controls tracking-controls tracking-controls--watched-first-categories"> + <label>{{d-icon "d-watching-first"}} {{i18n "user.watched_first_post_categories"}}</label> + <CategorySelector @categories={{@model.watchedFirstPostCategories}} @blockedCategories={{@selectedCategories}} @onChange={{action (mut @model.watchedFirstPostCategories)}} /> + </div> + <div class="instructions">{{i18n "user.watched_first_post_categories_instructions"}}</div> + + {{#if @siteSettings.mute_all_categories_by_default}} + <div class="controls tracking-controls tracking-controls--regular-categories"> + <label>{{d-icon "d-regular"}} {{i18n "user.regular_categories"}}</label> + <CategorySelector @categories={{@model.regularCategories}} @blockedCategories={{@selectedCategories}} @onChange={{action (mut @model.regularCategories)}} /> + </div> + <div class="instructions">{{i18n "user.regular_categories_instructions"}}</div> + {{else}} + <div class="controls tracking-controls tracking-controls--muted-categories"> + <label>{{d-icon "d-muted"}} {{i18n "user.muted_categories"}}</label> + + {{#if @canSee}} + <a class="show-tracking" href={{@model.mutedTopicsPath}}>{{i18n "user.tracked_topics_link"}}</a> + {{/if}} + + <CategorySelector @categories={{@model.mutedCategories}} @blockedCategories={{@selectedCategories}} @onChange={{action (mut @model.mutedCategories)}} /> + </div> + + <div class="instructions">{{i18n (if @hideMutedTags "user.muted_categories_instructions" "user.muted_categories_instructions_dont_hide")}}</div> + {{/if}} +</div> + +<PluginOutlet @name="user-preferences-categories" @tagName="span" @connectorTagName="div" @args={{hash model=@model save=@save}} /> + +<br> + +<PluginOutlet @name="user-custom-controls" @tagName="span" @connectorTagName="div" @args={{hash model=@model}} /> diff --git a/app/assets/javascripts/discourse/app/components/user-preferences/tags.hbs b/app/assets/javascripts/discourse/app/components/user-preferences/tags.hbs new file mode 100644 index 00000000000..a63a17ca4ae --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/user-preferences/tags.hbs @@ -0,0 +1,45 @@ +{{#if @siteSettings.tagging_enabled}} + <div class="control-group tag-notifications"> + <label class="control-label">{{i18n "user.tag_settings"}}</label> + + <div class="controls tracking-controls"> + <label>{{d-icon "d-watching" class="icon watching"}} {{i18n "user.watched_tags"}}</label> + <TagChooser @tags={{@model.watched_tags}} @blockedTags={{@selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash + allowAny=false + }} /> + </div> + + <div class="instructions">{{i18n "user.watched_tags_instructions"}}</div> + + <div class="controls tracking-controls"> + <label>{{d-icon "d-tracking" class="icon tracking"}} {{i18n "user.tracked_tags"}}</label> + <TagChooser @tags={{@model.tracked_tags}} @blockedTags={{@selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash + allowAny=false + }} /> + </div> + + <div class="instructions">{{i18n "user.tracked_tags_instructions"}}</div> + + <div class="controls tracking-controls"> + <label>{{d-icon "d-watching-first" class="icon watching-first-post"}} {{i18n "user.watched_first_post_tags"}}</label> + <TagChooser @tags={{@model.watching_first_post_tags}} @blockedTags={{@selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash + allowAny=false + }} /> + </div> + + <div class="instructions"> + {{i18n "user.watched_first_post_tags_instructions"}} + </div> + + <div class="controls tracking-controls"> + <label>{{d-icon "d-muted" class="icon muted"}} {{i18n "user.muted_tags"}}</label> + <TagChooser @tags={{@model.muted_tags}} @blockedTags={{@selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash + allowAny=false + }} /> + </div> + <div class="instructions">{{i18n "user.muted_tags_instructions"}}</div> + </div> + + <PluginOutlet @name="user-preferences-tags" @connectorTagName="div" @args={{hash model=@model save=@save}} /> + <PluginOutlet @name="user-custom-controls" @connectorTagName="div" @args={{hash model=@model}} /> +{{/if}} diff --git a/app/assets/javascripts/discourse/app/components/user-preferences/topic-tracking.hbs b/app/assets/javascripts/discourse/app/components/user-preferences/topic-tracking.hbs new file mode 100644 index 00000000000..75e772cb73c --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/user-preferences/topic-tracking.hbs @@ -0,0 +1,16 @@ +<div class="user-preferences_tracking-topics-controls"> + <div class="controls controls-dropdown"> + <label>{{i18n "user.new_topic_duration.label"}}</label> + <ComboBox @class="duration" @valueProperty="value" @content={{@considerNewTopicOptions}} @value={{@model.user_option.new_topic_duration_minutes}} @onChange={{action (mut @model.user_option.new_topic_duration_minutes)}} /> + </div> + + <div class="controls controls-dropdown"> + <label>{{i18n "user.auto_track_topics"}}</label> + <ComboBox @valueProperty="value" @content={{@autoTrackDurations}} @value={{@model.user_option.auto_track_topics_after_msecs}} @onChange={{action (mut @model.user_option.auto_track_topics_after_msecs)}} /> + </div> + + <div class="controls controls-dropdown"> + <label>{{i18n "user.notification_level_when_replying"}}</label> + <ComboBox @valueProperty="value" @content={{@notificationLevelsForReplying}} @value={{@model.user_option.notification_level_when_replying}} @onChange={{action (mut @model.user_option.notification_level_when_replying)}} /> + </div> +</div> diff --git a/app/assets/javascripts/discourse/app/components/user-preferences/user-api-keys.hbs b/app/assets/javascripts/discourse/app/components/user-preferences/user-api-keys.hbs new file mode 100644 index 00000000000..96833b93a32 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/user-preferences/user-api-keys.hbs @@ -0,0 +1,26 @@ +{{#if @model.userApiKeys}} + <div class="control-group apps"> + <label class="control-label">{{i18n "user.apps"}}</label> + <div class="controls"> + {{#each @model.userApiKeys as |key|}} + <div> + <span>{{key.application_name}}</span> + {{#if key.revoked}} + <DButton @action={{route-action "undoRevokeApiKey"}} @actionParam={{key}} @class="btn" @label="user.undo_revoke_access" /> + {{else}} + <DButton @action={{route-action "revokeApiKey"}} @actionParam={{key}} @class="btn" @label="user.revoke_access" /> + {{/if}} + <p> + <ul> + {{#each key.scopes as |scope|}} + <li>{{scope}}</li> + {{/each}} + </ul> + </p> + <p><span>{{i18n "user.api_approved"}}</span> {{bound-date key.created_at}}</p> + <p><span>{{i18n "user.api_last_used_at"}}</span> {{bound-date key.last_used_at}}</p> + </div> + {{/each}} + </div> + </div> +{{/if}} diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/tracking.js b/app/assets/javascripts/discourse/app/controllers/preferences/tracking.js new file mode 100644 index 00000000000..a2c76e7e3ba --- /dev/null +++ b/app/assets/javascripts/discourse/app/controllers/preferences/tracking.js @@ -0,0 +1,157 @@ +import Controller from "@ember/controller"; +import { NotificationLevels } from "discourse/lib/notification-levels"; +import I18n from "I18n"; +import { popupAjaxError } from "discourse/lib/ajax-error"; +import { action, computed } from "@ember/object"; +import { inject as service } from "@ember/service"; +import { tracked } from "@glimmer/tracking"; + +export default class extends Controller { + @service currentUser; + @service siteSettings; + @tracked saved = false; + + saveAttrNames = [ + "new_topic_duration_minutes", + "auto_track_topics_after_msecs", + "notification_level_when_replying", + "muted_category_ids", + "regular_category_ids", + "watched_category_ids", + "tracked_category_ids", + "watched_first_post_category_ids", + "muted_tags", + "tracked_tags", + "watched_tags", + "watching_first_post_tags", + ]; + + likeNotificationFrequencies = [ + { name: I18n.t("user.like_notification_frequency.always"), value: 0 }, + { + name: I18n.t("user.like_notification_frequency.first_time_and_daily"), + value: 1, + }, + { name: I18n.t("user.like_notification_frequency.first_time"), value: 2 }, + { name: I18n.t("user.like_notification_frequency.never"), value: 3 }, + ]; + + autoTrackDurations = [ + { name: I18n.t("user.auto_track_options.never"), value: -1 }, + { name: I18n.t("user.auto_track_options.immediately"), value: 0 }, + { + name: I18n.t("user.auto_track_options.after_30_seconds"), + value: 30000, + }, + { name: I18n.t("user.auto_track_options.after_1_minute"), value: 60000 }, + { + name: I18n.t("user.auto_track_options.after_2_minutes"), + value: 120000, + }, + { + name: I18n.t("user.auto_track_options.after_3_minutes"), + value: 180000, + }, + { + name: I18n.t("user.auto_track_options.after_4_minutes"), + value: 240000, + }, + { + name: I18n.t("user.auto_track_options.after_5_minutes"), + value: 300000, + }, + { + name: I18n.t("user.auto_track_options.after_10_minutes"), + value: 600000, + }, + ]; + + notificationLevelsForReplying = [ + { + name: I18n.t("topic.notifications.watching.title"), + value: NotificationLevels.WATCHING, + }, + { + name: I18n.t("topic.notifications.tracking.title"), + value: NotificationLevels.TRACKING, + }, + { + name: I18n.t("topic.notifications.regular.title"), + value: NotificationLevels.REGULAR, + }, + ]; + + considerNewTopicOptions = [ + { name: I18n.t("user.new_topic_duration.not_viewed"), value: -1 }, + { name: I18n.t("user.new_topic_duration.after_1_day"), value: 60 * 24 }, + { name: I18n.t("user.new_topic_duration.after_2_days"), value: 60 * 48 }, + { + name: I18n.t("user.new_topic_duration.after_1_week"), + value: 7 * 60 * 24, + }, + { + name: I18n.t("user.new_topic_duration.after_2_weeks"), + value: 2 * 7 * 60 * 24, + }, + { name: I18n.t("user.new_topic_duration.last_here"), value: -2 }, + ]; + + get canSee() { + return this.currentUser.id === this.model.id; + } + + @computed( + "model.watched_tags.[]", + "model.watching_first_post_tags.[]", + "model.tracked_tags.[]", + "model.muted_tags.[]" + ) + get selectedTags() { + return [] + .concat( + this.model.watched_tags, + this.model.watching_first_post_tags, + this.model.tracked_tags, + this.model.muted_tags + ) + .filter((t) => t); + } + + @computed( + "model.watchedCategories", + "model.watchedFirstPostCategories", + "model.trackedCategories", + "model.mutedCategories" + ) + get selectedCategories() { + return [] + .concat( + this.modelwatchedCategories, + this.model.watchedFirstPostCategories, + this.model.tracakedCategories, + this.model.mutedCategories + ) + .filter((t) => t); + } + + @computed("siteSettings.remove_muted_tags_from_latest") + get hideMutedTags() { + return this.siteSettings.remove_muted_tags_from_latest !== "never"; + } + + get canSave() { + return this.canSee || this.currentUser.admin; + } + + @action + save() { + this.saved = false; + + return this.model + .save(this.saveAttrNames) + .then(() => { + this.saved = true; + }) + .catch(popupAjaxError); + } +} diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/users.js b/app/assets/javascripts/discourse/app/controllers/preferences/users.js index f47f7efec3b..81eb728d3f3 100644 --- a/app/assets/javascripts/discourse/app/controllers/preferences/users.js +++ b/app/assets/javascripts/discourse/app/controllers/preferences/users.js @@ -51,6 +51,7 @@ export default Controller.extend({ this._super(...arguments); this.saveAttrNames = [ + "allow_private_messages", "muted_usernames", "allowed_pm_usernames", "enable_allowed_pm_users", @@ -72,11 +73,6 @@ export default Controller.extend({ return !allowPrivateMessages; }, - @discourseComputed("currentUser.can_send_private_messages") - showMessageSettings() { - return this.currentUser?.can_send_private_messages; - }, - @action save() { this.set("saved", false); diff --git a/app/assets/javascripts/discourse/app/routes/app-route-map.js b/app/assets/javascripts/discourse/app/routes/app-route-map.js index fd06c0b178b..452125109be 100644 --- a/app/assets/javascripts/discourse/app/routes/app-route-map.js +++ b/app/assets/javascripts/discourse/app/routes/app-route-map.js @@ -161,6 +161,7 @@ export default function () { this.route("profile"); this.route("emails"); this.route("notifications"); + this.route("tracking"); this.route("categories"); this.route("users"); this.route("tags"); diff --git a/app/assets/javascripts/discourse/app/routes/preferences-tracking.js b/app/assets/javascripts/discourse/app/routes/preferences-tracking.js new file mode 100644 index 00000000000..bfec0d17c4c --- /dev/null +++ b/app/assets/javascripts/discourse/app/routes/preferences-tracking.js @@ -0,0 +1,5 @@ +import RestrictedUserRoute from "discourse/routes/restricted-user"; + +export default RestrictedUserRoute.extend({ + showFooter: true, +}); diff --git a/app/assets/javascripts/discourse/app/templates/preferences.hbs b/app/assets/javascripts/discourse/app/templates/preferences.hbs index b2aa84d5020..21758e3d4ff 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences.hbs @@ -3,8 +3,7 @@ <div class="user-navigation user-navigation-secondary"> <HorizontalOverflowNav> <UserNav::PreferencesNav - @user={{this.model}} - @viewingSelf={{this.viewingSelf}} + @currentUser={{this.currentUser}} @model={{this.model}} @siteSettings={{this.siteSettings}}/> </HorizontalOverflowNav> @@ -79,6 +78,7 @@ </LinkTo> </li> {{/if}} + <PluginOutlet @name="user-preferences-nav" @tagName="span" @connectorTagName="li" @args={{hash model=this.model}} /> </MobileNav> </DSection> diff --git a/app/assets/javascripts/discourse/app/templates/preferences/apps.hbs b/app/assets/javascripts/discourse/app/templates/preferences/apps.hbs index 56795452dc7..cd41f237626 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences/apps.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences/apps.hbs @@ -1,28 +1,3 @@ -{{#if this.model.userApiKeys}} - <div class="control-group apps"> - <label class="control-label">{{i18n "user.apps"}}</label> - <div class="controls"> - {{#each this.model.userApiKeys as |key|}} - <div> - <span>{{key.application_name}}</span> - {{#if key.revoked}} - <DButton @action={{route-action "undoRevokeApiKey"}} @actionParam={{key}} @class="btn" @label="user.undo_revoke_access" /> - {{else}} - <DButton @action={{route-action "revokeApiKey"}} @actionParam={{key}} @class="btn" @label="user.revoke_access" /> - {{/if}} - <p> - <ul> - {{#each key.scopes as |scope|}} - <li>{{scope}}</li> - {{/each}} - </ul> - </p> - <p><span>{{i18n "user.api_approved"}}</span> {{bound-date key.created_at}}</p> - <p><span>{{i18n "user.api_last_used_at"}}</span> {{bound-date key.last_used_at}}</p> - </div> - {{/each}} - </div> - </div> -{{/if}} +<UserPreferences::UserApiKeys @model={{@model}}/> <PluginOutlet @name="user-preferences-apps" @tagName="span" @connectorTagName="div" @args={{hash model=this.model}} /> diff --git a/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs b/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs index 8a0b2b716b3..813e34faf37 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences/categories.hbs @@ -1,53 +1,10 @@ -<div class="control-group category-notifications"> - <label class="control-label">{{i18n "user.categories_settings"}}</label> - - <div class="controls tracking-controls tracking-controls--watched-categories"> - <label>{{d-icon "d-watching"}} {{i18n "user.watched_categories"}}</label> - {{#if this.canSee}} - <a class="show-tracking" href={{this.model.watchingTopicsPath}}>{{i18n "user.tracked_topics_link"}}</a> - {{/if}} - <CategorySelector @categories={{this.model.watchedCategories}} @blockedCategories={{this.selectedCategories}} @onChange={{action (mut this.model.watchedCategories)}} /> - </div> - <div class="instructions">{{i18n "user.watched_categories_instructions"}}</div> - - <div class="controls tracking-controls tracking-controls--tracked-categories"> - <label>{{d-icon "d-tracking"}} {{i18n "user.tracked_categories"}}</label> - {{#if this.canSee}} - <a class="show-tracking" href={{this.model.trackingTopicsPath}}>{{i18n "user.tracked_topics_link"}}</a> - {{/if}} - <CategorySelector @categories={{this.model.trackedCategories}} @blockedCategories={{this.selectedCategories}} @onChange={{action (mut this.model.trackedCategories)}} /> - </div> - <div class="instructions">{{i18n "user.tracked_categories_instructions"}}</div> - - <div class="controls tracking-controls tracking-controls--watched-first-categories"> - <label>{{d-icon "d-watching-first"}} {{i18n "user.watched_first_post_categories"}}</label> - <CategorySelector @categories={{this.model.watchedFirstPostCategories}} @blockedCategories={{this.selectedCategories}} @onChange={{action (mut this.model.watchedFirstPostCategories)}} /> - </div> - <div class="instructions">{{i18n "user.watched_first_post_categories_instructions"}}</div> - - {{#if this.siteSettings.mute_all_categories_by_default}} - <div class="controls tracking-controls tracking-controls--regular-categories"> - <label>{{d-icon "d-regular"}} {{i18n "user.regular_categories"}}</label> - <CategorySelector @categories={{this.model.regularCategories}} @blockedCategories={{this.selectedCategories}} @onChange={{action (mut this.model.regularCategories)}} /> - </div> - <div class="instructions">{{i18n "user.regular_categories_instructions"}}</div> - {{else}} - <div class="controls tracking-controls tracking-controls--muted-categories"> - <label>{{d-icon "d-muted"}} {{i18n "user.muted_categories"}}</label> - {{#if this.canSee}} - <a class="show-tracking" href={{this.model.mutedTopicsPath}}>{{i18n "user.tracked_topics_link"}}</a> - {{/if}} - <CategorySelector @categories={{this.model.mutedCategories}} @blockedCategories={{this.selectedCategories}} @onChange={{action (mut this.model.mutedCategories)}} /> - </div> - <div class="instructions">{{i18n (if this.hideMutedTags "user.muted_categories_instructions" "user.muted_categories_instructions_dont_hide")}}</div> - {{/if}} -</div> - -<PluginOutlet @name="user-preferences-categories" @tagName="span" @connectorTagName="div" @args={{hash model=this.model save=(action "save")}} /> - -<br> - -<PluginOutlet @name="user-custom-controls" @tagName="span" @connectorTagName="div" @args={{hash model=this.model}} /> +<UserPreferences::Categories + @canSee={{this.canSee}} + @model={{this.model}} + @selectedCategories={{this.selectedCategories}} + @hideMutedTags={{this.hideMutedTags}} + @save={{action "save"}} + @siteSettings={{this.siteSettings}}/> {{#if this.canSave}} <SaveControls @model={{this.model}} @action={{action "save"}} @saved={{this.saved}} /> diff --git a/app/assets/javascripts/discourse/app/templates/preferences/notifications.hbs b/app/assets/javascripts/discourse/app/templates/preferences/notifications.hbs index 6f142e25a1c..62135b8560a 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences/notifications.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences/notifications.hbs @@ -1,20 +1,13 @@ <div class="control-group notifications"> <label class="control-label">{{i18n "user.notifications"}}</label> - <div class="controls controls-dropdown"> - <label>{{i18n "user.new_topic_duration.label"}}</label> - <ComboBox @class="duration" @valueProperty="value" @content={{this.considerNewTopicOptions}} @value={{this.model.user_option.new_topic_duration_minutes}} @onChange={{action (mut this.model.user_option.new_topic_duration_minutes)}} /> - </div> - - <div class="controls controls-dropdown"> - <label>{{i18n "user.auto_track_topics"}}</label> - <ComboBox @valueProperty="value" @content={{this.autoTrackDurations}} @value={{this.model.user_option.auto_track_topics_after_msecs}} @onChange={{action (mut this.model.user_option.auto_track_topics_after_msecs)}} /> - </div> - - <div class="controls controls-dropdown"> - <label>{{i18n "user.notification_level_when_replying"}}</label> - <ComboBox @valueProperty="value" @content={{this.notificationLevelsForReplying}} @value={{this.model.user_option.notification_level_when_replying}} @onChange={{action (mut this.model.user_option.notification_level_when_replying)}} /> - </div> + {{#unless this.currentUser.redesigned_user_page_nav_enabled}} + <UserPreferences::TopicTracking + @considerNewTopicOptions={{this.considerNewTopicOptions}} + @model={{this.model}} + @autoTrackDurations={{this.autoTrackDurations}} + @notificationLevelsForReplying={{this.notificationLevelsForReplying}} /> + {{/unless}} <div class="controls controls-dropdown"> <label>{{i18n "user.like_notification_frequency.title"}}</label> @@ -34,13 +27,9 @@ <UserNotificationSchedule @model={{this.model}} /> {{#if this.showMessageSettings}} - <div class="control-group private-messages"> - <label class="control-label">{{i18n "user.private_messages"}}</label> - - <div class="controls"> - <PreferenceCheckbox @labelKey="user.allow_private_messages" @checked={{this.model.user_option.allow_private_messages}} /> - </div> - </div> + {{#unless this.currentUser.redesigned_user_page_nav_enabled}} + <UserPreferences::AllowPrivateMessages @model={{this.model}} /> + {{/unless}} {{/if}} <PluginOutlet @name="user-preferences-notifications" @tagName="span" @connectorTagName="div" @args={{hash model=this.model save=(action "save")}} /> diff --git a/app/assets/javascripts/discourse/app/templates/preferences/security.hbs b/app/assets/javascripts/discourse/app/templates/preferences/security.hbs index 78459f73e73..54a517cf807 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences/security.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences/security.hbs @@ -71,6 +71,10 @@ </div> {{/if}} +{{#if this.currentUser.redesigned_user_page_nav_enabled}} + <UserPreferences::UserApiKeys @model={{@model}}/> +{{/if}} + <PluginOutlet @name="user-preferences-security" @tagName="span" @connectorTagName="div" @args={{hash model=this.model save=(action "save")}} /> <br> diff --git a/app/assets/javascripts/discourse/app/templates/preferences/tags.hbs b/app/assets/javascripts/discourse/app/templates/preferences/tags.hbs index 6ef089fc766..71b18e61ddd 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences/tags.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences/tags.hbs @@ -1,50 +1,7 @@ -{{#if this.siteSettings.tagging_enabled}} - <div class="control-group tag-notifications"> - <label class="control-label">{{i18n "user.tag_settings"}}</label> +<UserPreferences::Tags + @model={{this.model}} + @selectedTags={{this.selectedTags}} + @save={{action "save"}} + @siteSettings={{this.siteSettings}}/> - <div class="controls tracking-controls"> - <label>{{d-icon "d-watching" class="icon watching"}} {{i18n "user.watched_tags"}}</label> - <TagChooser @tags={{this.model.watched_tags}} @blockedTags={{this.selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash - allowAny=false - }} /> - </div> - - <div class="instructions">{{i18n "user.watched_tags_instructions"}}</div> - - <div class="controls tracking-controls"> - <label>{{d-icon "d-tracking" class="icon tracking"}} {{i18n "user.tracked_tags"}}</label> - <TagChooser @tags={{this.model.tracked_tags}} @blockedTags={{this.selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash - allowAny=false - }} /> - </div> - - <div class="instructions">{{i18n "user.tracked_tags_instructions"}}</div> - - <div class="controls tracking-controls"> - <label>{{d-icon "d-watching-first" class="icon watching-first-post"}} {{i18n "user.watched_first_post_tags"}}</label> - <TagChooser @tags={{this.model.watching_first_post_tags}} @blockedTags={{this.selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash - allowAny=false - }} /> - </div> - - <div class="instructions"> - {{i18n "user.watched_first_post_tags_instructions"}} - </div> - - <div class="controls tracking-controls"> - <label>{{d-icon "d-muted" class="icon muted"}} {{i18n "user.muted_tags"}}</label> - <TagChooser @tags={{this.model.muted_tags}} @blockedTags={{this.selectedTags}} @everyTag={{true}} @unlimitedTagCount={{true}} @options={{hash - allowAny=false - }} /> - </div> - <div class="instructions">{{i18n "user.muted_tags_instructions"}}</div> - </div> - - <PluginOutlet @name="user-preferences-tags" @tagName="span" @connectorTagName="div" @args={{hash model=this.model save=(action "save")}} /> - - <br> - - <PluginOutlet @name="user-custom-controls" @tagName="span" @connectorTagName="div" @args={{hash model=this.model}} /> - - <SaveControls @model={{this.model}} @action={{action "save"}} @saved={{this.saved}} /> -{{/if}} +<SaveControls @model={{this.model}} @action={{action "save"}} @saved={{this.saved}} /> diff --git a/app/assets/javascripts/discourse/app/templates/preferences/tracking.hbs b/app/assets/javascripts/discourse/app/templates/preferences/tracking.hbs new file mode 100644 index 00000000000..c12f05eeb91 --- /dev/null +++ b/app/assets/javascripts/discourse/app/templates/preferences/tracking.hbs @@ -0,0 +1,29 @@ +<DSection @pageClass="user-preferences-tracking" /> + +<div class="user-preferences_tracking-topics-wrapper"> + <label class="control-label">{{i18n "user.topics_settings"}}</label> + <UserPreferences::TopicTracking + @considerNewTopicOptions={{this.considerNewTopicOptions}} + @model={{this.model}} + @autoTrackDurations={{this.autoTrackDurations}} + @notificationLevelsForReplying={{this.notificationLevelsForReplying}} /> +</div> + +<div class="user-preferences_tracking-categories-tags-wrapper"> + <UserPreferences::Categories + @canSee={{this.canSee}} + @model={{this.model}} + @selectedCategories={{this.selectedCategories}} + @hideMutedTags={{this.hideMutedTags}} + @siteSettings={{this.siteSettings}} /> + + <UserPreferences::Tags + @model={{this.model}} + @selectedTags={{this.selectedTags}} + @save={{this.save}} + @siteSettings={{this.siteSettings}} /> +</div> + +{{#if this.canSave}} + <SaveControls @model={{this.model}} @action={{this.save}} @saved={{this.saved}} /> +{{/if}} diff --git a/app/assets/javascripts/discourse/app/templates/preferences/users.hbs b/app/assets/javascripts/discourse/app/templates/preferences/users.hbs index 5589f14d47e..69c15a274ec 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences/users.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences/users.hbs @@ -21,7 +21,11 @@ <div class="instructions">{{i18n "user.muted_users_instructions"}}</div> </div> -{{#if this.showMessageSettings}} +{{#if this.currentUser.can_send_private_messages}} + {{#if this.currentUser.redesigned_user_page_nav_enabled}} + <UserPreferences::AllowPrivateMessages @model={{this.model}} /> + {{/if}} + <div class="control-group user-allow-pm"> <div class="controls"> <PreferenceCheckbox @labelKey="user.allow_private_messages_from_specific_users" @checked={{this.model.user_option.enable_allowed_pm_users}} @disabled={{this.disableAllowPmUsersSetting}} /> diff --git a/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js b/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js index 38891cc8e94..f0499b3984d 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/preferences-test.js @@ -91,6 +91,7 @@ acceptance("User Preferences", function (needs) { "/u/eviltrout/preferences/account", "defaults to account tab" ); + assert.ok(exists(".user-preferences"), "it shows the preferences"); const savePreferences = async () => { @@ -112,25 +113,32 @@ acceptance("User Preferences", function (needs) { await savePreferences(); await click(".preferences-nav .nav-notifications a"); + await selectKit( ".control-group.notifications .combo-box.duration" ).expand(); + await selectKit( ".control-group.notifications .combo-box.duration" ).selectRowByValue(1440); + await savePreferences(); await click(".preferences-nav .nav-categories a"); + const categorySelector = selectKit( ".tracking-controls .category-selector " ); + await categorySelector.expand(); await categorySelector.fillInFilter("faq"); await savePreferences(); this.siteSettings.tagging_enabled = false; + await visit("/"); await visit("/u/eviltrout/preferences"); + assert.ok( !exists(".preferences-nav .nav-tags a"), "tags tab isn't there when tags are disabled" @@ -564,7 +572,7 @@ acceptance("Ignored users", function (needs) { }); }); -acceptance("Security", function (needs) { +acceptance("User Preferences - Security", function (needs) { needs.user(); needs.pretender(preferencesPretender); diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-preferences-new-menu-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-new-menu-test.js new file mode 100644 index 00000000000..64fc8f8f836 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/acceptance/user-preferences-new-menu-test.js @@ -0,0 +1,60 @@ +import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers"; +import { click, visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import selectKit from "discourse/tests/helpers/select-kit-helper"; + +acceptance( + "New User Menu - Horizontal nav and preferences relocation", + function (needs) { + needs.user({ + redesigned_user_page_nav_enabled: true, + user_api_keys: [ + { + id: 1, + application_name: "Discourse Hub", + scopes: ["Read and clear notifications"], + created_at: "2020-11-14T00:57:09.093Z", + last_used_at: "2022-09-22T18:55:41.672Z", + }, + ], + }); + + test("Horizontal user nav exists", async function (assert) { + await visit("/u/eviltrout/preferences"); + assert.ok(exists(".horizontal-overflow-nav"), "horizontal nav exists"); + }); + + test("User Tracking page exists", async function (assert) { + await visit("/u/eviltrout/preferences"); + assert.ok(exists(".nav-tracking"), "the new tracking page link exists"); + }); + + test("User Categories page no longer exists", async function (assert) { + await visit("/u/eviltrout/preferences"); + assert.ok(!exists(".nav-categories"), "Categories tab no longer exists"); + }); + + test("Can save category tracking preferences", async function (assert) { + await visit("/u/eviltrout/preferences/tracking"); + + const categorySelector = selectKit( + ".tracking-controls .category-selector" + ); + + const savePreferences = async () => { + assert.notOk(exists(".saved"), "it hasn't been saved yet"); + await click(".save-changes"); + assert.ok(exists(".saved"), "it displays the saved message"); + }; + + await categorySelector.expand(); + await categorySelector.fillInFilter("faq"); + await savePreferences(); + }); + + test("Can view user api keys on security page", async function (assert) { + await visit("/u/eviltrout/preferences/security"); + assert.ok(exists(".control-group.apps"), "User can see apps section"); + }); + } +); diff --git a/app/assets/stylesheets/common/base/new-user.scss b/app/assets/stylesheets/common/base/new-user.scss index 3b6374ab199..ff1fb59dd45 100644 --- a/app/assets/stylesheets/common/base/new-user.scss +++ b/app/assets/stylesheets/common/base/new-user.scss @@ -184,3 +184,26 @@ margin: 1em 0; } } + +.user-preferences-tracking-page { + .user-preferences .form-vertical { + width: 100%; + } +} + +.user-preferences_tracking-topics-wrapper { + margin-bottom: 3em; + .control-label { + margin-bottom: 1em; + } +} + +.user-preferences_tracking-categories-tags-wrapper { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(16em, 1fr)); + gap: 2em; + .control-group { + width: 100%; + min-width: 16em; + } +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ebd5a1519d3..df210f42a95 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1326,6 +1326,7 @@ en: profile: "Profile" emails: "Emails" notifications: "Notifications" + tracking: "Tracking" categories: "Categories" users: "Users" tags: "Tags" @@ -1604,6 +1605,7 @@ en: other_settings: "Other" categories_settings: "Categories" + topics_settings: "Topics" new_topic_duration: label: "Consider topics new when" diff --git a/config/routes.rb b/config/routes.rb index 22e73895078..3b36ba31d88 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -494,6 +494,7 @@ Discourse::Application.routes.draw do put "#{root_path}/:username/preferences/primary-email" => "users#update_primary_email", format: :json, constraints: { username: RouteFormat.username } delete "#{root_path}/:username/preferences/email" => "users#destroy_email", constraints: { username: RouteFormat.username } get "#{root_path}/:username/preferences/notifications" => "users#preferences", constraints: { username: RouteFormat.username } + get "#{root_path}/:username/preferences/tracking" => "users#preferences", constraints: { username: RouteFormat.username } get "#{root_path}/:username/preferences/categories" => "users#preferences", constraints: { username: RouteFormat.username } get "#{root_path}/:username/preferences/users" => "users#preferences", constraints: { username: RouteFormat.username } get "#{root_path}/:username/preferences/tags" => "users#preferences", constraints: { username: RouteFormat.username }