DEV: Use component class instead of string in user-menu API (#20806)

In the future we'll be looking at things like tree-shaking and code-splitting. Using 'magic strings' to resolve components is not compatible with those techniques. It makes sense to switch to a more modern pattern now, before the new user-tab API is used too widely.

This commit is backwards-compatible. API consumers which pass a string will see a deprecation message asking them to pass a component class instead.

This commit also turns some unneeded getters into simple class properties (no need to use a getter when it just returns a constant).
This commit is contained in:
David Taylor 2023-03-24 14:26:30 +00:00 committed by GitHub
parent 142d2ab65e
commit 9b41700f87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 130 deletions

View File

@ -35,12 +35,11 @@
class="quick-access-panel" class="quick-access-panel"
tabindex="-1" tabindex="-1"
> >
{{component <this.currentPanelComponent
this.currentPanelComponent @closeUserMenu={{@closeUserMenu}}
closeUserMenu=@closeUserMenu @filterByTypes={{this.currentNotificationTypes}}
filterByTypes=this.currentNotificationTypes @ariaLabelledby={{concat "user-menu-button-" this.currentTabId}}
ariaLabelledby=(concat "user-menu-button-" this.currentTabId) />
}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -6,25 +6,27 @@ import UserMenuTab, { CUSTOM_TABS_CLASSES } from "discourse/lib/user-menu/tab";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import getUrl from "discourse-common/lib/get-url"; import getUrl from "discourse-common/lib/get-url";
import { wantsNewWindow } from "discourse/lib/intercept-click"; import { wantsNewWindow } from "discourse/lib/intercept-click";
import UserMenuNotificationsList from "./notifications-list";
import UserMenuRepliesNotificationsList from "./replies-notifications-list";
import UserMenuLikesNotificationsList from "./likes-notifications-list";
import UserMenuMessagesList from "./messages-list";
import UserMenuBookmarksList from "./bookmarks-list";
import UserMenuReviewablesList from "./reviewables-list";
import UserMenuProfileTabContent from "./profile-tab-content";
import UserMenuOtherNotificationsList from "./other-notifications-list";
import deprecated from "discourse-common/lib/deprecated";
import { getOwner } from "discourse-common/lib/get-owner";
const DEFAULT_TAB_ID = "all-notifications"; const DEFAULT_TAB_ID = "all-notifications";
const DEFAULT_PANEL_COMPONENT = "user-menu/notifications-list"; const DEFAULT_PANEL_COMPONENT = UserMenuNotificationsList;
const REVIEW_QUEUE_TAB_ID = "review-queue"; const REVIEW_QUEUE_TAB_ID = "review-queue";
const CORE_TOP_TABS = [ const CORE_TOP_TABS = [
class extends UserMenuTab { class extends UserMenuTab {
get id() { id = DEFAULT_TAB_ID;
return DEFAULT_TAB_ID; icon = "bell";
} panelComponent = DEFAULT_PANEL_COMPONENT;
get icon() {
return "bell";
}
get panelComponent() {
return DEFAULT_PANEL_COMPONENT;
}
get linkWhenActive() { get linkWhenActive() {
return `${this.currentUser.path}/notifications`; return `${this.currentUser.path}/notifications`;
@ -32,17 +34,10 @@ const CORE_TOP_TABS = [
}, },
class extends UserMenuTab { class extends UserMenuTab {
get id() { id = "replies";
return "replies"; icon = "reply";
} panelComponent = UserMenuRepliesNotificationsList;
notificationTypes = ["mentioned", "posted", "quoted", "replied"];
get icon() {
return "reply";
}
get panelComponent() {
return "user-menu/replies-notifications-list";
}
get count() { get count() {
return ( return (
@ -53,27 +48,15 @@ const CORE_TOP_TABS = [
); );
} }
get notificationTypes() {
return ["mentioned", "posted", "quoted", "replied"];
}
get linkWhenActive() { get linkWhenActive() {
return `${this.currentUser.path}/notifications/responses`; return `${this.currentUser.path}/notifications/responses`;
} }
}, },
class extends UserMenuTab { class extends UserMenuTab {
get id() { id = "likes";
return "likes"; icon = "heart";
} panelComponent = UserMenuLikesNotificationsList;
get icon() {
return "heart";
}
get panelComponent() {
return "user-menu/likes-notifications-list";
}
get shouldDisplay() { get shouldDisplay() {
return !this.currentUser.user_option.likes_notifications_disabled; return !this.currentUser.user_option.likes_notifications_disabled;
@ -96,17 +79,10 @@ const CORE_TOP_TABS = [
}, },
class extends UserMenuTab { class extends UserMenuTab {
get id() { id = "messages";
return "messages"; icon = "notification.private_message";
} panelComponent = UserMenuMessagesList;
notificationTypes = ["private_message", "group_message_summary"];
get icon() {
return "notification.private_message";
}
get panelComponent() {
return "user-menu/messages-list";
}
get count() { get count() {
return this.getUnreadCountForType("private_message"); return this.getUnreadCountForType("private_message");
@ -116,53 +92,31 @@ const CORE_TOP_TABS = [
return this.currentUser?.can_send_private_messages; return this.currentUser?.can_send_private_messages;
} }
get notificationTypes() {
return ["private_message", "group_message_summary"];
}
get linkWhenActive() { get linkWhenActive() {
return `${this.currentUser.path}/messages`; return `${this.currentUser.path}/messages`;
} }
}, },
class extends UserMenuTab { class extends UserMenuTab {
get id() { id = "bookmarks";
return "bookmarks"; icon = NO_REMINDER_ICON;
} panelComponent = UserMenuBookmarksList;
notificationTypes = ["bookmark_reminder"];
get icon() {
return NO_REMINDER_ICON;
}
get panelComponent() {
return "user-menu/bookmarks-list";
}
get count() { get count() {
return this.getUnreadCountForType("bookmark_reminder"); return this.getUnreadCountForType("bookmark_reminder");
} }
get notificationTypes() {
return ["bookmark_reminder"];
}
get linkWhenActive() { get linkWhenActive() {
return `${this.currentUser.path}/activity/bookmarks`; return `${this.currentUser.path}/activity/bookmarks`;
} }
}, },
class extends UserMenuTab { class extends UserMenuTab {
get id() { id = REVIEW_QUEUE_TAB_ID;
return REVIEW_QUEUE_TAB_ID; icon = "flag";
} panelComponent = UserMenuReviewablesList;
linkWhenActive = getUrl("/review");
get icon() {
return "flag";
}
get panelComponent() {
return "user-menu/reviewables-list";
}
get shouldDisplay() { get shouldDisplay() {
return ( return (
@ -173,26 +127,14 @@ const CORE_TOP_TABS = [
get count() { get count() {
return this.currentUser.get("reviewable_count"); return this.currentUser.get("reviewable_count");
} }
get linkWhenActive() {
return getUrl("/review");
}
}, },
]; ];
const CORE_BOTTOM_TABS = [ const CORE_BOTTOM_TABS = [
class extends UserMenuTab { class extends UserMenuTab {
get id() { id = "profile";
return "profile"; icon = "user";
} panelComponent = UserMenuProfileTabContent;
get icon() {
return "user";
}
get panelComponent() {
return "user-menu/profile-tab-content";
}
get linkWhenActive() { get linkWhenActive() {
return `${this.currentUser.path}/summary`; return `${this.currentUser.path}/summary`;
@ -201,23 +143,15 @@ const CORE_BOTTOM_TABS = [
]; ];
const CORE_OTHER_NOTIFICATIONS_TAB = class extends UserMenuTab { const CORE_OTHER_NOTIFICATIONS_TAB = class extends UserMenuTab {
id = "other-notifications";
icon = "discourse-other-tab";
panelComponent = UserMenuOtherNotificationsList;
constructor(currentUser, siteSettings, site, otherNotificationTypes) { constructor(currentUser, siteSettings, site, otherNotificationTypes) {
super(...arguments); super(...arguments);
this.otherNotificationTypes = otherNotificationTypes; this.otherNotificationTypes = otherNotificationTypes;
} }
get id() {
return "other-notifications";
}
get icon() {
return "discourse-other-tab";
}
get panelComponent() {
return "user-menu/other-notifications-list";
}
get count() { get count() {
return this.otherNotificationTypes.reduce((sum, notificationType) => { return this.otherNotificationTypes.reduce((sum, notificationType) => {
return sum + this.getUnreadCountForType(notificationType); return sum + this.getUnreadCountForType(notificationType);
@ -229,6 +163,18 @@ const CORE_OTHER_NOTIFICATIONS_TAB = class extends UserMenuTab {
} }
}; };
function resolvePanelComponent(owner, panelComponent) {
if (typeof panelComponent === "string") {
const nameForConsole = JSON.stringify(panelComponent);
deprecated(
`user-menu tab panelComponent must be passed as a component class (passed ${nameForConsole})`,
{ id: "discourse.user-menu.panel-component-class" }
);
return owner.resolveRegistration(`component:${panelComponent}`);
}
return panelComponent;
}
export default class UserMenu extends Component { export default class UserMenu extends Component {
@service currentUser; @service currentUser;
@service siteSettings; @service siteSettings;
@ -323,7 +269,10 @@ export default class UserMenu extends Component {
event.preventDefault(); event.preventDefault();
this.currentTabId = tab.id; this.currentTabId = tab.id;
this.currentPanelComponent = tab.panelComponent; this.currentPanelComponent = resolvePanelComponent(
getOwner(this),
tab.panelComponent
);
this.currentNotificationTypes = tab.notificationTypes; this.currentNotificationTypes = tab.notificationTypes;
} }

View File

@ -2056,9 +2056,9 @@ class PluginApi {
* ``` * ```
* api.registerUserMenuTab((UserMenuTab) => { * api.registerUserMenuTab((UserMenuTab) => {
* return class extends UserMenuTab { * return class extends UserMenuTab {
* get id() { * id = "custom-tab-id";
* return "custom-tab-id"; * panelComponent = MyCustomPanelGlimmerComponent;
* } * icon = "some-fa5-icon";
* *
* get shouldDisplay() { * get shouldDisplay() {
* return this.siteSettings.enable_custom_tab && this.currentUser.admin; * return this.siteSettings.enable_custom_tab && this.currentUser.admin;
@ -2067,14 +2067,6 @@ class PluginApi {
* get count() { * get count() {
* return this.currentUser.my_custom_notification_count; * return this.currentUser.my_custom_notification_count;
* } * }
*
* get panelComponent() {
* return "your-custom-glimmer-component";
* }
*
* get icon() {
* return "some-fa5-icon";
* }
* } * }
* }); * });
* ``` * ```

View File

@ -45,7 +45,7 @@ export default class UserMenuTab {
} }
/** /**
* @returns {string} Dasherized version of the component name that should be rendered in the panel area when the tab is active. * @returns {Component} Component class that should be rendered in the panel area when the tab is active.
*/ */
get panelComponent() { get panelComponent() {
throw new Error("not implemented"); throw new Error("not implemented");

View File

@ -17,6 +17,7 @@ import TopicFixtures from "discourse/tests/fixtures/topic";
import { Promise } from "rsvp"; import { Promise } from "rsvp";
import { later } from "@ember/runloop"; import { later } from "@ember/runloop";
import I18n from "I18n"; import I18n from "I18n";
import DButton from "discourse/components/d-button";
acceptance("User menu", function (needs) { acceptance("User menu", function (needs) {
needs.user({ needs.user({
@ -178,7 +179,7 @@ acceptance("User menu", function (needs) {
} }
get panelComponent() { get panelComponent() {
return "d-button"; return DButton;
} }
get title() { get title() {
@ -246,7 +247,7 @@ acceptance("User menu", function (needs) {
} }
get panelComponent() { get panelComponent() {
return "d-button"; return DButton;
} }
}; };
}); });
@ -266,7 +267,7 @@ acceptance("User menu", function (needs) {
} }
get panelComponent() { get panelComponent() {
return "d-button"; return DButton;
} }
}; };
}); });
@ -680,7 +681,7 @@ acceptance("User menu", function (needs) {
} }
get panelComponent() { get panelComponent() {
return "d-button"; return DButton;
} }
get linkWhenActive() { get linkWhenActive() {
@ -700,7 +701,7 @@ acceptance("User menu", function (needs) {
} }
get panelComponent() { get panelComponent() {
return "d-button"; return DButton;
} }
}; };
}); });