DEV: Convert User model to native class syntax (#25628)
This commit was created with a combination of the ember-native-class-codemod and manual cleanup. User-status-related functionality was previously encapsulated in its own `User.reopen` call, which is essentially an 'inline mixin'. This commit refactors it into a utility class, with an instance accessible on `User#statusManager`
This commit is contained in:
parent
9883e6a0c8
commit
061e79297f
|
@ -207,7 +207,7 @@ export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
|||
);
|
||||
}
|
||||
this.setProperties({ user });
|
||||
this.user.trackStatus();
|
||||
this.user.statusManager.trackStatus();
|
||||
return user;
|
||||
})
|
||||
.catch(() => this._close())
|
||||
|
@ -216,7 +216,7 @@ export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
|||
|
||||
_close() {
|
||||
if (this.user) {
|
||||
this.user.stopTrackingStatus();
|
||||
this.user.statusManager.stopTrackingStatus();
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
|
|
|
@ -14,12 +14,12 @@ export default Component.extend({
|
|||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
this.user?.trackStatus?.();
|
||||
this.user?.statusManager?.trackStatus();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.user?.stopTrackingStatus?.();
|
||||
this.user?.statusManager?.stopTrackingStatus();
|
||||
},
|
||||
|
||||
@discourseComputed("user.username")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { getOwner, setOwner } from "@ember/application";
|
||||
import { A } from "@ember/array";
|
||||
import EmberObject, { computed, get, getProperties } from "@ember/object";
|
||||
import { dependentKeyCompat } from "@ember/object/compat";
|
||||
|
@ -169,74 +170,83 @@ function userOption(userOptionKey) {
|
|||
});
|
||||
}
|
||||
|
||||
const User = RestModel.extend({
|
||||
appEvents: service(),
|
||||
userTips: service(),
|
||||
export default class User extends RestModel.extend(Evented) {
|
||||
@service appEvents;
|
||||
@service userTips;
|
||||
|
||||
mailing_list_mode: userOption("mailing_list_mode"),
|
||||
external_links_in_new_tab: userOption("external_links_in_new_tab"),
|
||||
enable_quoting: userOption("enable_quoting"),
|
||||
dynamic_favicon: userOption("dynamic_favicon"),
|
||||
automatically_unpin_topics: userOption("automatically_unpin_topics"),
|
||||
likes_notifications_disabled: userOption("likes_notifications_disabled"),
|
||||
hide_profile_and_presence: userOption("hide_profile_and_presence"),
|
||||
title_count_mode: userOption("title_count_mode"),
|
||||
enable_defer: userOption("enable_defer"),
|
||||
timezone: userOption("timezone"),
|
||||
skip_new_user_tips: userOption("skip_new_user_tips"),
|
||||
default_calendar: userOption("default_calendar"),
|
||||
bookmark_auto_delete_preference: userOption(
|
||||
"bookmark_auto_delete_preference"
|
||||
),
|
||||
seen_popups: userOption("seen_popups"),
|
||||
should_be_redirected_to_top: userOption("should_be_redirected_to_top"),
|
||||
redirected_to_top: userOption("redirected_to_top"),
|
||||
treat_as_new_topic_start_date: userOption("treat_as_new_topic_start_date"),
|
||||
@userOption("mailing_list_mode") mailing_list_mode;
|
||||
@userOption("external_links_in_new_tab") external_links_in_new_tab;
|
||||
@userOption("enable_quoting") enable_quoting;
|
||||
@userOption("dynamic_favicon") dynamic_favicon;
|
||||
@userOption("automatically_unpin_topics") automatically_unpin_topics;
|
||||
@userOption("likes_notifications_disabled") likes_notifications_disabled;
|
||||
@userOption("hide_profile_and_presence") hide_profile_and_presence;
|
||||
@userOption("title_count_mode") title_count_mode;
|
||||
@userOption("enable_defer") enable_defer;
|
||||
@userOption("timezone") timezone;
|
||||
@userOption("skip_new_user_tips") skip_new_user_tips;
|
||||
@userOption("default_calendar") default_calendar;
|
||||
@userOption("bookmark_auto_delete_preference")
|
||||
bookmark_auto_delete_preference;
|
||||
@userOption("seen_popups") seen_popups;
|
||||
@userOption("should_be_redirected_to_top") should_be_redirected_to_top;
|
||||
@userOption("redirected_to_top") redirected_to_top;
|
||||
@userOption("treat_as_new_topic_start_date") treat_as_new_topic_start_date;
|
||||
|
||||
hasPMs: gt("private_messages_stats.all", 0),
|
||||
hasStartedPMs: gt("private_messages_stats.mine", 0),
|
||||
hasUnreadPMs: gt("private_messages_stats.unread", 0),
|
||||
@gt("private_messages_stats.all", 0) hasPMs;
|
||||
@gt("private_messages_stats.mine", 0) hasStartedPMs;
|
||||
@gt("private_messages_stats.unread", 0) hasUnreadPMs;
|
||||
@url("id", "username_lower", "/admin/users/%@1/%@2") adminPath;
|
||||
@equal("trust_level", 0) isBasic;
|
||||
@equal("trust_level", 3) isRegular;
|
||||
@equal("trust_level", 4) isLeader;
|
||||
@or("staff", "isLeader") canManageTopic;
|
||||
@alias("sidebar_category_ids") sidebarCategoryIds;
|
||||
@alias("sidebar_sections") sidebarSections;
|
||||
@mapBy("sidebarTags", "name") sidebarTagNames;
|
||||
@filterBy("groups", "has_messages", true) groupsWithMessages;
|
||||
|
||||
numGroupsToDisplay = 2;
|
||||
|
||||
statusManager = new UserStatusManager(this);
|
||||
|
||||
@discourseComputed("can_be_deleted", "post_count")
|
||||
canBeDeleted(canBeDeleted, postCount) {
|
||||
const maxPostCount = this.siteSettings.delete_all_posts_max;
|
||||
return canBeDeleted && postCount <= maxPostCount;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
stream() {
|
||||
return UserStream.create({ user: this });
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
bookmarks() {
|
||||
return Bookmark.create({ user: this });
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
postsStream() {
|
||||
return UserPostsStream.create({ user: this });
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
userDraftsStream() {
|
||||
return UserDraftsStream.create({ user: this });
|
||||
},
|
||||
}
|
||||
|
||||
staff: computed("admin", "moderator", {
|
||||
get() {
|
||||
return this.admin || this.moderator;
|
||||
},
|
||||
@computed("admin", "moderator")
|
||||
get staff() {
|
||||
return this.admin || this.moderator;
|
||||
}
|
||||
|
||||
// prevents staff property to be overridden
|
||||
set() {
|
||||
return this.admin || this.moderator;
|
||||
},
|
||||
}),
|
||||
// prevents staff property to be overridden
|
||||
set staff(value) {}
|
||||
|
||||
destroySession() {
|
||||
return ajax(`/session/${this.username}`, { type: "DELETE" });
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("username_lower")
|
||||
searchContext(username) {
|
||||
|
@ -245,7 +255,7 @@ const User = RestModel.extend({
|
|||
id: username,
|
||||
user: this,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("username", "name")
|
||||
displayName(username, name) {
|
||||
|
@ -253,7 +263,7 @@ const User = RestModel.extend({
|
|||
return name;
|
||||
}
|
||||
return username;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("profile_background_upload_url")
|
||||
profileBackgroundUrl(bgUrl) {
|
||||
|
@ -261,13 +271,13 @@ const User = RestModel.extend({
|
|||
return htmlSafe("");
|
||||
}
|
||||
return htmlSafe("background-image: url(" + getURLWithCDN(bgUrl) + ")");
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
path() {
|
||||
// no need to observe, requires a hard refresh to update
|
||||
return userPath(this.username_lower);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
userApiKeys() {
|
||||
|
@ -287,7 +297,7 @@ const User = RestModel.extend({
|
|||
return obj;
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
revokeApiKey(key) {
|
||||
return ajax("/user-api-key/revoke", {
|
||||
|
@ -296,7 +306,7 @@ const User = RestModel.extend({
|
|||
}).then(() => {
|
||||
key.set("revoked", true);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
undoRevokeApiKey(key) {
|
||||
return ajax("/user-api-key/undo-revoke", {
|
||||
|
@ -305,7 +315,7 @@ const User = RestModel.extend({
|
|||
}).then(() => {
|
||||
key.set("revoked", false);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
pmPath(topic) {
|
||||
const userId = this.id;
|
||||
|
@ -323,35 +333,33 @@ const User = RestModel.extend({
|
|||
return userPath(`${username}/messages/group/${groups[0].name}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
adminPath: url("id", "username_lower", "/admin/users/%@1/%@2"),
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
mutedTopicsPath() {
|
||||
return defaultHomepage() === "latest"
|
||||
? getURL("/?state=muted")
|
||||
: getURL("/latest?state=muted");
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
watchingTopicsPath() {
|
||||
return defaultHomepage() === "latest"
|
||||
? getURL("/?state=watching")
|
||||
: getURL("/latest?state=watching");
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed()
|
||||
trackingTopicsPath() {
|
||||
return defaultHomepage() === "latest"
|
||||
? getURL("/?state=tracking")
|
||||
: getURL("/latest?state=tracking");
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("username")
|
||||
username_lower(username) {
|
||||
return username.toLowerCase();
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("trust_level")
|
||||
trustLevel(trustLevel) {
|
||||
|
@ -359,36 +367,37 @@ const User = RestModel.extend({
|
|||
"id",
|
||||
parseInt(trustLevel, 10)
|
||||
);
|
||||
},
|
||||
|
||||
isBasic: equal("trust_level", 0),
|
||||
isRegular: equal("trust_level", 3),
|
||||
isLeader: equal("trust_level", 4),
|
||||
canManageTopic: or("staff", "isLeader"),
|
||||
}
|
||||
|
||||
@discourseComputed("previous_visit_at")
|
||||
previousVisitAt(previous_visit_at) {
|
||||
return new Date(previous_visit_at);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("suspended_till")
|
||||
suspended(suspendedTill) {
|
||||
return suspendedTill && moment(suspendedTill).isAfter();
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("suspended_till")
|
||||
suspendedForever: isForever,
|
||||
suspendedForever(suspendedTill) {
|
||||
return isForever(suspendedTill);
|
||||
}
|
||||
|
||||
@discourseComputed("silenced_till")
|
||||
silencedForever: isForever,
|
||||
silencedForever(silencedTill) {
|
||||
isForever(silencedTill);
|
||||
}
|
||||
|
||||
@discourseComputed("suspended_till")
|
||||
suspendedTillDate: longDate,
|
||||
suspendedTillDate(silencedTill) {
|
||||
return longDate(silencedTill);
|
||||
}
|
||||
|
||||
@discourseComputed("silenced_till")
|
||||
silencedTillDate: longDate,
|
||||
|
||||
sidebarCategoryIds: alias("sidebar_category_ids"),
|
||||
silencedTillDate(silencedTill) {
|
||||
return longDate(silencedTill);
|
||||
}
|
||||
|
||||
@discourseComputed("sidebar_tags.[]")
|
||||
sidebarTags(sidebarTags) {
|
||||
|
@ -399,18 +408,14 @@ const User = RestModel.extend({
|
|||
return sidebarTags.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
},
|
||||
|
||||
sidebarSections: alias("sidebar_sections"),
|
||||
|
||||
sidebarTagNames: mapBy("sidebarTags", "name"),
|
||||
}
|
||||
|
||||
changeUsername(new_username) {
|
||||
return ajax(userPath(`${this.username_lower}/preferences/username`), {
|
||||
type: "PUT",
|
||||
data: { new_username },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
addEmail(email) {
|
||||
return ajax(userPath(`${this.username_lower}/preferences/email`), {
|
||||
|
@ -423,7 +428,7 @@ const User = RestModel.extend({
|
|||
|
||||
this.unconfirmed_emails.pushObject(email);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
changeEmail(email) {
|
||||
return ajax(userPath(`${this.username_lower}/preferences/email`), {
|
||||
|
@ -436,7 +441,7 @@ const User = RestModel.extend({
|
|||
|
||||
this.unconfirmed_emails.pushObject(email);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
save(fields) {
|
||||
const data = this.getProperties(
|
||||
|
@ -506,7 +511,7 @@ const User = RestModel.extend({
|
|||
.finally(() => {
|
||||
this.set("isSaving", false);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
setPrimaryEmail(email) {
|
||||
return ajax(userPath(`${this.username}/preferences/primary-email.json`), {
|
||||
|
@ -517,7 +522,7 @@ const User = RestModel.extend({
|
|||
this.secondary_emails.pushObject(this.email);
|
||||
this.set("email", email);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
destroyEmail(email) {
|
||||
return ajax(userPath(`${this.username}/preferences/email.json`), {
|
||||
|
@ -530,7 +535,7 @@ const User = RestModel.extend({
|
|||
this.secondary_emails.removeObject(email);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
changePassword() {
|
||||
return ajax("/session/forgot_password", {
|
||||
|
@ -538,55 +543,55 @@ const User = RestModel.extend({
|
|||
data: { login: this.email || this.username },
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
loadSecondFactorCodes() {
|
||||
return ajax("/u/second_factors.json", {
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
requestSecurityKeyChallenge() {
|
||||
return ajax("/u/create_second_factor_security_key.json", {
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
registerSecurityKey(credential) {
|
||||
return ajax("/u/register_second_factor_security_key.json", {
|
||||
data: credential,
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
trustedSession() {
|
||||
return ajax("/u/trusted-session.json");
|
||||
},
|
||||
}
|
||||
|
||||
createPasskey() {
|
||||
return ajax("/u/create_passkey.json", {
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
registerPasskey(credential) {
|
||||
return ajax("/u/register_passkey.json", {
|
||||
data: credential,
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
deletePasskey(id) {
|
||||
return ajax(`/u/delete_passkey/${id}`, {
|
||||
type: "DELETE",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
createSecondFactorTotp() {
|
||||
return ajax("/u/create_second_factor_totp.json", {
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
enableSecondFactorTotp(authToken, name) {
|
||||
return ajax("/u/enable_second_factor_totp.json", {
|
||||
|
@ -596,13 +601,13 @@ const User = RestModel.extend({
|
|||
},
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
disableAllSecondFactors() {
|
||||
return ajax("/u/disable_second_factor.json", {
|
||||
type: "PUT",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
updateSecondFactor(id, name, disable, targetMethod) {
|
||||
return ajax("/u/second_factor.json", {
|
||||
|
@ -614,7 +619,7 @@ const User = RestModel.extend({
|
|||
},
|
||||
type: "PUT",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
updateSecurityKey(id, name, disable) {
|
||||
return ajax("/u/security_key.json", {
|
||||
|
@ -625,7 +630,7 @@ const User = RestModel.extend({
|
|||
},
|
||||
type: "PUT",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
toggleSecondFactor(authToken, authMethod, targetMethod, enable) {
|
||||
return ajax("/u/second_factor.json", {
|
||||
|
@ -637,20 +642,20 @@ const User = RestModel.extend({
|
|||
},
|
||||
type: "PUT",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
generateSecondFactorCodes() {
|
||||
return ajax("/u/second_factors_backup.json", {
|
||||
type: "PUT",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
revokeAssociatedAccount(providerName) {
|
||||
return ajax(userPath(`${this.username}/preferences/revoke-account`), {
|
||||
data: { provider_name: providerName },
|
||||
type: "POST",
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
async loadUserAction(id) {
|
||||
const result = await ajax(`/user_actions/${id}.json`);
|
||||
|
@ -673,16 +678,14 @@ const User = RestModel.extend({
|
|||
const action = UserAction.collapseStream([UserAction.create(ua)]);
|
||||
this.stream.set("itemsLoaded", this.stream.get("itemsLoaded") + 1);
|
||||
this.stream.get("content").insertAt(0, action[0]);
|
||||
},
|
||||
}
|
||||
|
||||
inAllStream(ua) {
|
||||
return (
|
||||
ua.action_type === UserAction.TYPES.posts ||
|
||||
ua.action_type === UserAction.TYPES.topics
|
||||
);
|
||||
},
|
||||
|
||||
numGroupsToDisplay: 2,
|
||||
}
|
||||
|
||||
@discourseComputed("groups.[]")
|
||||
filteredGroups() {
|
||||
|
@ -691,15 +694,13 @@ const User = RestModel.extend({
|
|||
return groups.filter((group) => {
|
||||
return !group.automatic || group.name === "moderators";
|
||||
});
|
||||
},
|
||||
|
||||
groupsWithMessages: filterBy("groups", "has_messages", true),
|
||||
}
|
||||
|
||||
@discourseComputed("filteredGroups", "numGroupsToDisplay")
|
||||
displayGroups(filteredGroups, numGroupsToDisplay) {
|
||||
const groups = filteredGroups.slice(0, numGroupsToDisplay);
|
||||
return groups.length === 0 ? null : groups;
|
||||
},
|
||||
}
|
||||
|
||||
// NOTE: This only includes groups *visible* to the user via the serializer,
|
||||
// so be wary when using this.
|
||||
|
@ -713,7 +714,7 @@ const User = RestModel.extend({
|
|||
groupIds.includes(0) ||
|
||||
this.groups.mapBy("id").some((groupId) => groupIds.includes(groupId))
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
// The user's stat count, excluding PMs.
|
||||
@discourseComputed("statsExcludingPms.@each.count")
|
||||
|
@ -728,7 +729,7 @@ const User = RestModel.extend({
|
|||
}
|
||||
});
|
||||
return count;
|
||||
},
|
||||
}
|
||||
|
||||
// The user's stats, excluding PMs.
|
||||
@discourseComputed("stats.@each.isPM")
|
||||
|
@ -737,7 +738,7 @@ const User = RestModel.extend({
|
|||
return [];
|
||||
}
|
||||
return this.stats.rejectBy("isPM");
|
||||
},
|
||||
}
|
||||
|
||||
findDetails(options) {
|
||||
const user = this;
|
||||
|
@ -803,7 +804,7 @@ const User = RestModel.extend({
|
|||
user.setProperties(json.user);
|
||||
return user;
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
findStaffInfo() {
|
||||
if (!User.currentProp("staff")) {
|
||||
|
@ -814,21 +815,21 @@ const User = RestModel.extend({
|
|||
this.setProperties(info);
|
||||
}
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
pickAvatar(upload_id, type) {
|
||||
return ajax(userPath(`${this.username_lower}/preferences/avatar/pick`), {
|
||||
type: "PUT",
|
||||
data: { upload_id, type },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
selectAvatar(avatarUrl) {
|
||||
return ajax(userPath(`${this.username_lower}/preferences/avatar/select`), {
|
||||
type: "PUT",
|
||||
data: { url: avatarUrl },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
isAllowedToUploadAFile(type) {
|
||||
const settingName = type === "image" ? "embedded_media" : "attachments";
|
||||
|
@ -838,21 +839,21 @@ const User = RestModel.extend({
|
|||
this.trust_level > 0 ||
|
||||
this.siteSettings[`newuser_max_${settingName}`] > 0
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
createInvite(email, group_ids, custom_message) {
|
||||
return ajax("/invites", {
|
||||
type: "POST",
|
||||
data: { email, group_ids, custom_message },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
generateInviteLink(email, group_ids, topic_id) {
|
||||
return ajax("/invites", {
|
||||
type: "POST",
|
||||
data: { email, skip_email: true, group_ids, topic_id },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get mutedCategories() {
|
||||
|
@ -866,13 +867,14 @@ const User = RestModel.extend({
|
|||
}
|
||||
|
||||
return Category.findByIds(this.get("muted_category_ids"));
|
||||
},
|
||||
}
|
||||
|
||||
set mutedCategories(categories) {
|
||||
this.set(
|
||||
"muted_category_ids",
|
||||
categories.map((c) => c.id)
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get regularCategories() {
|
||||
|
@ -886,13 +888,14 @@ const User = RestModel.extend({
|
|||
}
|
||||
|
||||
return Category.findByIds(this.get("regular_category_ids"));
|
||||
},
|
||||
}
|
||||
|
||||
set regularCategories(categories) {
|
||||
this.set(
|
||||
"regular_category_ids",
|
||||
categories.map((c) => c.id)
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get trackedCategories() {
|
||||
|
@ -906,13 +909,14 @@ const User = RestModel.extend({
|
|||
}
|
||||
|
||||
return Category.findByIds(this.get("tracked_category_ids"));
|
||||
},
|
||||
}
|
||||
|
||||
set trackedCategories(categories) {
|
||||
this.set(
|
||||
"tracked_category_ids",
|
||||
categories.map((c) => c.id)
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get watchedCategories() {
|
||||
|
@ -926,13 +930,14 @@ const User = RestModel.extend({
|
|||
}
|
||||
|
||||
return Category.findByIds(this.get("watched_category_ids"));
|
||||
},
|
||||
}
|
||||
|
||||
set watchedCategories(categories) {
|
||||
this.set(
|
||||
"watched_category_ids",
|
||||
categories.map((c) => c.id)
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get watchedFirstPostCategories() {
|
||||
|
@ -946,28 +951,29 @@ const User = RestModel.extend({
|
|||
}
|
||||
|
||||
return Category.findByIds(this.get("watched_first_post_category_ids"));
|
||||
},
|
||||
}
|
||||
|
||||
set watchedFirstPostCategories(categories) {
|
||||
this.set(
|
||||
"watched_first_post_category_ids",
|
||||
categories.map((c) => c.id)
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("can_delete_account")
|
||||
canDeleteAccount(canDeleteAccount) {
|
||||
return !this.siteSettings.enable_discourse_connect && canDeleteAccount;
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get sidebarLinkToFilteredList() {
|
||||
return this.get("user_option.sidebar_link_to_filtered_list");
|
||||
},
|
||||
}
|
||||
|
||||
@dependentKeyCompat
|
||||
get sidebarShowCountOfNewItems() {
|
||||
return this.get("user_option.sidebar_show_count_of_new_items");
|
||||
},
|
||||
}
|
||||
|
||||
delete() {
|
||||
if (this.can_delete_account) {
|
||||
|
@ -978,7 +984,7 @@ const User = RestModel.extend({
|
|||
} else {
|
||||
return Promise.reject(I18n.t("user.delete_yourself_not_allowed"));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
updateNotificationLevel({ level, expiringAt = null, actingUser = null }) {
|
||||
if (!actingUser) {
|
||||
|
@ -1001,7 +1007,7 @@ const User = RestModel.extend({
|
|||
actingUser.ignored_users.addObject(this.username);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
dismissBanner(bannerKey) {
|
||||
this.set("dismissed_banner_key", bannerKey);
|
||||
|
@ -1009,7 +1015,7 @@ const User = RestModel.extend({
|
|||
type: "PUT",
|
||||
data: { dismissed_banner_key: bannerKey },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
checkEmail() {
|
||||
return ajax(userPath(`${this.username_lower}/emails.json`), {
|
||||
|
@ -1024,7 +1030,7 @@ const User = RestModel.extend({
|
|||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
summary() {
|
||||
const store = getOwnerWithFallback(this).lookup("service:store");
|
||||
|
@ -1072,13 +1078,13 @@ const User = RestModel.extend({
|
|||
return summary;
|
||||
}
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
canManageGroup(group) {
|
||||
return group.get("automatic")
|
||||
? false
|
||||
: group.get("can_admin_group") || group.get("is_group_owner");
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("groups.@each.title", "badges.[]")
|
||||
availableTitles() {
|
||||
|
@ -1105,7 +1111,7 @@ const User = RestModel.extend({
|
|||
id: title,
|
||||
};
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("groups.[]")
|
||||
availableFlairs() {
|
||||
|
@ -1126,7 +1132,7 @@ const User = RestModel.extend({
|
|||
}
|
||||
|
||||
return flairs;
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("user_option.text_size_seq", "user_option.text_size")
|
||||
currentTextSize(serverSeq, serverSize) {
|
||||
|
@ -1137,7 +1143,7 @@ const User = RestModel.extend({
|
|||
}
|
||||
}
|
||||
return serverSize;
|
||||
},
|
||||
}
|
||||
|
||||
updateTextSizeCookie(newSize) {
|
||||
if (newSize) {
|
||||
|
@ -1149,7 +1155,7 @@ const User = RestModel.extend({
|
|||
} else {
|
||||
removeCookie(TEXT_SIZE_COOKIE_NAME, { path: "/" });
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed("second_factor_enabled", "staff")
|
||||
enforcedSecondFactor(secondFactorEnabled, staff) {
|
||||
|
@ -1158,7 +1164,7 @@ const User = RestModel.extend({
|
|||
!secondFactorEnabled &&
|
||||
(enforce === "all" || (enforce === "staff" && staff))
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
resolvedTimezone() {
|
||||
deprecated(
|
||||
|
@ -1171,7 +1177,7 @@ const User = RestModel.extend({
|
|||
);
|
||||
|
||||
return this.user_option.timezone;
|
||||
},
|
||||
}
|
||||
|
||||
calculateMutedIds(notificationLevel, id, type) {
|
||||
const muted_ids = this.get(type);
|
||||
|
@ -1180,14 +1186,14 @@ const User = RestModel.extend({
|
|||
} else {
|
||||
return muted_ids.filter((existing_id) => existing_id !== id);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
setPrimaryGroup(primaryGroupId) {
|
||||
return ajax(`/admin/users/${this.id}/primary_group`, {
|
||||
type: "PUT",
|
||||
data: { primary_group_id: primaryGroupId },
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
enterDoNotDisturbFor(duration) {
|
||||
return ajax({
|
||||
|
@ -1197,7 +1203,7 @@ const User = RestModel.extend({
|
|||
}).then((response) => {
|
||||
return this.updateDoNotDisturbStatus(response.ends_at);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
leaveDoNotDisturb() {
|
||||
return ajax({
|
||||
|
@ -1206,29 +1212,29 @@ const User = RestModel.extend({
|
|||
}).then(() => {
|
||||
this.updateDoNotDisturbStatus(null);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
updateDoNotDisturbStatus(ends_at) {
|
||||
this.set("do_not_disturb_until", ends_at);
|
||||
this.appEvents.trigger("do-not-disturb:changed", this.do_not_disturb_until);
|
||||
},
|
||||
}
|
||||
|
||||
updateDraftProperties(properties) {
|
||||
this.setProperties(properties);
|
||||
this.appEvents.trigger("user-drafts:changed");
|
||||
},
|
||||
}
|
||||
|
||||
updateReviewableCount(count) {
|
||||
this.set("reviewable_count", count);
|
||||
this.appEvents.trigger("user-reviewable-count:changed", count);
|
||||
},
|
||||
}
|
||||
|
||||
isInDoNotDisturb() {
|
||||
return (
|
||||
this.do_not_disturb_until &&
|
||||
new Date(this.do_not_disturb_until) >= new Date()
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@discourseComputed(
|
||||
"tracked_tags.[]",
|
||||
|
@ -1237,8 +1243,8 @@ const User = RestModel.extend({
|
|||
)
|
||||
trackedTags(trackedTags, watchedTags, watchingFirstPostTags) {
|
||||
return [...trackedTags, ...watchedTags, ...watchingFirstPostTags];
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
User.reopenClass(Singleton, {
|
||||
// Find a `User` for a given username.
|
||||
|
@ -1269,7 +1275,7 @@ User.reopenClass(Singleton, {
|
|||
|
||||
const store = getOwnerWithFallback(this).lookup("service:store");
|
||||
const currentUser = store.createRecord("user", userJson);
|
||||
currentUser.trackStatus();
|
||||
currentUser.statusManager.trackStatus();
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
|
@ -1379,13 +1385,21 @@ User.reopenClass(Singleton, {
|
|||
});
|
||||
|
||||
// user status tracking
|
||||
User.reopen(Evented, {
|
||||
_subscribersCount: 0,
|
||||
_clearStatusTimerId: null,
|
||||
class UserStatusManager {
|
||||
@service appEvents;
|
||||
|
||||
user;
|
||||
_subscribersCount = 0;
|
||||
_clearStatusTimerId = null;
|
||||
|
||||
constructor(user) {
|
||||
this.user = user;
|
||||
setOwner(this, getOwner(user));
|
||||
}
|
||||
|
||||
// always call stopTrackingStatus() when done with a user
|
||||
trackStatus() {
|
||||
if (!this.id && !isTesting()) {
|
||||
if (!this.user.id && !isTesting()) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
"It's impossible to track user status on a user model that doesn't have id. This user model won't be receiving live user status updates."
|
||||
|
@ -1393,17 +1407,17 @@ User.reopen(Evented, {
|
|||
}
|
||||
|
||||
if (this._subscribersCount === 0) {
|
||||
this.addObserver("status", this, "_statusChanged");
|
||||
this.user.addObserver("status", this, "_statusChanged");
|
||||
|
||||
this.appEvents.on("user-status:changed", this, this._updateStatus);
|
||||
|
||||
if (this.status && this.status.ends_at) {
|
||||
this._scheduleStatusClearing(this.status.ends_at);
|
||||
if (this.user.status?.ends_at) {
|
||||
this._scheduleStatusClearing(this.user.status.ends_at);
|
||||
}
|
||||
}
|
||||
|
||||
this._subscribersCount++;
|
||||
},
|
||||
}
|
||||
|
||||
stopTrackingStatus() {
|
||||
if (this._subscribersCount === 0) {
|
||||
|
@ -1412,28 +1426,28 @@ User.reopen(Evented, {
|
|||
|
||||
if (this._subscribersCount === 1) {
|
||||
// the last subscriber is unsubscribing
|
||||
this.removeObserver("status", this, "_statusChanged");
|
||||
this.user.removeObserver("status", this, "_statusChanged");
|
||||
this.appEvents.off("user-status:changed", this, this._updateStatus);
|
||||
this._unscheduleStatusClearing();
|
||||
}
|
||||
|
||||
this._subscribersCount--;
|
||||
},
|
||||
}
|
||||
|
||||
isTrackingStatus() {
|
||||
return this._subscribersCount > 0;
|
||||
},
|
||||
}
|
||||
|
||||
_statusChanged(sender, key) {
|
||||
this.trigger("status-changed");
|
||||
_statusChanged() {
|
||||
this.user.trigger("status-changed");
|
||||
|
||||
const status = this.get(key);
|
||||
const status = this.user.status;
|
||||
if (status && status.ends_at) {
|
||||
this._scheduleStatusClearing(status.ends_at);
|
||||
} else {
|
||||
this._unscheduleStatusClearing();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_scheduleStatusClearing(endsAt) {
|
||||
if (isTesting()) {
|
||||
|
@ -1451,23 +1465,23 @@ User.reopen(Evented, {
|
|||
"_autoClearStatus",
|
||||
remaining
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
_unscheduleStatusClearing() {
|
||||
cancel(this._clearStatusTimerId);
|
||||
this._clearStatusTimerId = null;
|
||||
},
|
||||
}
|
||||
|
||||
_autoClearStatus() {
|
||||
this.set("status", null);
|
||||
},
|
||||
this.user.set("status", null);
|
||||
}
|
||||
|
||||
_updateStatus(statuses) {
|
||||
if (statuses.hasOwnProperty(this.id)) {
|
||||
this.set("status", statuses[this.id]);
|
||||
if (statuses.hasOwnProperty(this.user.id)) {
|
||||
this.user.set("status", statuses[this.user.id]);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof Discourse !== "undefined") {
|
||||
let warned = false;
|
||||
|
@ -1485,5 +1499,3 @@ if (typeof Discourse !== "undefined") {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default User;
|
||||
|
|
|
@ -36,7 +36,7 @@ export default DiscourseRoute.extend({
|
|||
return user
|
||||
.findDetails()
|
||||
.then(() => user.findStaffInfo())
|
||||
.then(() => user.trackStatus())
|
||||
.then(() => user.statusManager.trackStatus())
|
||||
.catch(() => this.router.replaceWith("/404"));
|
||||
},
|
||||
|
||||
|
@ -76,7 +76,7 @@ export default DiscourseRoute.extend({
|
|||
`/u/${user.username_lower}/counters`,
|
||||
this.onUserCountersMessage
|
||||
);
|
||||
user.stopTrackingStatus();
|
||||
user.statusManager.stopTrackingStatus();
|
||||
|
||||
// Remove the search context
|
||||
this.searchService.searchContext = null;
|
||||
|
|
|
@ -407,14 +407,14 @@ export default class PostCooked {
|
|||
|
||||
_trackMentionedUsersStatus() {
|
||||
this._post()?.mentioned_users?.forEach((user) => {
|
||||
user.trackStatus?.();
|
||||
user.statusManager?.trackStatus?.();
|
||||
user.on?.("status-changed", this, "_rerenderUserStatusOnMentions");
|
||||
});
|
||||
}
|
||||
|
||||
_stopTrackingMentionedUsersStatus() {
|
||||
this._post()?.mentioned_users?.forEach((user) => {
|
||||
user.stopTrackingStatus?.();
|
||||
user.statusManager?.stopTrackingStatus?.();
|
||||
user.off?.("status-changed", this, "_rerenderUserStatusOnMentions");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ export default createWidget("poster-name", {
|
|||
|
||||
didRenderWidget() {
|
||||
if (this.attrs.user) {
|
||||
this.attrs.user.trackStatus();
|
||||
this.attrs.user.statusManager.trackStatus();
|
||||
this.attrs.user.on("status-changed", this, "scheduleRerender");
|
||||
}
|
||||
},
|
||||
|
@ -69,7 +69,7 @@ export default createWidget("poster-name", {
|
|||
willRerenderWidget() {
|
||||
if (this.attrs.user) {
|
||||
this.attrs.user.off("status-changed", this, "scheduleRerender");
|
||||
this.attrs.user.stopTrackingStatus();
|
||||
this.attrs.user.statusManager.stopTrackingStatus();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -344,7 +344,7 @@ export function acceptance(name, optionsOrCallback) {
|
|||
updateCurrentUser(userChanges);
|
||||
}
|
||||
|
||||
User.current().trackStatus();
|
||||
User.current().statusManager.trackStatus();
|
||||
}
|
||||
|
||||
if (settingChanges) {
|
||||
|
@ -372,7 +372,7 @@ export function acceptance(name, optionsOrCallback) {
|
|||
let app = getApplication();
|
||||
options?.afterEach?.call(this);
|
||||
if (loggedIn) {
|
||||
User.current().stopTrackingStatus();
|
||||
User.current().statusManager.stopTrackingStatus();
|
||||
}
|
||||
testCleanup(this.container, app);
|
||||
|
||||
|
|
|
@ -120,25 +120,25 @@ module("Unit | Model | user", function (hooks) {
|
|||
test("subsequent calls to trackStatus and stopTrackingStatus increase and decrease subscribers counter", function (assert) {
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const user = store.createRecord("user");
|
||||
assert.strictEqual(user._subscribersCount, 0);
|
||||
assert.strictEqual(user.statusManager._subscribersCount, 0);
|
||||
|
||||
user.trackStatus();
|
||||
assert.strictEqual(user._subscribersCount, 1);
|
||||
user.statusManager.trackStatus();
|
||||
assert.strictEqual(user.statusManager._subscribersCount, 1);
|
||||
|
||||
user.trackStatus();
|
||||
assert.strictEqual(user._subscribersCount, 2);
|
||||
user.statusManager.trackStatus();
|
||||
assert.strictEqual(user.statusManager._subscribersCount, 2);
|
||||
|
||||
user.stopTrackingStatus();
|
||||
assert.strictEqual(user._subscribersCount, 1);
|
||||
user.statusManager.stopTrackingStatus();
|
||||
assert.strictEqual(user.statusManager._subscribersCount, 1);
|
||||
|
||||
user.stopTrackingStatus();
|
||||
assert.strictEqual(user._subscribersCount, 0);
|
||||
user.statusManager.stopTrackingStatus();
|
||||
assert.strictEqual(user.statusManager._subscribersCount, 0);
|
||||
});
|
||||
|
||||
test("attempt to stop tracking status if status wasn't tracked doesn't throw", function (assert) {
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const user = store.createRecord("user");
|
||||
user.stopTrackingStatus();
|
||||
user.statusManager.stopTrackingStatus();
|
||||
assert.ok(true);
|
||||
});
|
||||
|
||||
|
@ -160,8 +160,8 @@ module("Unit | Model | user", function (hooks) {
|
|||
const appEvents = user1.appEvents;
|
||||
|
||||
try {
|
||||
user1.trackStatus();
|
||||
user2.trackStatus();
|
||||
user1.statusManager.trackStatus();
|
||||
user2.statusManager.trackStatus();
|
||||
assert.strictEqual(user1.status, status1);
|
||||
assert.strictEqual(user2.status, status2);
|
||||
|
||||
|
@ -173,8 +173,8 @@ module("Unit | Model | user", function (hooks) {
|
|||
assert.strictEqual(user1.status, null);
|
||||
assert.strictEqual(user2.status, null);
|
||||
} finally {
|
||||
user1.stopTrackingStatus();
|
||||
user2.stopTrackingStatus();
|
||||
user1.statusManager.stopTrackingStatus();
|
||||
user2.statusManager.stopTrackingStatus();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -158,12 +158,12 @@ export default class ChatChannelRow extends Component {
|
|||
|
||||
@action
|
||||
startTrackingStatus() {
|
||||
this.#firstDirectMessageUser?.trackStatus();
|
||||
this.#firstDirectMessageUser?.statusManager.trackStatus();
|
||||
}
|
||||
|
||||
@action
|
||||
stopTrackingStatus() {
|
||||
this.#firstDirectMessageUser?.stopTrackingStatus();
|
||||
this.#firstDirectMessageUser?.statusManager.stopTrackingStatus();
|
||||
}
|
||||
|
||||
<template>
|
||||
|
|
|
@ -249,11 +249,11 @@ export default class ChatMessage extends Component {
|
|||
@action
|
||||
initMentionedUsers() {
|
||||
this.args.message.mentionedUsers.forEach((user) => {
|
||||
if (user.isTrackingStatus()) {
|
||||
if (user.statusManager.isTrackingStatus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
user.trackStatus();
|
||||
user.statusManager.trackStatus();
|
||||
user.on("status-changed", this, "refreshStatusOnMentions");
|
||||
});
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ export default class ChatMessage extends Component {
|
|||
|
||||
#teardownMentionedUsers() {
|
||||
this.args.message.mentionedUsers.forEach((user) => {
|
||||
user.stopTrackingStatus();
|
||||
user.statusManager.stopTrackingStatus();
|
||||
user.off("status-changed", this, "refreshStatusOnMentions");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ export default class ChatMessageInfo extends Component {
|
|||
|
||||
@bind
|
||||
trackStatus() {
|
||||
this.#user?.trackStatus?.();
|
||||
this.#user?.statusManager.trackStatus();
|
||||
}
|
||||
|
||||
@bind
|
||||
stopTrackingStatus() {
|
||||
this.#user?.stopTrackingStatus?.();
|
||||
this.#user?.statusManager.stopTrackingStatus();
|
||||
}
|
||||
|
||||
get usernameClasses() {
|
||||
|
|
|
@ -292,7 +292,7 @@ export default {
|
|||
if (this.oneOnOneMessage) {
|
||||
const user = this.channel.chatable.users[0];
|
||||
if (user.username !== I18n.t("chat.deleted_chat_username")) {
|
||||
user.trackStatus();
|
||||
user.statusManager.trackStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ export default {
|
|||
@bind
|
||||
willDestroy() {
|
||||
if (this.oneOnOneMessage) {
|
||||
this.channel.chatable.users[0].stopTrackingStatus();
|
||||
this.channel.chatable.users[0].statusManager.stopTrackingStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue