DEV: Convert badge & preferences controllers to native class syntax (#28608)

Changes made using the ember-native-class-codemod, plus some manual tweaks
This commit is contained in:
David Taylor 2024-08-28 17:18:22 +01:00 committed by GitHub
parent 7210f36801
commit efa08658be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 834 additions and 869 deletions

View File

@ -8,7 +8,7 @@ function badgeKey(badge) {
return ("000" + pos).slice(-4) + (10 - type) + name;
}
export default Controller.extend({
export default class IndexController extends Controller {
@discourseComputed("model")
badgeGroups(model) {
let sorted = model.sort((a, b) => badgeKey(a).localeCompare(badgeKey(b)));
@ -35,5 +35,5 @@ export default Controller.extend({
}
return grouped;
},
});
}
}

View File

@ -5,17 +5,18 @@ import UserBadge from "discourse/models/user-badge";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
export default Controller.extend({
application: controller(),
queryParams: ["username"],
noMoreBadges: false,
userBadges: null,
hiddenSetTitle: true,
export default class ShowController extends Controller {
@controller application;
queryParams = ["username"];
noMoreBadges = false;
userBadges = null;
hiddenSetTitle = true;
@discourseComputed("userBadgesAll")
filteredList(userBadgesAll) {
return userBadgesAll.filterBy("badge.allow_title", true);
},
}
@discourseComputed("filteredList")
selectableUserBadges(filteredList) {
@ -26,29 +27,29 @@ export default Controller.extend({
}),
...filteredList.uniqBy("badge.name"),
];
},
}
@discourseComputed("username")
user(username) {
if (username) {
return this.userBadges[0].get("user");
}
},
}
@discourseComputed("username", "model.grant_count", "userBadges.grant_count")
grantCount(username, modelCount, userCount) {
return username ? userCount : modelCount;
},
}
@discourseComputed("model.grant_count", "userBadges.grant_count")
othersCount(modelCount, userCount) {
return modelCount - userCount;
},
}
@discourseComputed("model.allow_title", "model.has_badge", "model")
canSelectTitle(hasTitleBadges, hasBadge) {
return this.siteSettings.enable_badges && hasTitleBadges && hasBadge;
},
}
@discourseComputed("noMoreBadges", "grantCount", "userBadges.length")
canLoadMore(noMoreBadges, grantCount, userBadgeLength) {
@ -56,12 +57,12 @@ export default Controller.extend({
return false;
}
return grantCount > (userBadgeLength || 0);
},
}
@discourseComputed("user", "model.grant_count")
canShowOthers(user, grantCount) {
return !!user && grantCount > 1;
},
}
@action
loadMore() {
@ -89,10 +90,10 @@ export default Controller.extend({
.finally(() => {
this.set("loadingMore", false);
});
},
}
@action
toggleSetUserTitle() {
return this.toggleProperty("hiddenSetTitle");
},
});
}
}

View File

@ -14,14 +14,33 @@ import getURL from "discourse-common/lib/get-url";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
export default Controller.extend(CanCheckEmails, {
dialog: service(),
modal: service(),
user: controller(),
canDownloadPosts: alias("user.viewingSelf"),
export default class AccountController extends Controller.extend(
CanCheckEmails
) {
@service dialog;
@service modal;
@controller user;
@setting("enable_names") canEditName;
@setting("enable_user_status") canSelectUserStatus;
@alias("user.viewingSelf") canDownloadPosts;
@not("currentUser.can_delete_account") cannotDeleteAccount;
@or("model.isSaving", "deleting", "cannotDeleteAccount") deleteDisabled;
@gt("model.availableTitles.length", 0) canSelectTitle;
@gt("model.availableFlairs.length", 0) canSelectFlair;
@propertyNotEqual("model.id", "currentUser.id") disableConnectButtons;
canSaveUser = true;
newNameInput = null;
newTitleInput = null;
newPrimaryGroupInput = null;
newStatus = null;
revoking = null;
init() {
this._super(...arguments);
super.init(...arguments);
this.saveAttrNames = [
"name",
@ -31,25 +50,11 @@ export default Controller.extend(CanCheckEmails, {
"status",
];
this.set("revoking", {});
},
canEditName: setting("enable_names"),
canSelectUserStatus: setting("enable_user_status"),
canSaveUser: true,
newNameInput: null,
newTitleInput: null,
newPrimaryGroupInput: null,
newStatus: null,
revoking: null,
cannotDeleteAccount: not("currentUser.can_delete_account"),
deleteDisabled: or("model.isSaving", "deleting", "cannotDeleteAccount"),
}
reset() {
this.set("passwordProgress", null);
},
}
@discourseComputed()
nameInstructions() {
@ -58,10 +63,7 @@ export default Controller.extend(CanCheckEmails, {
? "user.name.instructions_required"
: "user.name.instructions"
);
},
canSelectTitle: gt("model.availableTitles.length", 0),
canSelectFlair: gt("model.availableFlairs.length", 0),
}
@discourseComputed("model.filteredGroups")
canSelectPrimaryGroup(primaryGroupOptions) {
@ -69,12 +71,12 @@ export default Controller.extend(CanCheckEmails, {
primaryGroupOptions.length > 0 &&
this.siteSettings.user_selected_primary_groups
);
},
}
@discourseComputed("model.associated_accounts")
associatedAccountsLoaded(associatedAccounts) {
return typeof associatedAccounts !== "undefined";
},
}
@discourseComputed("model.associated_accounts.[]")
authProviders(accounts) {
@ -88,9 +90,7 @@ export default Controller.extend(CanCheckEmails, {
});
return result.filter((value) => value.account || value.method.can_connect);
},
disableConnectButtons: propertyNotEqual("model.id", "currentUser.id"),
}
@discourseComputed(
"model.email",
@ -123,7 +123,7 @@ export default Controller.extend(CanCheckEmails, {
}
return emails.sort((a, b) => a.email.localeCompare(b.email));
},
}
@discourseComputed(
"model.second_factor_enabled",
@ -139,7 +139,7 @@ export default Controller.extend(CanCheckEmails, {
return false;
}
return findAll().length > 0;
},
}
@discourseComputed(
"siteSettings.max_allowed_secondary_emails",
@ -147,7 +147,7 @@ export default Controller.extend(CanCheckEmails, {
)
canAddEmail(maxAllowedSecondaryEmails, canEditEmail) {
return maxAllowedSecondaryEmails > 0 && canEditEmail;
},
}
@action
resendConfirmationEmail(email, event) {
@ -161,7 +161,7 @@ export default Controller.extend(CanCheckEmails, {
.finally(() => {
email.set("resending", false);
});
},
}
@action
showUserStatusModal(status) {
@ -173,98 +173,100 @@ export default Controller.extend(CanCheckEmails, {
deleteAction: () => this.set("newStatus", null),
},
});
},
}
actions: {
save() {
this.set("saved", false);
@action
save() {
this.set("saved", false);
this.model.setProperties({
name: this.newNameInput,
title: this.newTitleInput,
primary_group_id: this.newPrimaryGroupInput,
flair_group_id: this.newFlairGroupId,
status: this.newStatus,
});
this.model.setProperties({
name: this.newNameInput,
title: this.newTitleInput,
primary_group_id: this.newPrimaryGroupInput,
flair_group_id: this.newFlairGroupId,
status: this.newStatus,
});
return this.model
.save(this.saveAttrNames)
.then(() => this.set("saved", true))
.catch(popupAjaxError);
},
return this.model
.save(this.saveAttrNames)
.then(() => this.set("saved", true))
.catch(popupAjaxError);
}
setPrimaryEmail(email) {
this.model.setPrimaryEmail(email).catch(popupAjaxError);
},
@action
setPrimaryEmail(email) {
this.model.setPrimaryEmail(email).catch(popupAjaxError);
}
destroyEmail(email) {
this.model.destroyEmail(email);
},
@action
destroyEmail(email) {
this.model.destroyEmail(email);
}
delete() {
this.dialog.alert({
message: I18n.t("user.delete_account_confirm"),
buttons: [
{
icon: "exclamation-triangle",
label: I18n.t("user.delete_account"),
class: "btn-danger",
action: () => {
return this.model.delete().then(
() => {
next(() => {
this.dialog.alert({
message: I18n.t("user.deleted_yourself"),
didConfirm: () =>
DiscourseURL.redirectAbsolute(getURL("/")),
didCancel: () =>
DiscourseURL.redirectAbsolute(getURL("/")),
});
@action
delete() {
this.dialog.alert({
message: I18n.t("user.delete_account_confirm"),
buttons: [
{
icon: "exclamation-triangle",
label: I18n.t("user.delete_account"),
class: "btn-danger",
action: () => {
return this.model.delete().then(
() => {
next(() => {
this.dialog.alert({
message: I18n.t("user.deleted_yourself"),
didConfirm: () =>
DiscourseURL.redirectAbsolute(getURL("/")),
didCancel: () => DiscourseURL.redirectAbsolute(getURL("/")),
});
},
() => {
next(() =>
this.dialog.alert(
I18n.t("user.delete_yourself_not_allowed")
)
);
this.set("deleting", false);
}
);
},
});
},
() => {
next(() =>
this.dialog.alert(I18n.t("user.delete_yourself_not_allowed"))
);
this.set("deleting", false);
}
);
},
{
label: I18n.t("composer.cancel"),
},
],
});
},
},
{
label: I18n.t("composer.cancel"),
},
],
});
}
revokeAccount(account) {
this.set(`revoking.${account.name}`, true);
@action
revokeAccount(account) {
this.set(`revoking.${account.name}`, true);
this.model
.revokeAssociatedAccount(account.name)
.then((result) => {
if (result.success) {
this.model.associated_accounts.removeObject(account);
} else {
this.dialog.alert(result.message);
}
})
.catch(popupAjaxError)
.finally(() => this.set(`revoking.${account.name}`, false));
},
this.model
.revokeAssociatedAccount(account.name)
.then((result) => {
if (result.success) {
this.model.associated_accounts.removeObject(account);
} else {
this.dialog.alert(result.message);
}
})
.catch(popupAjaxError)
.finally(() => this.set(`revoking.${account.name}`, false));
}
connectAccount(method) {
method.doLogin({ reconnect: true });
},
@action
connectAccount(method) {
method.doLogin({ reconnect: true });
}
exportUserArchive() {
this.dialog.yesNoConfirm({
message: I18n.t("user.download_archive.confirm"),
didConfirm: () => exportUserArchive(),
});
},
},
});
@action
exportUserArchive() {
this.dialog.yesNoConfirm({
message: I18n.t("user.download_archive.confirm"),
didConfirm: () => exportUserArchive(),
});
}
}

View File

@ -1,38 +1,32 @@
import Controller from "@ember/controller";
import EmberObject from "@ember/object";
import EmberObject, { action } from "@ember/object";
import { empty, or } from "@ember/object/computed";
import { propertyEqual } from "discourse/lib/computed";
import { emailValid } from "discourse/lib/utilities";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
export default Controller.extend({
queryParams: ["new"],
export default class EmailController extends Controller {
queryParams = ["new"];
taken = false;
saving = false;
error = false;
success = false;
oldEmail = null;
newEmail = null;
successMessage = null;
taken: false,
saving: false,
error: false,
success: false,
oldEmail: null,
newEmail: null,
successMessage: null,
@empty("newEmail") newEmailEmpty;
newEmailEmpty: empty("newEmail"),
@or("saving", "newEmailEmpty", "taken", "unchanged", "invalidEmail")
saveDisabled;
saveDisabled: or(
"saving",
"newEmailEmpty",
"taken",
"unchanged",
"invalidEmail"
),
unchanged: propertyEqual("newEmailLower", "oldEmail"),
@propertyEqual("newEmailLower", "oldEmail") unchanged;
@discourseComputed("newEmail")
newEmailLower(newEmail) {
return newEmail.toLowerCase().trim();
},
}
@discourseComputed("saving", "new")
saveButtonText(saving, isNew) {
@ -43,12 +37,12 @@ export default Controller.extend({
return I18n.t("user.add_email.add");
}
return I18n.t("user.change");
},
}
@discourseComputed("newEmail")
invalidEmail(newEmail) {
return !emailValid(newEmail);
},
}
@discourseComputed("invalidEmail", "oldEmail", "newEmail")
emailValidation(invalidEmail, oldEmail, newEmail) {
@ -58,7 +52,7 @@ export default Controller.extend({
reason: I18n.t("user.email.invalid"),
});
}
},
}
reset() {
this.setProperties({
@ -68,49 +62,45 @@ export default Controller.extend({
success: false,
newEmail: null,
});
},
}
actions: {
saveEmail() {
this.set("saving", true);
@action
saveEmail() {
this.set("saving", true);
return (
this.new
? this.model.addEmail(this.newEmail)
: this.model.changeEmail(this.newEmail)
).then(
() => {
this.set("success", true);
return (
this.new
? this.model.addEmail(this.newEmail)
: this.model.changeEmail(this.newEmail)
).then(
() => {
this.set("success", true);
if (this.model.staff) {
if (this.model.staff) {
this.set("successMessage", I18n.t("user.change_email.success_staff"));
} else {
if (this.currentUser.admin) {
this.set(
"successMessage",
I18n.t("user.change_email.success_staff")
I18n.t("user.change_email.success_via_admin")
);
} else {
if (this.currentUser.admin) {
this.set(
"successMessage",
I18n.t("user.change_email.success_via_admin")
);
} else {
this.set("successMessage", I18n.t("user.change_email.success"));
}
}
},
(e) => {
this.setProperties({ error: true, saving: false });
if (
e.jqXHR.responseJSON &&
e.jqXHR.responseJSON.errors &&
e.jqXHR.responseJSON.errors[0]
) {
this.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
} else {
this.set("errorMessage", I18n.t("user.change_email.error"));
this.set("successMessage", I18n.t("user.change_email.success"));
}
}
);
},
},
});
},
(e) => {
this.setProperties({ error: true, saving: false });
if (
e.jqXHR.responseJSON &&
e.jqXHR.responseJSON.errors &&
e.jqXHR.responseJSON.errors[0]
) {
this.set("errorMessage", e.jqXHR.responseJSON.errors[0]);
} else {
this.set("errorMessage", I18n.t("user.change_email.error"));
}
}
);
}
}

View File

@ -1,4 +1,5 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { equal } from "@ember/object/computed";
import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators";
@ -10,56 +11,50 @@ const EMAIL_LEVELS = {
NEVER: 2,
};
export default Controller.extend({
subpageTitle: I18n.t("user.preferences_nav.emails"),
emailMessagesLevelAway: equal(
"model.user_option.email_messages_level",
EMAIL_LEVELS.ONLY_WHEN_AWAY
),
emailLevelAway: equal(
"model.user_option.email_level",
EMAIL_LEVELS.ONLY_WHEN_AWAY
),
export default class EmailsController extends Controller {
subpageTitle = I18n.t("user.preferences_nav.emails");
init() {
this._super(...arguments);
@equal("model.user_option.email_messages_level", EMAIL_LEVELS.ONLY_WHEN_AWAY)
emailMessagesLevelAway;
this.saveAttrNames = [
"email_level",
"email_messages_level",
"mailing_list_mode",
"mailing_list_mode_frequency",
"email_digests",
"email_in_reply_to",
"email_previous_replies",
"digest_after_minutes",
"include_tl0_in_digests",
];
@equal("model.user_option.email_level", EMAIL_LEVELS.ONLY_WHEN_AWAY)
emailLevelAway;
this.previousRepliesOptions = [
{ name: I18n.t("user.email_previous_replies.always"), value: 0 },
{ name: I18n.t("user.email_previous_replies.unless_emailed"), value: 1 },
{ name: I18n.t("user.email_previous_replies.never"), value: 2 },
];
saveAttrNames = [
"email_level",
"email_messages_level",
"mailing_list_mode",
"mailing_list_mode_frequency",
"email_digests",
"email_in_reply_to",
"email_previous_replies",
"digest_after_minutes",
"include_tl0_in_digests",
];
this.emailLevelOptions = [
{ name: I18n.t("user.email_level.always"), value: EMAIL_LEVELS.ALWAYS },
{
name: I18n.t("user.email_level.only_when_away"),
value: EMAIL_LEVELS.ONLY_WHEN_AWAY,
},
{ name: I18n.t("user.email_level.never"), value: EMAIL_LEVELS.NEVER },
];
previousRepliesOptions = [
{ name: I18n.t("user.email_previous_replies.always"), value: 0 },
{ name: I18n.t("user.email_previous_replies.unless_emailed"), value: 1 },
{ name: I18n.t("user.email_previous_replies.never"), value: 2 },
];
this.digestFrequencies = [
{ name: I18n.t("user.email_digests.every_30_minutes"), value: 30 },
{ name: I18n.t("user.email_digests.every_hour"), value: 60 },
{ name: I18n.t("user.email_digests.daily"), value: 1440 },
{ name: I18n.t("user.email_digests.weekly"), value: 10080 },
{ name: I18n.t("user.email_digests.every_month"), value: 43200 },
{ name: I18n.t("user.email_digests.every_six_months"), value: 259200 },
];
},
emailLevelOptions = [
{ name: I18n.t("user.email_level.always"), value: EMAIL_LEVELS.ALWAYS },
{
name: I18n.t("user.email_level.only_when_away"),
value: EMAIL_LEVELS.ONLY_WHEN_AWAY,
},
{ name: I18n.t("user.email_level.never"), value: EMAIL_LEVELS.NEVER },
];
digestFrequencies = [
{ name: I18n.t("user.email_digests.every_30_minutes"), value: 30 },
{ name: I18n.t("user.email_digests.every_hour"), value: 60 },
{ name: I18n.t("user.email_digests.daily"), value: 1440 },
{ name: I18n.t("user.email_digests.weekly"), value: 10080 },
{ name: I18n.t("user.email_digests.every_month"), value: 43200 },
{ name: I18n.t("user.email_digests.every_six_months"), value: 259200 },
];
@discourseComputed()
frequencyEstimate() {
@ -71,7 +66,7 @@ export default Controller.extend({
dailyEmailEstimate: estimate,
});
}
},
}
@discourseComputed()
mailingListModeOptions() {
@ -79,7 +74,7 @@ export default Controller.extend({
{ name: this.frequencyEstimate, value: 1 },
{ name: I18n.t("user.mailing_list_mode.individual_no_echo"), value: 2 },
];
},
}
@discourseComputed()
emailFrequencyInstructions() {
@ -88,17 +83,16 @@ export default Controller.extend({
count: this.siteSettings.email_time_window_mins,
})
: null;
},
}
actions: {
save() {
this.set("saved", false);
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
})
.catch(popupAjaxError);
},
},
});
@action
save() {
this.set("saved", false);
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
})
.catch(popupAjaxError);
}
}

View File

@ -1,5 +1,5 @@
import Controller, { inject as controller } from "@ember/controller";
import { computed } from "@ember/object";
import { action, computed } from "@ember/object";
import { not, reads } from "@ember/object/computed";
import { service } from "@ember/service";
import { reload } from "discourse/helpers/page-reloader";
@ -31,22 +31,35 @@ const USER_HOMES = {
const TEXT_SIZES = ["smallest", "smaller", "normal", "larger", "largest"];
const TITLE_COUNT_MODES = ["notifications", "contextual"];
export default Controller.extend({
session: service(),
export default class InterfaceController extends Controller {
@service session;
@controller("preferences") preferencesController;
currentThemeId: -1,
previewingColorScheme: false,
selectedDarkColorSchemeId: null,
preferencesController: controller("preferences"),
makeColorSchemeDefault: true,
canPreviewColorScheme: propertyEqual("model.id", "currentUser.id"),
subpageTitle: I18n.t("user.preferences_nav.interface"),
currentThemeId = -1;
previewingColorScheme = false;
selectedDarkColorSchemeId = null;
makeColorSchemeDefault = true;
@propertyEqual("model.id", "currentUser.id") canPreviewColorScheme;
subpageTitle = I18n.t("user.preferences_nav.interface");
@reads("userSelectableColorSchemes.length") showColorSchemeSelector;
@not("currentSchemeCanBeSelected") showColorSchemeNoneItem;
selectedColorSchemeNoneLabel = I18n.t(
"user.color_schemes.default_description"
);
init() {
this._super(...arguments);
super.init(...arguments);
this.set("selectedDarkColorSchemeId", this.session.userDarkSchemeId);
},
this.set(
"enableDarkMode",
this.get("model.user_option.dark_scheme_id") === -1 ? false : true
);
this.set("selectedColorSchemeId", this.getSelectedColorSchemeId());
}
@discourseComputed("makeThemeDefault")
saveAttrNames(makeThemeDefault) {
@ -75,42 +88,39 @@ export default Controller.extend({
}
return attrs;
},
}
@discourseComputed()
availableLocales() {
return JSON.parse(this.siteSettings.available_locales);
},
}
@discourseComputed
defaultDarkSchemeId() {
return this.siteSettings.default_dark_mode_color_scheme_id;
},
}
@discourseComputed
textSizes() {
return TEXT_SIZES.map((value) => {
return { name: I18n.t(`user.text_size.${value}`), value };
});
},
}
homepageId: computed(
"model.user_option.homepage_id",
"userSelectableHome.[]",
function () {
return (
this.model.user_option.homepage_id ||
this.userSelectableHome.firstObject.value
);
}
),
@computed("model.user_option.homepage_id", "userSelectableHome.[]")
get homepageId() {
return (
this.model.user_option.homepage_id ||
this.userSelectableHome.firstObject.value
);
}
@discourseComputed
titleCountModes() {
return TITLE_COUNT_MODES.map((value) => {
return { name: I18n.t(`user.title_count_mode.${value}`), value };
});
},
}
@discourseComputed
bookmarkAfterNotificationModes() {
@ -120,17 +130,17 @@ export default Controller.extend({
name: I18n.t(`bookmarks.auto_delete_preference.${key.toLowerCase()}`),
};
});
},
}
@discourseComputed
userSelectableThemes() {
return listThemes(this.site);
},
}
@discourseComputed("userSelectableThemes")
showThemeSelector(themes) {
return themes && themes.length > 1;
},
}
@discourseComputed("themeId")
themeIdChanged(themeId) {
@ -140,17 +150,12 @@ export default Controller.extend({
} else {
return this.currentThemeId !== themeId;
}
},
}
@discourseComputed
userSelectableColorSchemes() {
return listColorSchemes(this.site);
},
showColorSchemeSelector: reads("userSelectableColorSchemes.length"),
selectedColorSchemeNoneLabel: I18n.t(
"user.color_schemes.default_description"
),
}
@discourseComputed(
"userSelectableThemes",
@ -168,19 +173,17 @@ export default Controller.extend({
}
return userColorSchemes.findBy("id", theme.color_scheme_id);
},
showColorSchemeNoneItem: not("currentSchemeCanBeSelected"),
}
@discourseComputed("model.user_option.theme_ids", "themeId")
showThemeSetDefault(userOptionThemes, selectedTheme) {
return !userOptionThemes || userOptionThemes[0] !== selectedTheme;
},
}
@discourseComputed("model.user_option.text_size", "textSize")
showTextSetDefault(userOptionTextSize, selectedTextSize) {
return userOptionTextSize !== selectedTextSize;
},
}
homeChanged() {
const siteHome = this.siteSettings.top_menu.split("|")[0].split(",")[0];
@ -192,7 +195,7 @@ export default Controller.extend({
const userHome = USER_HOMES[this.get("model.user_option.homepage_id")];
setDefaultHomepage(userHome || siteHome);
},
}
@discourseComputed()
userSelectableHome() {
@ -226,19 +229,19 @@ export default Controller.extend({
});
return result;
},
}
@discourseComputed
showDarkModeToggle() {
return this.defaultDarkSchemeId > 0 && !this.showDarkColorSchemeSelector;
},
}
@discourseComputed
userSelectableDarkColorSchemes() {
return listColorSchemes(this.site, {
darkOnly: true,
});
},
}
@discourseComputed("userSelectableDarkColorSchemes")
showDarkColorSchemeSelector(darkSchemes) {
@ -247,221 +250,201 @@ export default Controller.extend({
// but we show a checkbox in that case
const minToShow = this.defaultDarkSchemeId > 0 ? 2 : 1;
return darkSchemes && darkSchemes.length > minToShow;
},
}
enableDarkMode: computed({
set(key, value) {
return value;
},
get() {
return this.get("model.user_option.dark_scheme_id") === -1 ? false : true;
},
}),
getSelectedColorSchemeId() {
if (!this.session.userColorSchemeId) {
return;
}
selectedColorSchemeId: computed({
set(key, value) {
return value;
},
get() {
if (!this.session.userColorSchemeId) {
return;
}
const theme = this.userSelectableThemes?.findBy("id", this.themeId);
const theme = this.userSelectableThemes?.findBy("id", this.themeId);
// we don't want to display the numeric ID of a scheme
// when it is set by the theme but not marked as user selectable
if (
theme?.color_scheme_id === this.session.userColorSchemeId &&
!this.userSelectableColorSchemes.findBy(
"id",
this.session.userColorSchemeId
)
) {
return;
} else {
return this.session.userColorSchemeId;
}
}
// we don't want to display the numeric ID of a scheme
// when it is set by the theme but not marked as user selectable
@action
save() {
this.set("saved", false);
const makeThemeDefault = this.makeThemeDefault;
if (makeThemeDefault) {
this.set("model.user_option.theme_ids", [this.themeId]);
}
const makeTextSizeDefault = this.makeTextSizeDefault;
if (makeTextSizeDefault) {
this.set("model.user_option.text_size", this.textSize);
}
if (!this.showColorSchemeSelector) {
this.set("model.user_option.color_scheme_id", null);
} else if (this.makeColorSchemeDefault) {
this.set("model.user_option.color_scheme_id", this.selectedColorSchemeId);
}
if (this.showDarkModeToggle) {
this.set(
"model.user_option.dark_scheme_id",
this.enableDarkMode ? null : -1
);
} else {
// if chosen dark scheme matches site dark scheme, no need to store
if (
theme?.color_scheme_id === this.session.userColorSchemeId &&
!this.userSelectableColorSchemes.findBy(
"id",
this.session.userColorSchemeId
)
this.defaultDarkSchemeId > 0 &&
this.selectedDarkColorSchemeId === this.defaultDarkSchemeId
) {
return;
this.set("model.user_option.dark_scheme_id", null);
} else {
return this.session.userColorSchemeId;
}
},
}),
actions: {
save() {
this.set("saved", false);
const makeThemeDefault = this.makeThemeDefault;
if (makeThemeDefault) {
this.set("model.user_option.theme_ids", [this.themeId]);
}
const makeTextSizeDefault = this.makeTextSizeDefault;
if (makeTextSizeDefault) {
this.set("model.user_option.text_size", this.textSize);
}
if (!this.showColorSchemeSelector) {
this.set("model.user_option.color_scheme_id", null);
} else if (this.makeColorSchemeDefault) {
this.set(
"model.user_option.color_scheme_id",
this.selectedColorSchemeId
);
}
if (this.showDarkModeToggle) {
this.set(
"model.user_option.dark_scheme_id",
this.enableDarkMode ? null : -1
this.selectedDarkColorSchemeId
);
} else {
// if chosen dark scheme matches site dark scheme, no need to store
if (
this.defaultDarkSchemeId > 0 &&
this.selectedDarkColorSchemeId === this.defaultDarkSchemeId
) {
this.set("model.user_option.dark_scheme_id", null);
}
}
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
if (makeThemeDefault) {
setLocalTheme([]);
} else {
this.set(
"model.user_option.dark_scheme_id",
this.selectedDarkColorSchemeId
setLocalTheme(
[this.themeId],
this.get("model.user_option.theme_key_seq")
);
}
}
if (makeTextSizeDefault) {
this.model.updateTextSizeCookie(null);
} else {
this.model.updateTextSizeCookie(this.textSize);
}
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
if (this.makeColorSchemeDefault) {
updateColorSchemeCookie(null);
updateColorSchemeCookie(null, { dark: true });
} else {
updateColorSchemeCookie(this.selectedColorSchemeId);
if (makeThemeDefault) {
setLocalTheme([]);
} else {
setLocalTheme(
[this.themeId],
this.get("model.user_option.theme_key_seq")
);
}
if (makeTextSizeDefault) {
this.model.updateTextSizeCookie(null);
} else {
this.model.updateTextSizeCookie(this.textSize);
}
if (this.makeColorSchemeDefault) {
updateColorSchemeCookie(null);
if (
this.defaultDarkSchemeId > 0 &&
this.selectedDarkColorSchemeId === this.defaultDarkSchemeId
) {
updateColorSchemeCookie(null, { dark: true });
} else {
updateColorSchemeCookie(this.selectedColorSchemeId);
if (
this.defaultDarkSchemeId > 0 &&
this.selectedDarkColorSchemeId === this.defaultDarkSchemeId
) {
updateColorSchemeCookie(null, { dark: true });
} else {
updateColorSchemeCookie(this.selectedDarkColorSchemeId, {
dark: true,
});
}
updateColorSchemeCookie(this.selectedDarkColorSchemeId, {
dark: true,
});
}
this.homeChanged();
if (this.themeId && this.themeId !== this.currentThemeId) {
reload();
}
})
.catch(popupAjaxError);
},
selectTextSize(newSize) {
const classList = document.documentElement.classList;
TEXT_SIZES.forEach((name) => {
const className = `text-size-${name}`;
if (newSize === name) {
classList.add(className);
} else {
classList.remove(className);
}
});
// Force refresh when leaving this screen
this.session.requiresRefresh = true;
this.set("textSize", newSize);
},
this.homeChanged();
loadColorScheme(colorSchemeId) {
this.setProperties({
selectedColorSchemeId: colorSchemeId,
previewingColorScheme: this.canPreviewColorScheme,
});
if (!this.canPreviewColorScheme) {
return;
}
if (colorSchemeId < 0) {
const defaultTheme = this.userSelectableThemes.findBy(
"id",
this.themeId
);
if (defaultTheme && defaultTheme.color_scheme_id) {
colorSchemeId = defaultTheme.color_scheme_id;
if (this.themeId && this.themeId !== this.currentThemeId) {
reload();
}
}
loadColorSchemeStylesheet(colorSchemeId, this.themeId);
if (this.selectedDarkColorSchemeId === -1) {
// set this same scheme for dark mode preview when dark scheme is disabled
loadColorSchemeStylesheet(colorSchemeId, this.themeId, true);
}
},
})
.catch(popupAjaxError);
}
loadDarkColorScheme(colorSchemeId) {
this.setProperties({
selectedDarkColorSchemeId: colorSchemeId,
previewingColorScheme: this.canPreviewColorScheme,
});
@action
selectTextSize(newSize) {
const classList = document.documentElement.classList;
if (!this.canPreviewColorScheme) {
return;
}
if (colorSchemeId === -1) {
// load preview of regular scheme when dark scheme is disabled
loadColorSchemeStylesheet(
this.selectedColorSchemeId,
this.themeId,
true
);
this.session.set("darkModeAvailable", false);
TEXT_SIZES.forEach((name) => {
const className = `text-size-${name}`;
if (newSize === name) {
classList.add(className);
} else {
loadColorSchemeStylesheet(colorSchemeId, this.themeId, true);
this.session.set("darkModeAvailable", true);
classList.remove(className);
}
},
});
undoColorSchemePreview() {
this.setProperties({
selectedColorSchemeId: this.session.userColorSchemeId,
selectedDarkColorSchemeId: this.session.userDarkSchemeId,
previewingColorScheme: false,
});
const darkStylesheet = document.querySelector("link#cs-preview-dark"),
lightStylesheet = document.querySelector("link#cs-preview-light");
if (darkStylesheet) {
darkStylesheet.remove();
// Force refresh when leaving this screen
this.session.requiresRefresh = true;
this.set("textSize", newSize);
}
@action
loadColorScheme(colorSchemeId) {
this.setProperties({
selectedColorSchemeId: colorSchemeId,
previewingColorScheme: this.canPreviewColorScheme,
});
if (!this.canPreviewColorScheme) {
return;
}
if (colorSchemeId < 0) {
const defaultTheme = this.userSelectableThemes.findBy("id", this.themeId);
if (defaultTheme && defaultTheme.color_scheme_id) {
colorSchemeId = defaultTheme.color_scheme_id;
}
}
loadColorSchemeStylesheet(colorSchemeId, this.themeId);
if (this.selectedDarkColorSchemeId === -1) {
// set this same scheme for dark mode preview when dark scheme is disabled
loadColorSchemeStylesheet(colorSchemeId, this.themeId, true);
}
}
if (lightStylesheet) {
lightStylesheet.remove();
}
},
@action
loadDarkColorScheme(colorSchemeId) {
this.setProperties({
selectedDarkColorSchemeId: colorSchemeId,
previewingColorScheme: this.canPreviewColorScheme,
});
resetSeenUserTips() {
this.model.set("user_option.skip_new_user_tips", false);
this.model.set("user_option.seen_popups", null);
return this.model.save(["skip_new_user_tips", "seen_popups"]);
},
},
});
if (!this.canPreviewColorScheme) {
return;
}
if (colorSchemeId === -1) {
// load preview of regular scheme when dark scheme is disabled
loadColorSchemeStylesheet(this.selectedColorSchemeId, this.themeId, true);
this.session.set("darkModeAvailable", false);
} else {
loadColorSchemeStylesheet(colorSchemeId, this.themeId, true);
this.session.set("darkModeAvailable", true);
}
}
@action
undoColorSchemePreview() {
this.setProperties({
selectedColorSchemeId: this.session.userColorSchemeId,
selectedDarkColorSchemeId: this.session.userDarkSchemeId,
previewingColorScheme: false,
});
const darkStylesheet = document.querySelector("link#cs-preview-dark"),
lightStylesheet = document.querySelector("link#cs-preview-light");
if (darkStylesheet) {
darkStylesheet.remove();
}
if (lightStylesheet) {
lightStylesheet.remove();
}
}
@action
resetSeenUserTips() {
this.model.set("user_option.skip_new_user_tips", false);
this.model.set("user_option.seen_popups", null);
return this.model.save(["skip_new_user_tips", "seen_popups"]);
}
}

View File

@ -1,12 +1,13 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { popupAjaxError } from "discourse/lib/ajax-error";
import I18n from "discourse-i18n";
export default Controller.extend({
subpageTitle: I18n.t("user.preferences_nav.notifications"),
export default class NotificationsController extends Controller {
subpageTitle = I18n.t("user.preferences_nav.notifications");
init() {
this._super(...arguments);
super.init(...arguments);
this.saveAttrNames = [
"muted_usernames",
@ -28,17 +29,16 @@ export default Controller.extend({
{ name: I18n.t("user.like_notification_frequency.first_time"), value: 2 },
{ name: I18n.t("user.like_notification_frequency.never"), value: 3 },
];
},
}
actions: {
save() {
this.set("saved", false);
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
})
.catch(popupAjaxError);
},
},
});
@action
save() {
this.set("saved", false);
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
})
.catch(popupAjaxError);
}
}

View File

@ -9,30 +9,36 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
export default Controller.extend({
dialog: service(),
modal: service(),
subpageTitle: I18n.t("user.preferences_nav.profile"),
init() {
this._super(...arguments);
this.saveAttrNames = [
"bio_raw",
"website",
"location",
"custom_fields",
"user_fields",
"profile_background_upload_url",
"card_background_upload_url",
"date_of_birth",
"timezone",
"default_calendar",
];
export default class ProfileController extends Controller {
@service dialog;
@service modal;
this.calendarOptions = [
{ name: I18n.t("download_calendar.google"), value: "google" },
{ name: I18n.t("download_calendar.ics"), value: "ics" },
];
},
subpageTitle = I18n.t("user.preferences_nav.profile");
@readOnly("model.can_change_bio") canChangeBio;
@readOnly("model.can_change_location") canChangeLocation;
@readOnly("model.can_change_website") canChangeWebsite;
@readOnly("model.can_upload_profile_header") canUploadProfileHeader;
@readOnly("model.can_upload_user_card_background")
canUploadUserCardBackground;
saveAttrNames = [
"bio_raw",
"website",
"location",
"custom_fields",
"user_fields",
"profile_background_upload_url",
"card_background_upload_url",
"date_of_birth",
"timezone",
"default_calendar",
];
calendarOptions = [
{ name: I18n.t("download_calendar.google"), value: "google" },
{ name: I18n.t("download_calendar.ics"), value: "ics" },
];
@discourseComputed("model.user_fields.@each.value")
userFields() {
@ -57,29 +63,17 @@ export default Controller.extend({
const value = this.model.user_fields?.[field.id.toString()];
return EmberObject.create({ field, value });
});
},
}
@discourseComputed("currentUser.needs_required_fields_check")
showEnforcedRequiredFieldsNotice(needsRequiredFieldsCheck) {
return needsRequiredFieldsCheck;
},
}
@discourseComputed("model.user_option.default_calendar")
canChangeDefaultCalendar(defaultCalendar) {
return defaultCalendar !== "none_selected";
},
canChangeBio: readOnly("model.can_change_bio"),
canChangeLocation: readOnly("model.can_change_location"),
canChangeWebsite: readOnly("model.can_change_website"),
canUploadProfileHeader: readOnly("model.can_upload_profile_header"),
canUploadUserCardBackground: readOnly(
"model.can_upload_user_card_background"
),
}
@action
async showFeaturedTopicModal() {
@ -90,7 +84,7 @@ export default Controller.extend({
},
});
document.querySelector(".feature-topic-on-profile-btn")?.focus();
},
}
_missingRequiredFields(siteFields, userFields) {
return siteFields
@ -100,59 +94,61 @@ export default Controller.extend({
isEmpty(userFields[siteField.id])
)
.map((field) => EmberObject.create({ field, value: "" }));
},
}
actions: {
clearFeaturedTopicFromProfile() {
this.dialog.yesNoConfirm({
message: I18n.t("user.feature_topic_on_profile.clear.warning"),
didConfirm: () => {
return ajax(`/u/${this.model.username}/clear-featured-topic`, {
type: "PUT",
@action
clearFeaturedTopicFromProfile() {
this.dialog.yesNoConfirm({
message: I18n.t("user.feature_topic_on_profile.clear.warning"),
didConfirm: () => {
return ajax(`/u/${this.model.username}/clear-featured-topic`, {
type: "PUT",
})
.then(() => {
this.model.set("featured_topic", null);
})
.then(() => {
this.model.set("featured_topic", null);
})
.catch(popupAjaxError);
},
});
},
.catch(popupAjaxError);
},
});
}
useCurrentTimezone() {
this.model.set("user_option.timezone", moment.tz.guess());
},
@action
useCurrentTimezone() {
this.model.set("user_option.timezone", moment.tz.guess());
}
_updateUserFields() {
const model = this.model,
userFields = this.userFields;
@action
_updateUserFields() {
const model = this.model,
userFields = this.userFields;
if (!isEmpty(userFields)) {
const modelFields = model.get("user_fields");
if (!isEmpty(modelFields)) {
userFields.forEach(function (uf) {
const value = uf.get("value");
modelFields[uf.get("field.id").toString()] = isEmpty(value)
? null
: value;
});
}
}
},
save() {
this.set("saved", false);
// Update the user fields
this.send("_updateUserFields");
return this.model
.save(this.saveAttrNames)
.then(({ user }) => this.model.set("bio_cooked", user.bio_cooked))
.catch(popupAjaxError)
.finally(() => {
this.currentUser.set("needs_required_fields_check", false);
this.set("saved", true);
if (!isEmpty(userFields)) {
const modelFields = model.get("user_fields");
if (!isEmpty(modelFields)) {
userFields.forEach(function (uf) {
const value = uf.get("value");
modelFields[uf.get("field.id").toString()] = isEmpty(value)
? null
: value;
});
},
},
});
}
}
}
@action
save() {
this.set("saved", false);
// Update the user fields
this.send("_updateUserFields");
return this.model
.save(this.saveAttrNames)
.then(({ user }) => this.model.set("bio_cooked", user.bio_cooked))
.catch(popupAjaxError)
.finally(() => {
this.currentUser.set("needs_required_fields_check", false);
this.set("saved", true);
});
}
}

View File

@ -17,34 +17,35 @@ import { SECOND_FACTOR_METHODS } from "discourse/models/user";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
export default Controller.extend(CanCheckEmails, {
dialog: service(),
modal: service(),
siteSettings: service(),
loading: false,
dirty: false,
errorMessage: null,
newUsername: null,
backupEnabled: alias("model.second_factor_backup_enabled"),
secondFactorMethod: SECOND_FACTOR_METHODS.TOTP,
totps: [],
security_keys: [],
export default class SecondFactorController extends Controller.extend(
CanCheckEmails
) {
@service dialog;
@service modal;
@service siteSettings;
init() {
this._super(...arguments);
},
loading = false;
dirty = false;
errorMessage = null;
newUsername = null;
@alias("model.second_factor_backup_enabled") backupEnabled;
secondFactorMethod = SECOND_FACTOR_METHODS.TOTP;
totps = [];
security_keys = [];
@discourseComputed
hasOAuth() {
return findAll().length > 0;
},
}
@discourseComputed
displayOAuthWarning() {
return (
this.hasOAuth && this.siteSettings.enforce_second_factor_on_external_auth
);
},
}
@discourseComputed("currentUser")
showEnforcedWithOAuthNotice(user) {
@ -54,7 +55,7 @@ export default Controller.extend(CanCheckEmails, {
this.hasOAuth &&
!this.siteSettings.enforce_second_factor_on_external_auth
);
},
}
@discourseComputed("currentUser")
showEnforcedNotice(user) {
@ -63,12 +64,12 @@ export default Controller.extend(CanCheckEmails, {
user.enforcedSecondFactor &&
this.siteSettings.enforce_second_factor_on_external_auth
);
},
}
@discourseComputed("totps", "security_keys")
hasSecondFactors(totps, security_keys) {
return totps.length > 0 || security_keys.length > 0;
},
}
async createToTpModal() {
try {
@ -84,7 +85,7 @@ export default Controller.extend(CanCheckEmails, {
} catch (error) {
popupAjaxError(error);
}
},
}
async createSecurityKeyModal() {
try {
@ -100,7 +101,7 @@ export default Controller.extend(CanCheckEmails, {
} catch (error) {
popupAjaxError(error);
}
},
}
@action
handleError(error) {
@ -116,17 +117,17 @@ export default Controller.extend(CanCheckEmails, {
} else {
popupAjaxError(error);
}
},
}
@action
setBackupEnabled(value) {
this.set("backupEnabled", value);
},
}
@action
setCodesRemaining(value) {
this.model.set("second_factor_remaining_backup_codes", value);
},
}
@action
loadSecondFactors() {
@ -152,12 +153,12 @@ export default Controller.extend(CanCheckEmails, {
})
.catch((e) => this.handleError(e))
.finally(() => this.set("loading", false));
},
}
@action
markDirty() {
this.set("dirty", true);
},
}
@action
async createTotp() {
@ -177,7 +178,7 @@ export default Controller.extend(CanCheckEmails, {
} catch (error) {
popupAjaxError(error);
}
},
}
@action
async createSecurityKey() {
@ -197,175 +198,179 @@ export default Controller.extend(CanCheckEmails, {
} catch (error) {
popupAjaxError(error);
}
},
}
actions: {
disableAllSecondFactors() {
if (this.loading) {
return;
}
@action
disableAllSecondFactors() {
if (this.loading) {
return;
}
this.dialog.deleteConfirm({
title: I18n.t("user.second_factor.disable_confirm"),
bodyComponent: SecondFactorConfirmPhrase,
bodyComponentModel: {
totps: this.totps,
security_keys: this.security_keys,
},
confirmButtonLabel: "user.second_factor.disable",
confirmButtonDisabled: true,
confirmButtonIcon: "ban",
cancelButtonClass: "btn-flat",
didConfirm: () => {
this.model
.disableAllSecondFactors()
.then(() => {
const usernameLower = this.model.username.toLowerCase();
DiscourseURL.redirectTo(userPath(`${usernameLower}/preferences`));
})
.catch((e) => this.handleError(e))
.finally(() => this.set("loading", false));
},
});
},
disableSingleSecondFactor(secondFactorMethod) {
if (this.totps.concat(this.security_keys).length === 1) {
this.send("disableAllSecondFactors");
return;
}
this.dialog.deleteConfirm({
title: I18n.t("user.second_factor.delete_single_confirm_title"),
message: I18n.t("user.second_factor.delete_single_confirm_message", {
name: secondFactorMethod.name,
}),
confirmButtonLabel: "user.second_factor.delete",
confirmButtonIcon: "ban",
cancelButtonClass: "btn-flat",
didConfirm: () => {
if (this.totps.includes(secondFactorMethod)) {
this.currentUser
.updateSecondFactor(
secondFactorMethod.id,
secondFactorMethod.name,
true,
secondFactorMethod.method
)
.then((response) => {
if (response.error) {
return;
}
this.markDirty();
this.set(
"totps",
this.totps.filter(
(totp) =>
totp.id !== secondFactorMethod.id ||
totp.method !== secondFactorMethod.method
)
);
})
.catch((e) => this.handleError(e))
.finally(() => {
this.set("loading", false);
});
}
this.dialog.deleteConfirm({
title: I18n.t("user.second_factor.disable_confirm"),
bodyComponent: SecondFactorConfirmPhrase,
bodyComponentModel: {
totps: this.totps,
security_keys: this.security_keys,
},
confirmButtonLabel: "user.second_factor.disable",
confirmButtonDisabled: true,
confirmButtonIcon: "ban",
cancelButtonClass: "btn-flat",
didConfirm: () => {
this.model
.disableAllSecondFactors()
.then(() => {
const usernameLower = this.model.username.toLowerCase();
DiscourseURL.redirectTo(userPath(`${usernameLower}/preferences`));
})
.catch((e) => this.handleError(e))
.finally(() => this.set("loading", false));
},
});
}
if (this.security_keys.includes(secondFactorMethod)) {
this.currentUser
.updateSecurityKey(
secondFactorMethod.id,
secondFactorMethod.name,
true
)
.then((response) => {
if (response.error) {
return;
}
this.markDirty();
this.set(
"security_keys",
this.security_keys.filter(
(securityKey) => securityKey.id !== secondFactorMethod.id
)
);
})
.catch((e) => this.handleError(e))
.finally(() => {
this.set("loading", false);
});
}
},
});
},
disableSecondFactorBackup() {
this.dialog.deleteConfirm({
title: I18n.t("user.second_factor.delete_backup_codes_confirm_title"),
message: I18n.t(
"user.second_factor.delete_backup_codes_confirm_message"
),
confirmButtonLabel: "user.second_factor.delete",
confirmButtonIcon: "ban",
cancelButtonClass: "btn-flat",
didConfirm: () => {
this.set("backupCodes", []);
this.set("loading", true);
this.model
.updateSecondFactor(0, "", true, SECOND_FACTOR_METHODS.BACKUP_CODE)
@action
disableSingleSecondFactor(secondFactorMethod) {
if (this.totps.concat(this.security_keys).length === 1) {
this.send("disableAllSecondFactors");
return;
}
this.dialog.deleteConfirm({
title: I18n.t("user.second_factor.delete_single_confirm_title"),
message: I18n.t("user.second_factor.delete_single_confirm_message", {
name: secondFactorMethod.name,
}),
confirmButtonLabel: "user.second_factor.delete",
confirmButtonIcon: "ban",
cancelButtonClass: "btn-flat",
didConfirm: () => {
if (this.totps.includes(secondFactorMethod)) {
this.currentUser
.updateSecondFactor(
secondFactorMethod.id,
secondFactorMethod.name,
true,
secondFactorMethod.method
)
.then((response) => {
if (response.error) {
this.set("errorMessage", response.error);
return;
}
this.set("errorMessage", null);
this.model.set("second_factor_backup_enabled", false);
this.markDirty();
this.send("closeModal");
this.set(
"totps",
this.totps.filter(
(totp) =>
totp.id !== secondFactorMethod.id ||
totp.method !== secondFactorMethod.method
)
);
})
.catch((error) => {
this.send("closeModal");
this.onError(error);
.catch((e) => this.handleError(e))
.finally(() => {
this.set("loading", false);
});
}
if (this.security_keys.includes(secondFactorMethod)) {
this.currentUser
.updateSecurityKey(
secondFactorMethod.id,
secondFactorMethod.name,
true
)
.then((response) => {
if (response.error) {
return;
}
this.markDirty();
this.set(
"security_keys",
this.security_keys.filter(
(securityKey) => securityKey.id !== secondFactorMethod.id
)
);
})
.finally(() => this.set("loading", false));
},
});
},
.catch((e) => this.handleError(e))
.finally(() => {
this.set("loading", false);
});
}
},
});
}
async editSecurityKey(security_key) {
await this.modal.show(SecondFactorEditSecurityKey, {
model: {
securityKey: security_key,
user: this.model,
markDirty: () => this.markDirty(),
onError: (e) => this.handleError(e),
},
});
this.loadSecondFactors();
},
@action
disableSecondFactorBackup() {
this.dialog.deleteConfirm({
title: I18n.t("user.second_factor.delete_backup_codes_confirm_title"),
message: I18n.t("user.second_factor.delete_backup_codes_confirm_message"),
confirmButtonLabel: "user.second_factor.delete",
confirmButtonIcon: "ban",
cancelButtonClass: "btn-flat",
didConfirm: () => {
this.set("backupCodes", []);
this.set("loading", true);
async editSecondFactor(second_factor) {
await this.modal.show(SecondFactorEdit, {
model: {
secondFactor: second_factor,
user: this.model,
markDirty: () => this.markDirty(),
onError: (e) => this.handleError(e),
},
});
this.loadSecondFactors();
},
this.model
.updateSecondFactor(0, "", true, SECOND_FACTOR_METHODS.BACKUP_CODE)
.then((response) => {
if (response.error) {
this.set("errorMessage", response.error);
return;
}
async editSecondFactorBackup() {
await this.modal.show(SecondFactorBackupEdit, {
model: {
secondFactor: this.model,
markDirty: () => this.markDirty(),
onError: (e) => this.handleError(e),
setBackupEnabled: (e) => this.setBackupEnabled(e),
setCodesRemaining: (e) => this.setCodesRemaining(e),
},
});
},
},
});
this.set("errorMessage", null);
this.model.set("second_factor_backup_enabled", false);
this.markDirty();
this.send("closeModal");
})
.catch((error) => {
this.send("closeModal");
this.onError(error);
})
.finally(() => this.set("loading", false));
},
});
}
@action
async editSecurityKey(security_key) {
await this.modal.show(SecondFactorEditSecurityKey, {
model: {
securityKey: security_key,
user: this.model,
markDirty: () => this.markDirty(),
onError: (e) => this.handleError(e),
},
});
this.loadSecondFactors();
}
@action
async editSecondFactor(second_factor) {
await this.modal.show(SecondFactorEdit, {
model: {
secondFactor: second_factor,
user: this.model,
markDirty: () => this.markDirty(),
onError: (e) => this.handleError(e),
},
});
this.loadSecondFactors();
}
@action
async editSecondFactorBackup() {
await this.modal.show(SecondFactorBackupEdit, {
model: {
secondFactor: this.model,
markDirty: () => this.markDirty(),
onError: (e) => this.handleError(e),
setBackupEnabled: (e) => this.setBackupEnabled(e),
setCodesRemaining: (e) => this.setCodesRemaining(e),
},
});
}
}

