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:
parent
7210f36801
commit
efa08658be
|
@ -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;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue