DEV: Bump `@discourse/lint-configs` and autofix (#29847)

In particular, this applies:

- new `discourse/no-implicit-this` template-lint rule
- `init`/`willDestroy` ordering enforcement
- `lines-between-class-members`
This commit is contained in:
David Taylor 2024-11-20 14:15:04 +00:00 committed by GitHub
parent 581fb97bfa
commit 6f7c581a80
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 304 additions and 263 deletions

View File

@ -88,6 +88,7 @@ export default class AdminFlagItem extends Component {
this.dMenu.close();
});
}
@action
edit() {
this.router.transitionTo("adminConfig.flags.edit", this.args.flag);

View File

@ -44,10 +44,12 @@ export default class EmbeddableHost extends Component.extend(
edit() {
this.set("editToggled", true);
}
@action
onUserChange(user) {
this.set("user", user);
}
@action
save() {
if (this.cantSave) {

View File

@ -46,6 +46,11 @@ export default class InstallThemeModal extends Component {
keyGenUrl = this.args.model.keyGenUrl || "/admin/themes/generate_key_pair";
importUrl = this.args.model.importUrl || "/admin/themes/import";
willDestroy() {
super.willDestroy(...arguments);
this.args.model.clearParams?.();
}
get showPublicKey() {
return this.uploadUrl?.match?.(/^ssh:\/\/.+@.+$|.+@.+:.+$/);
}
@ -125,11 +130,6 @@ export default class InstallThemeModal extends Component {
);
}
willDestroy() {
super.willDestroy(...arguments);
this.args.model.clearParams?.();
}
@action
async generatePublicKey() {
try {

View File

@ -96,6 +96,7 @@ export default class FileTypesList extends Component {
this.args.changeValueCallback(newTypes.join(TOKEN_SEPARATOR));
}
<template>
<ListSetting
@value={{this.settingValue}}

View File

@ -166,6 +166,7 @@ export default class ThemesList extends Component {
results = this._applyFilter(results);
return this._searchThemes(results, this.searchTerm);
}
@discourseComputed("themesList.@each.markedToDelete")
someInactiveSelected() {
return (

View File

@ -26,6 +26,7 @@ export default class AdminUserBadgesController extends Controller {
availableBadges() {
return grantableBadges(this.get("allBadges"), this.get("userBadges"));
}
@discourseComputed("model", "model.[]", "model.expandedBadges.[]")
groupedBadges() {
const allBadges = this.model;
@ -71,6 +72,7 @@ export default class AdminUserBadgesController extends Controller {
return expanded.sortBy("granted_at").reverse();
}
@action
expandGroup(userBadge) {
const model = this.model;

View File

@ -27,6 +27,11 @@ export default class DialogService extends Service {
class = null;
_confirming = false;
willDestroy() {
this.dialogInstance?.destroy();
this.reset();
}
async dialog(params) {
const {
message,
@ -171,11 +176,6 @@ export default class DialogService extends Service {
});
}
willDestroy() {
this.dialogInstance?.destroy();
this.reset();
}
@bind
didConfirmWrapped() {
if (this.didConfirm) {

View File

@ -9,6 +9,14 @@ export default class BasicTopicList extends Component {
@not("loaded") loading;
init() {
super.init(...arguments);
const topicList = this.topicList;
if (topicList) {
this._initFromTopicList(topicList);
}
}
@discourseComputed("topicList.loaded")
loaded() {
let topicList = this.topicList;
@ -31,14 +39,6 @@ export default class BasicTopicList extends Component {
}
}
init() {
super.init(...arguments);
const topicList = this.topicList;
if (topicList) {
this._initFromTopicList(topicList);
}
}
didInsertElement() {
super.didInsertElement(...arguments);

View File

@ -76,6 +76,12 @@ export default class DEditor extends Component {
},
};
init() {
super.init(...arguments);
this.register = getRegister(this);
}
@computed("formTemplateIds")
get selectedFormTemplateId() {
if (this._selectedFormTemplateId) {
@ -116,12 +122,6 @@ export default class DEditor extends Component {
}
}
init() {
super.init(...arguments);
this.register = getRegister(this);
}
didInsertElement() {
super.didInsertElement(...arguments);

View File

@ -48,6 +48,11 @@ export default class DLightbox extends Component {
animationDuration = ANIMATION_DURATION;
scrollPosition = 0;
willDestroy() {
super.willDestroy(...arguments);
this.cleanup();
}
get layoutType() {
return window.innerWidth > window.innerHeight
? LAYOUT_TYPES.HORIZONTAL
@ -487,9 +492,4 @@ export default class DLightbox extends Component {
});
}
}
willDestroy() {
super.willDestroy(...arguments);
this.cleanup();
}
}

View File

@ -26,6 +26,20 @@ export default class DiscourseTopic extends Component.extend(Scrolling) {
SHORT_POST = 1200;
dockAt = 0;
init() {
super.init(...arguments);
this.appEvents.on("discourse:focus-changed", this, "gotFocus");
this.appEvents.on("post:highlight", this, "_highlightPost");
}
willDestroy() {
super.willDestroy(...arguments);
// this happens after route exit, stuff could have trickled in
this.appEvents.off("discourse:focus-changed", this, "gotFocus");
this.appEvents.off("post:highlight", this, "_highlightPost");
}
@observes("enteredAt")
_enteredTopic() {
// Ember is supposed to only call observers when values change but something
@ -43,12 +57,6 @@ export default class DiscourseTopic extends Component.extend(Scrolling) {
}
}
init() {
super.init(...arguments);
this.appEvents.on("discourse:focus-changed", this, "gotFocus");
this.appEvents.on("post:highlight", this, "_highlightPost");
}
didInsertElement() {
super.didInsertElement(...arguments);
@ -61,14 +69,6 @@ export default class DiscourseTopic extends Component.extend(Scrolling) {
);
}
willDestroy() {
super.willDestroy(...arguments);
// this happens after route exit, stuff could have trickled in
this.appEvents.off("discourse:focus-changed", this, "gotFocus");
this.appEvents.off("post:highlight", this, "_highlightPost");
}
willDestroyElement() {
super.willDestroyElement(...arguments);

View File

@ -49,6 +49,25 @@ export default class GlimmerSiteHeader extends Component {
schedule("afterRender", () => this.animateMenu());
}
willDestroy() {
super.willDestroy(...arguments);
this.appEvents.off("user-menu:rendered", this, this.animateMenu);
if (this.dropDownHeaderEnabled) {
this.appEvents.off(
"sidebar-hamburger-dropdown:rendered",
this,
this.animateMenu
);
}
this._itsatrap?.destroy();
this._itsatrap = null;
window.removeEventListener("scroll", this._recalculateHeaderOffset);
this._resizeObserver?.disconnect();
}
get dropDownHeaderEnabled() {
return !this.sidebarEnabled || this.site.narrowDesktopView;
}
@ -361,25 +380,6 @@ export default class GlimmerSiteHeader extends Component {
);
}
willDestroy() {
super.willDestroy(...arguments);
this.appEvents.off("user-menu:rendered", this, this.animateMenu);
if (this.dropDownHeaderEnabled) {
this.appEvents.off(
"sidebar-hamburger-dropdown:rendered",
this,
this.animateMenu
);
}
this._itsatrap?.destroy();
this._itsatrap = null;
window.removeEventListener("scroll", this._recalculateHeaderOffset);
this._resizeObserver?.disconnect();
}
<template>
<div
class={{concatClass

View File

@ -53,6 +53,13 @@ export default class BookmarkModal extends Component {
_itsatrap = new ItsATrap();
willDestroy() {
super.willDestroy(...arguments);
this._itsatrap?.destroy();
this._itsatrap = null;
KeyboardShortcuts.unpause();
}
get bookmark() {
return this.args.model.bookmark;
}
@ -127,13 +134,6 @@ export default class BookmarkModal extends Component {
return labels;
}
willDestroy() {
super.willDestroy(...arguments);
this._itsatrap?.destroy();
this._itsatrap = null;
KeyboardShortcuts.unpause();
}
@action
didInsert() {
discourseLater(() => {

View File

@ -26,6 +26,7 @@ export default class ChangeOwnerModal extends Component {
get selectedPostsCount() {
return this.args.model.selectedPostsCount;
}
@action
async changeOwnershipOfPosts() {
this.saving = true;

View File

@ -141,9 +141,11 @@ export default class PluginOutletComponent extends GlimmerComponentWithDeprecate
});
return this.#parentView;
}
set parentView(value) {
this.#parentView = value;
}
get _parentView() {
return this.parentView;
}
@ -166,9 +168,11 @@ class PluginOutletWithTagNameWrapper extends ClassicComponent {
return this.#parentView.parentView;
}
}
set parentView(value) {
this.#parentView = value;
}
get _parentView() {
return this.parentView;
}

View File

@ -47,6 +47,14 @@ export default class SearchMenu extends Component {
_debouncer = null;
_activeSearch = null;
willDestroy() {
if (!this.args.inlineResults) {
document.removeEventListener("mousedown", this.onDocumentPress);
document.removeEventListener("touchend", this.onDocumentPress);
}
super.willDestroy(...arguments);
}
@bind
setupEventListeners() {
// We only need to register click events when the search menu is rendered outside of the header.
@ -57,14 +65,6 @@ export default class SearchMenu extends Component {
}
}
willDestroy() {
if (!this.args.inlineResults) {
document.removeEventListener("mousedown", this.onDocumentPress);
document.removeEventListener("touchend", this.onDocumentPress);
}
super.willDestroy(...arguments);
}
@bind
onDocumentPress(event) {
if (!this.menuPanelOpen) {

View File

@ -17,6 +17,13 @@ export default class Sidebar extends Component {
}
}
willDestroy() {
super.willDestroy(...arguments);
if (this.site.mobileView) {
document.removeEventListener("click", this.collapseSidebar);
}
}
get showSwitchPanelButtonsOnTop() {
return this.siteSettings.default_sidebar_switch_panel_position === "top";
}
@ -55,11 +62,4 @@ export default class Sidebar extends Component {
this.args.toggleSidebar();
}
}
willDestroy() {
super.willDestroy(...arguments);
if (this.site.mobileView) {
document.removeEventListener("click", this.collapseSidebar);
}
}
}

View File

@ -44,6 +44,7 @@ export default class SectionFormLink extends Component {
}
}
}
@action
dragEnter() {
this.dragCount++;

View File

@ -205,6 +205,7 @@ class PmRemoveLink extends Component {
didConfirm: () => this.args.removeAllowedUser(this.args.model),
});
}
<template>
<DButton
class="remove-invited"

View File

@ -101,6 +101,7 @@ export default class extends Controller {
)
.filter((t) => t);
}
@computed(
"model.watchedCategories",
"model.mutedCategories",

View File

@ -12,6 +12,17 @@ export default class UsersController extends Controller {
)
allowPmUsersEnabled;
init() {
super.init(...arguments);
this.saveAttrNames = [
"allow_private_messages",
"muted_usernames",
"allowed_pm_usernames",
"enable_allowed_pm_users",
];
}
@computed("model.muted_usernames")
get mutedUsernames() {
let usernames = this.model.muted_usernames;
@ -34,17 +45,6 @@ export default class UsersController extends Controller {
return makeArray(usernames).uniq();
}
init() {
super.init(...arguments);
this.saveAttrNames = [
"allow_private_messages",
"muted_usernames",
"allowed_pm_usernames",
"enable_allowed_pm_users",
];
}
@action
onChangeMutedUsernames(usernames) {
this.model.set("muted_usernames", usernames.uniq().join(","));

View File

@ -2273,6 +2273,7 @@ class PluginApi {
addSaveableUserField(fieldName) {
addSaveableUserField(fieldName);
}
addSaveableUserOptionField(fieldName) {
addSaveableUserOptionField(fieldName);
}

View File

@ -7,6 +7,7 @@ export default class UserMenuBaseItem {
this.site = site;
this.siteSettings = siteSettings;
}
get className() {}
get linkHref() {

View File

@ -81,13 +81,6 @@ export default class Site extends RestModel.extend().reopenClass(Singleton) {
@sort("categories", "topicCountDesc") categoriesByCount;
@computed("categories.[]")
get categoriesById() {
const map = new Map();
this.categories.forEach((c) => map.set(c.id, c));
return map;
}
init() {
super.init(...arguments);
@ -95,6 +88,13 @@ export default class Site extends RestModel.extend().reopenClass(Singleton) {
this.categories = this.categories || [];
}
@computed("categories.[]")
get categoriesById() {
const map = new Map();
this.categories.forEach((c) => map.set(c.id, c));
return map;
}
@discourseComputed("notification_types")
notificationLookup(notificationTypes) {
const result = [];

View File

@ -26,10 +26,6 @@ export default class ClientErrorHandlerService extends Service {
document.addEventListener("discourse-error", this.handleDiscourseError);
}
get rootElement() {
return document.querySelector(getOwner(this).rootElement);
}
willDestroy() {
document.removeEventListener("discourse-error", this.handleDiscourseError);
this.rootElement
@ -37,6 +33,10 @@ export default class ClientErrorHandlerService extends Service {
.forEach((e) => e.remove());
}
get rootElement() {
return document.querySelector(getOwner(this).rootElement);
}
@bind
handleDiscourseError(e) {
if (e.detail?.themeId) {

View File

@ -60,6 +60,10 @@ export default class LightboxService extends Service {
);
}
willDestroy() {
this.#reset();
}
@bind
async onLightboxOpened({ items, currentItem }) {
this.originalSiteThemeColor = await getSiteThemeColor();
@ -268,8 +272,4 @@ export default class LightboxService extends Service {
event.target.toggleAttribute(SELECTORS.DOCUMENT_LAST_FOCUSED_ELEMENT);
}
willDestroy() {
this.#reset();
}
}

View File

@ -16,6 +16,10 @@ export default class NotificationsService extends Service {
this._checkDoNotDisturb();
}
willDestroy() {
clearTimeout(this.#dndTimer);
}
_checkDoNotDisturb() {
clearTimeout(this.#dndTimer);
@ -38,8 +42,4 @@ export default class NotificationsService extends Service {
this.isInDoNotDisturb = false;
}
}
willDestroy() {
clearTimeout(this.#dndTimer);
}
}

View File

@ -277,10 +277,6 @@ export default class PresenceService extends Service {
}
}
get _presentChannels() {
return new Set(this._presentProxies.keys());
}
willDestroy() {
super.willDestroy(...arguments);
window.removeEventListener("beforeunload", this._beaconLeaveAll);
@ -288,6 +284,10 @@ export default class PresenceService extends Service {
cancel(this._debounceTimer);
}
get _presentChannels() {
return new Set(this._presentProxies.keys());
}
// Get a PresenceChannel object representing a single channel
getChannel(channelName) {
return PresenceChannel.create({

View File

@ -24,6 +24,17 @@ export default class RouteScrollManager extends Service {
? document.getElementById("ember-testing-container")
: document.scrollingElement;
init() {
super.init(...arguments);
this.router.on("routeDidChange", this.routeDidChange);
this.router.on("routeWillChange", this.routeWillChange);
}
willDestroy() {
this.router.off("routeDidChange", this.routeDidChange);
this.router.off("routeWillChange", this.routeWillChange);
}
@bind
routeWillChange() {
this.historyStore.set(STORE_KEY, [
@ -63,15 +74,4 @@ export default class RouteScrollManager extends Service {
// No overrides - default to true
return true;
}
init() {
super.init(...arguments);
this.router.on("routeDidChange", this.routeDidChange);
this.router.on("routeWillChange", this.routeWillChange);
}
willDestroy() {
this.router.off("routeDidChange", this.routeDidChange);
this.router.off("routeWillChange", this.routeWillChange);
}
}

View File

@ -52,15 +52,6 @@ export default class PostCooked {
: null;
}
update(prev) {
if (
prev.attrs.cooked !== this.attrs.cooked ||
prev.attrs.highlightTerm !== this.attrs.highlightTerm
) {
return this.init();
}
}
init() {
this.originalQuoteContents = null;
// todo should be a better way of detecting if it is composer preview
@ -77,6 +68,15 @@ export default class PostCooked {
return this.cookedDiv;
}
update(prev) {
if (
prev.attrs.cooked !== this.attrs.cooked ||
prev.attrs.highlightTerm !== this.attrs.highlightTerm
) {
return this.init();
}
}
destroy() {
this._stopTrackingMentionedUsersStatus();
destroyUserStatusOnMentions();

View File

@ -183,6 +183,7 @@ export default class Widget {
}
}
init() {}
transform() {
return {};
}
@ -191,8 +192,6 @@ export default class Widget {
return {};
}
init() {}
destroy() {}
get(propertyPath) {

View File

@ -8,6 +8,7 @@ class BroccoliNoOp extends Plugin {
constructor(path) {
super([new WatchedDir(path)]);
}
build() {}
}

View File

@ -50,8 +50,6 @@ acceptance("Sidebar - Plugin API", function (needs) {
},
];
willDestroy = () => (sectionDestroy = "section test");
links = [
new (class extends BaseCustomSidebarSectionLink {
name = "random-channel";
@ -99,6 +97,7 @@ acceptance("Sidebar - Plugin API", function (needs) {
text = "Homepage";
})(),
];
willDestroy = () => (sectionDestroy = "section test");
};
}
);

View File

@ -14,6 +14,7 @@ acceptance("Acceptance | decorateCookedElement", function () {
DemoComponent.eventLog.push("created");
super(...arguments);
}
willDestroy() {
super.willDestroy(...arguments);
DemoComponent.eventLog.push("willDestroy");

View File

@ -47,6 +47,7 @@ class DemoWidget extends Widget {
),
];
}
dummyAction() {}
@bind
@ -67,6 +68,11 @@ class DemoComponent extends ClassicComponent {
super.init(...arguments);
}
willDestroy() {
super.willDestroy(...arguments);
DemoComponent.eventLog.push("willDestroy");
}
didInsertElement() {
super.didInsertElement(...arguments);
DemoComponent.eventLog.push("didInsertElement");
@ -81,11 +87,6 @@ class DemoComponent extends ClassicComponent {
super.didReceiveAttrs(...arguments);
DemoComponent.eventLog.push("didReceiveAttrs");
}
willDestroy() {
super.willDestroy(...arguments);
DemoComponent.eventLog.push("willDestroy");
}
}
class ToggleDemoWidget extends Widget {

View File

@ -123,9 +123,11 @@ module("Unit | Utility | plugin-api", function (hooks) {
static someStaticMethod() {
return "original static method";
}
someFunction() {
return "original function";
}
get someGetter() {
return "original getter";
}

View File

@ -253,6 +253,7 @@ export default class CategoryRow extends Component {
description.length > limit ? "&hellip;" : ""
}`;
}
_isValidInput(eventKey) {
// relying on passing the event to the input is risky as it could not work
// dispatching the event won't work as the event won't be trusted

View File

@ -23,6 +23,16 @@ export default class TagChooser extends MultiSelectComponent.extend(TagsMixin) {
excludeSynonyms = false;
excludeHasSynonyms = false;
init() {
super.init(...arguments);
this.setProperties({
blockedTags: this.blockedTags || [],
termMatchesForbidden: false,
termMatchErrorMessage: null,
});
}
modifyComponentForRow(collection, item) {
if (this.getValue(item) === this.selectKit.filter && !item.count) {
return "select-kit/select-kit-row";
@ -50,16 +60,6 @@ export default class TagChooser extends MultiSelectComponent.extend(TagsMixin) {
return null;
}
init() {
super.init(...arguments);
this.setProperties({
blockedTags: this.blockedTags || [],
termMatchesForbidden: false,
termMatchErrorMessage: null,
});
}
@computed("tags.[]")
get value() {
return makeArray(this.tags).uniq();

View File

@ -40,6 +40,12 @@ export default class TagDrop extends ComboBoxComponent.extend(TagsMixin) {
@readOnly("tagId") value;
init() {
super.init(...arguments);
this.insertAfterCollection(MAIN_COLLECTION, MORE_TAGS_COLLECTION);
}
@computed("maxTagsInFilterList", "topTags.[]", "mainCollection.[]")
get shouldShowMoreTags() {
if (this.selectKit.filter?.length > 0) {
@ -49,12 +55,6 @@ export default class TagDrop extends ComboBoxComponent.extend(TagsMixin) {
}
}
init() {
super.init(...arguments);
this.insertAfterCollection(MAIN_COLLECTION, MORE_TAGS_COLLECTION);
}
modifyComponentForCollection(collection) {
if (collection === MORE_TAGS_COLLECTION) {
return FilterForMore;

View File

@ -6,7 +6,7 @@
"license": "GPL-2.0-only",
"devDependencies": {
"@babel/plugin-proposal-decorators": "^7.25.9",
"@discourse/lint-configs": "^2.1.0",
"@discourse/lint-configs": "^2.2.0",
"@discourse/moment-timezone-names-translations": "^1.0.0",
"@fortawesome/fontawesome-free": "6.6.0",
"@glint/core": "^1.5.0",
@ -45,8 +45,8 @@
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
"lint:js": "eslint ./app/assets/javascripts $(script/list_bundled_plugins) --cache --no-error-on-unmatched-pattern",
"lint:js:fix": "eslint --fix ./app/assets/javascripts $(script/list_bundled_plugins) --no-error-on-unmatched-pattern",
"lint:hbs": "ember-template-lint 'app/assets/javascripts/**/*.{gjs,hbs}' 'plugins/**/assets/javascripts/**/*.{gjs,hbs}' --no-error-on-unmatched-pattern",
"lint:hbs:fix": "ember-template-lint 'app/assets/javascripts/**/*.{gjs,hbs}' 'plugins/**/assets/javascripts/**/*.{gjs,hbs}' --no-error-on-unmatched-pattern --fix",
"lint:hbs": "ember-template-lint 'app/assets/javascripts/**/*.{gjs,hbs}' $(script/list_bundled_plugins '/assets/javascripts/**/*.{gjs,hbs}') --no-error-on-unmatched-pattern",
"lint:hbs:fix": "ember-template-lint 'app/assets/javascripts/**/*.{gjs,hbs}' $(script/list_bundled_plugins '/assets/javascripts/**/*.{gjs,hbs}') --no-error-on-unmatched-pattern --fix",
"lint:prettier": "pnpm pprettier --list-different 'app/assets/stylesheets/**/*.scss' 'app/assets/javascripts/**/*.{js,gjs,hbs}' $(script/list_bundled_plugins '/assets/stylesheets/**/*.scss') $(script/list_bundled_plugins '/assets/javascripts/**/*.{js,gjs,hbs}')",
"lint:prettier:fix": "pnpm prettier -w 'app/assets/stylesheets/**/*.scss' 'app/assets/javascripts/**/*.{js,gjs,hbs}' $(script/list_bundled_plugins '/assets/stylesheets/**/*.scss') $(script/list_bundled_plugins '/assets/javascripts/**/*.{js,gjs,hbs}')",
"lttf:ignore": "lint-to-the-future ignore",

View File

@ -106,7 +106,7 @@
</div>
{{/if}}
{{#each triggerFields as |field|}}
{{#each this.triggerFields as |field|}}
<AutomationField
@automation={{this.automation}}
@field={{field}}

View File

@ -1,4 +1,4 @@
{{#if model.length}}
{{#if this.model.length}}
<table class="automations">
<thead>
<tr>
@ -13,7 +13,7 @@
</tr>
</thead>
<tbody>
{{#each model as |automation|}}
{{#each this.model as |automation|}}
<tr>
{{#if automation.script.not_found}}
<td colspan="5" class="alert alert-danger">

View File

@ -38,7 +38,7 @@
{{i18n "discourse_automation.title"}}
</h1>
{{#if showNewAutomation}}
{{#if this.showNewAutomation}}
<DButton
@label="discourse_automation.create"
@icon="plus"

View File

@ -5,8 +5,8 @@
<div class="controls">
<Input
@value={{metadata.topic_id}}
{{on "input" (action (mut metadata.topic_id) value="target.value")}}
@value={{this.metadata.topic_id}}
{{on "input" (action (mut this.metadata.topic_id) value="target.value")}}
/>
</div>
</div>

View File

@ -5,5 +5,5 @@
class="incoming-chat-webhooks-back"
/>
<ChatIncomingWebhookEditForm @chatChannels={{model.chat_channels}} />
<ChatIncomingWebhookEditForm @chatChannels={{this.model.chat_channels}} />
</div>

View File

@ -5,10 +5,10 @@
class="incoming-chat-webhooks-back"
/>
<ConditionalLoadingSpinner @condition={{not model.webhook}}>
<ConditionalLoadingSpinner @condition={{not this.model.webhook}}>
<ChatIncomingWebhookEditForm
@webhook={{model.webhook}}
@chatChannels={{model.chat_channels}}
@webhook={{this.model.webhook}}
@chatChannels={{this.model.chat_channels}}
/>
</ConditionalLoadingSpinner>
</div>

View File

@ -24,6 +24,12 @@ export default class ChatChannelMessageEmojiPicker extends Component {
};
});
@action
willDestroy() {
super.willDestroy(...arguments);
this._popper?.destroy();
}
@action
didSelectEmoji(emoji) {
this.chatEmojiPickerManager.picker?.didSelectEmoji(emoji);
@ -58,12 +64,6 @@ export default class ChatChannelMessageEmojiPicker extends Component {
element.classList.remove("hidden");
}
@action
willDestroy() {
super.willDestroy(...arguments);
this._popper?.destroy();
}
<template>
<ChatEmojiPicker
{{this.listenToBodyScroll}}

View File

@ -43,6 +43,7 @@ export default class ChatFooter extends Component {
this.siteSettings.enable_public_channels,
].filter(Boolean).length;
}
get shouldRenderFooter() {
return (
(this.site.mobileView || this.chatStateManager.isDrawerExpanded) &&

View File

@ -21,17 +21,17 @@ export default class ChatHeader extends Component {
this.router.on("routeDidChange", this, this.#updatePreviousURL);
}
willDestroy() {
super.willDestroy(...arguments);
this.router.off("routeDidChange", this, this.#updatePreviousURL);
}
get shouldRender() {
return (
this.siteSettings.chat_enabled && this.site.mobileView && this.isChatOpen
);
}
willDestroy() {
super.willDestroy(...arguments);
this.router.off("routeDidChange", this, this.#updatePreviousURL);
}
get isChatOpen() {
return this.router.currentURL.startsWith("/chat");
}

View File

@ -21,11 +21,6 @@ export default class UserThreads extends Component {
trackedChannels = {};
@cached
get threadsCollection() {
return this.chatApi.userThreads(this.handleLoadedThreads);
}
willDestroy() {
super.willDestroy(...arguments);
@ -36,6 +31,11 @@ export default class UserThreads extends Component {
this.trackedChannels = {};
}
@cached
get threadsCollection() {
return this.chatApi.userThreads(this.handleLoadedThreads);
}
@bind
handleLoadedThreads(result) {
return result.threads.map((threadObject) => {

View File

@ -27,6 +27,7 @@ export default class ChatController extends Controller {
this.chatStateManager.hasPreloadedChannels
);
}
get shouldUseCoreSidebar() {
return this.siteSettings.navigation_menu === "sidebar";
}

View File

@ -6,6 +6,10 @@ export default class ChatDraftsManager extends Service {
drafts = {};
willDestroy() {
cancel(this?._persistHandler);
}
async add(message, channelId, threadId) {
try {
this.drafts[this.key(channelId, threadId)] = message;
@ -46,8 +50,4 @@ export default class ChatDraftsManager extends Service {
// We don't want to throw an error if the draft fails to save
}
}
willDestroy() {
cancel(this?._persistHandler);
}
}

View File

@ -20,6 +20,28 @@ export default class ChatNotificationManager extends Service {
_subscribedToChat = false;
_countChatInDocTitle = true;
willDestroy() {
super.willDestroy(...arguments);
if (!this._shouldRun()) {
return;
}
this._chatPresenceChannel.off(
"change",
this._subscribeToCorrectNotifications
);
this._chatPresenceChannel.unsubscribe();
this._chatPresenceChannel.leave();
this._corePresenceChannel.off(
"change",
this._subscribeToCorrectNotifications
);
this._corePresenceChannel.unsubscribe();
this._corePresenceChannel.leave();
}
start() {
if (!this._shouldRun()) {
return;
@ -52,28 +74,6 @@ export default class ChatNotificationManager extends Service {
);
}
willDestroy() {
super.willDestroy(...arguments);
if (!this._shouldRun()) {
return;
}
this._chatPresenceChannel.off(
"change",
this._subscribeToCorrectNotifications
);
this._chatPresenceChannel.unsubscribe();
this._chatPresenceChannel.leave();
this._corePresenceChannel.off(
"change",
this._subscribeToCorrectNotifications
);
this._corePresenceChannel.unsubscribe();
this._corePresenceChannel.leave();
}
shouldCountChatInDocTitle() {
return this._countChatInDocTitle;
}

View File

@ -23,6 +23,11 @@ export default class ChatTrackingStateManager extends Service {
// NOTE: In future, we may want to preload some thread tracking state
// as well, but for now we do that on demand when the user opens a channel,
willDestroy() {
super.willDestroy(...arguments);
cancel(this._onTriggerNotificationDebounceHandler);
}
// to avoid having to load all the threads across all channels into memory at once.
setupWithPreloadedState({ channel_tracking = {} }) {
this.chatChannelsManager.channels.forEach((channel) => {
@ -87,11 +92,6 @@ export default class ChatTrackingStateManager extends Service {
}, 0);
}
willDestroy() {
super.willDestroy(...arguments);
cancel(this._onTriggerNotificationDebounceHandler);
}
/**
* Some reactivity in the app such as the document title
* updates are only done via appEvents -- rather than

View File

@ -43,6 +43,29 @@ export default class Chat extends Service {
@tracked _activeMessage = null;
@tracked _activeChannel = null;
init() {
super.init(...arguments);
if (this.userCanChat) {
this.presenceChannel = this.presence.getChannel("/chat/online");
onPresenceChange({
callback: this.onPresenceChangeCallback,
browserHiddenTime: 150000,
userUnseenTime: 150000,
});
}
}
willDestroy() {
super.willDestroy(...arguments);
if (this.userCanChat) {
this.chatSubscriptionsManager.stopChannelsSubscriptions();
removeOnPresenceChange(this.onPresenceChangeCallback);
}
}
get activeChannel() {
return this._activeChannel;
}
@ -94,20 +117,6 @@ export default class Chat extends Service {
}
}
init() {
super.init(...arguments);
if (this.userCanChat) {
this.presenceChannel = this.presence.getChannel("/chat/online");
onPresenceChange({
callback: this.onPresenceChangeCallback,
browserHiddenTime: 150000,
userUnseenTime: 150000,
});
}
}
@bind
onPresenceChangeCallback(present) {
if (present) {
@ -245,15 +254,6 @@ export default class Chat extends Service {
);
}
willDestroy() {
super.willDestroy(...arguments);
if (this.userCanChat) {
this.chatSubscriptionsManager.stopChannelsSubscriptions();
removeOnPresenceChange(this.onPresenceChangeCallback);
}
}
updatePresence() {
next(() => {
if (this.isDestroyed || this.isDestroying) {

View File

@ -8,6 +8,10 @@ const KEEP_ALIVE_DURATION_SECONDS = 10;
export default class ComposerPresenceManager extends Service {
@service presence;
willDestroy() {
this.leave();
}
notifyState(intent, id) {
if (
this.siteSettings.allow_users_to_hide_profile &&
@ -57,8 +61,4 @@ export default class ComposerPresenceManager extends Service {
this._presentChannel = this.presence.getChannel(channelName);
this._presentChannel.enter();
}
willDestroy() {
this.leave();
}
}

View File

@ -28,6 +28,14 @@ export default class PollBreakdownChart extends Component {
this._optionToSlice = {};
}
willDestroy() {
super.willDestroy(...arguments);
if (this._chart) {
this._chart.destroy();
}
}
didInsertElement() {
super.didInsertElement(...arguments);
@ -44,14 +52,6 @@ export default class PollBreakdownChart extends Component {
}
}
willDestroy() {
super.willDestroy(...arguments);
if (this._chart) {
this._chart.destroy();
}
}
@discourseComputed("optionColors", "index")
colorStyle(optionColors, index) {
return htmlSafe(`background: ${optionColors[index]};`);

View File

@ -155,6 +155,7 @@ export default class PollInfoComponent extends Component {
this.publicTitle
);
}
<template>
<div class="poll-info">
<div class="poll-info_counts">

View File

@ -104,6 +104,7 @@ export default class PollResultsPieComponent extends Component {
registerCanvasElement = modifier((element) => {
this.canvasElement = element;
});
get canvasId() {
return htmlSafe(`poll-results-chart-${this.args.id}`);
}
@ -125,6 +126,7 @@ export default class PollResultsPieComponent extends Component {
// eslint-disable-next-line no-undef
this._chart = new Chart(el.getContext("2d"), config);
}
<template>
<div class="poll-results-chart">
<canvas

View File

@ -72,6 +72,7 @@ export default class PollResultsStandardComponent extends Component {
get isMultiple() {
return this.args.pollType === "multiple";
}
<template>
<ul class="results">
{{#each this.orderedOptions key="voters" as |option|}}

View File

@ -20,6 +20,7 @@ export default class TabsComponent extends Component {
? this.tabs[1]
: this.tabs[0];
}
get tabs() {
let tabs = [];

View File

@ -32,8 +32,8 @@ importers:
specifier: ^7.25.9
version: 7.25.9(@babel/core@7.26.0)
'@discourse/lint-configs':
specifier: ^2.1.0
version: 2.1.0(ember-template-lint@6.0.0)(eslint@9.14.0)(prettier@2.8.8)
specifier: ^2.2.0
version: 2.2.0(ember-template-lint@6.0.0)(eslint@9.14.0)(prettier@2.8.8)
'@discourse/moment-timezone-names-translations':
specifier: ^1.0.0
version: 1.0.0
@ -1836,8 +1836,8 @@ packages:
'@discourse/itsatrap@2.0.10':
resolution: {integrity: sha512-Jn1gdiyHMGUsmUfLFf4Q7VnTAv0l7NePbegU6pKhKHEmbzV3FosGxq30fTOYgVyTS1bxqGjlA6LvQttJpv3ROw==}
'@discourse/lint-configs@2.1.0':
resolution: {integrity: sha512-VoZ/+kV1F/nO+bdKN3a/LSFcJTXGwUUVw3yaJiiJCc/+k4fcPaOgcI3z+Ow0ld+DPFZUVkqmxj18r86ThCLyCA==}
'@discourse/lint-configs@2.2.0':
resolution: {integrity: sha512-lj13X+3/DRV2ZBQe3eJvxOO23e87DPfUSSqm0UPfP04VJ7141BHwWn9VVF0rOr+bMe2eiirsqlg2AbMn7gMb+A==}
peerDependencies:
ember-template-lint: 6.0.0
eslint: ^9.14.0
@ -2547,6 +2547,12 @@ packages:
'@socket.io/component-emitter@3.1.2':
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
'@stylistic/eslint-plugin-js@2.11.0':
resolution: {integrity: sha512-btchD0P3iij6cIk5RR5QMdEhtCCV0+L6cNheGhGCd//jaHILZMTi/EOqgEDAf1s4ZoViyExoToM+S2Iwa3U9DA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: '>=8.40.0'
'@swc/core-darwin-arm64@1.9.2':
resolution: {integrity: sha512-nETmsCoY29krTF2PtspEgicb3tqw7Ci5sInTI03EU5zpqYbPjoPH99BVTjj0OsF53jP5MxwnLI5Hm21lUn1d6A==}
engines: {node: '>=10'}
@ -9341,11 +9347,12 @@ snapshots:
'@discourse/itsatrap@2.0.10': {}
'@discourse/lint-configs@2.1.0(ember-template-lint@6.0.0)(eslint@9.14.0)(prettier@2.8.8)':
'@discourse/lint-configs@2.2.0(ember-template-lint@6.0.0)(eslint@9.14.0)(prettier@2.8.8)':
dependencies:
'@babel/core': 7.26.0(supports-color@8.1.1)
'@babel/eslint-parser': 7.25.8(@babel/core@7.26.0)(eslint@9.14.0)
'@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.0)
'@stylistic/eslint-plugin-js': 2.11.0(eslint@9.14.0)
ember-template-lint: 6.0.0
eslint: 9.14.0
eslint-plugin-decorator-position: 6.0.0(@babel/eslint-parser@7.25.8(@babel/core@7.26.0)(eslint@9.14.0))(eslint@9.14.0)
@ -10306,6 +10313,12 @@ snapshots:
'@socket.io/component-emitter@3.1.2': {}
'@stylistic/eslint-plugin-js@2.11.0(eslint@9.14.0)':
dependencies:
eslint: 9.14.0
eslint-visitor-keys: 4.2.0
espree: 10.3.0
'@swc/core-darwin-arm64@1.9.2':
optional: true