View File

@ -16,13 +16,19 @@ import I18n from "discourse-i18n";
// Number of tokens shown by default.
const DEFAULT_AUTH_TOKENS_COUNT = 2;
export default Controller.extend(CanCheckEmails, {
modal: service(),
dialog: service(),
router: service(),
passwordProgress: null,
subpageTitle: I18n.t("user.preferences_nav.security"),
showAllAuthTokens: false,
export default class SecurityController extends Controller.extend(
CanCheckEmails
) {
@service modal;
@service dialog;
@service router;
passwordProgress = null;
subpageTitle = I18n.t("user.preferences_nav.security");
showAllAuthTokens = false;
@gt("model.user_auth_tokens.length", DEFAULT_AUTH_TOKENS_COUNT)
canShowAllAuthTokens;
get canUsePasskeys() {
return (
@ -31,7 +37,7 @@ export default Controller.extend(CanCheckEmails, {
this.siteSettings.enable_passkeys &&
isWebauthnSupported()
);
},
}
@discourseComputed("model.is_anonymous")
canChangePassword(isAnonymous) {
@ -43,7 +49,7 @@ export default Controller.extend(CanCheckEmails, {
this.siteSettings.enable_local_logins
);
}
},
}
@discourseComputed("showAllAuthTokens", "model.user_auth_tokens")
authTokens(showAllAuthTokens, tokens) {
@ -60,12 +66,7 @@ export default Controller.extend(CanCheckEmails, {
return showAllAuthTokens
? tokens
: tokens.slice(0, DEFAULT_AUTH_TOKENS_COUNT);
},
canShowAllAuthTokens: gt(
"model.user_auth_tokens.length",
DEFAULT_AUTH_TOKENS_COUNT
),
}
@action
changePassword(event) {
@ -89,13 +90,13 @@ export default Controller.extend(CanCheckEmails, {
});
});
}
},
}
@action
toggleShowAllAuthTokens(event) {
event?.preventDefault();
this.toggleProperty("showAllAuthTokens");
},
}
@action
revokeAuthToken(token, event) {
@ -115,7 +116,7 @@ export default Controller.extend(CanCheckEmails, {
} // All sessions revoked
})
.catch(popupAjaxError);
},
}
@action
async manage2FA() {
@ -136,19 +137,17 @@ export default Controller.extend(CanCheckEmails, {
} catch (error) {
popupAjaxError(error);
}
},
}
actions: {
save() {
this.set("saved", false);
@action
save() {
this.set("saved", false);
return this.model
.then(() => this.set("saved", true))
.catch(popupAjaxError);
},
return this.model.then(() => this.set("saved", true)).catch(popupAjaxError);
}
showToken(token) {
this.modal.show(AuthTokenModal, { model: token });
},
},
});
@action
showToken(token) {
this.modal.show(AuthTokenModal, { model: token });
}
}

View File

@ -1,18 +1,15 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators";
export default Controller.extend({
init() {
this._super(...arguments);
this.saveAttrNames = [
"muted_tags",
"tracked_tags",
"watched_tags",
"watching_first_post_tags",
];
},
export default class TagsController extends Controller {
saveAttrNames = [
"muted_tags",
"tracked_tags",
"watched_tags",
"watching_first_post_tags",
];
@discourseComputed(
"model.watched_tags.[]",
@ -22,17 +19,16 @@ export default Controller.extend({
)
selectedTags(watched, watchedFirst, tracked, muted) {
return [].concat(watched, watchedFirst, tracked, muted).filter((t) => t);
},
}
actions: {
save() {
this.set("saved", false);
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
})
.catch(popupAjaxError);
},
},
});
@action
save() {
this.set("saved", false);
return this.model
.save(this.saveAttrNames)
.then(() => {
this.set("saved", true);
})
.catch(popupAjaxError);
}
}

View File

@ -5,38 +5,37 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
import { makeArray } from "discourse-common/lib/helpers";
import discourseComputed from "discourse-common/utils/decorators";
export default Controller.extend({
allowPmUsersEnabled: and(
export default class UsersController extends Controller {
@and(
"model.user_option.enable_allowed_pm_users",
"model.user_option.allow_private_messages"
),
)
allowPmUsersEnabled;
mutedUsernames: computed("model.muted_usernames", {
get() {
let usernames = this.model.muted_usernames;
@computed("model.muted_usernames")
get mutedUsernames() {
let usernames = this.model.muted_usernames;
if (typeof usernames === "string") {
usernames = usernames.split(",").filter(Boolean);
}
if (typeof usernames === "string") {
usernames = usernames.split(",").filter(Boolean);
}
return makeArray(usernames).uniq();
},
}),
return makeArray(usernames).uniq();
}
allowedPmUsernames: computed("model.allowed_pm_usernames", {
get() {
let usernames = this.model.allowed_pm_usernames;
@computed("model.allowed_pm_usernames")
get allowedPmUsernames() {
let usernames = this.model.allowed_pm_usernames;
if (typeof usernames === "string") {
usernames = usernames.split(",").filter(Boolean);
}
if (typeof usernames === "string") {
usernames = usernames.split(",").filter(Boolean);
}
return makeArray(usernames).uniq();
},
}),
return makeArray(usernames).uniq();
}
init() {
this._super(...arguments);
super.init(...arguments);
this.saveAttrNames = [
"allow_private_messages",
@ -44,22 +43,22 @@ export default Controller.extend({
"allowed_pm_usernames",
"enable_allowed_pm_users",
];
},
}
@action
onChangeMutedUsernames(usernames) {
this.model.set("muted_usernames", usernames.uniq().join(","));
},
}
@action
onChangeAllowedPmUsernames(usernames) {
this.model.set("allowed_pm_usernames", usernames.uniq().join(","));
},
}
@discourseComputed("model.user_option.allow_private_messages")
disableAllowPmUsersSetting(allowPrivateMessages) {
return !allowPrivateMessages;
},
}
@action
save() {
@ -69,5 +68,5 @@ export default Controller.extend({
.save(this.saveAttrNames)
.then(() => this.set("saved", true))
.catch(popupAjaxError);
},
});
}
}