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"
tabindex="-1"
>
{{component
this.currentPanelComponent
closeUserMenu=@closeUserMenu
filterByTypes=this.currentNotificationTypes
ariaLabelledby=(concat "user-menu-button-" this.currentTabId)
}}
<this.currentPanelComponent
@closeUserMenu={{@closeUserMenu}}
@filterByTypes={{this.currentNotificationTypes}}
@ariaLabelledby={{concat "user-menu-button-" this.currentTabId}}
/>
</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 getUrl from "discourse-common/lib/get-url";
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_PANEL_COMPONENT = "user-menu/notifications-list";
const DEFAULT_PANEL_COMPONENT = UserMenuNotificationsList;
const REVIEW_QUEUE_TAB_ID = "review-queue";
const CORE_TOP_TABS = [
class extends UserMenuTab {
get id() {
return DEFAULT_TAB_ID;
}
get icon() {
return "bell";
}
get panelComponent() {
return DEFAULT_PANEL_COMPONENT;
}
id = DEFAULT_TAB_ID;
icon = "bell";
panelComponent = DEFAULT_PANEL_COMPONENT;
get linkWhenActive() {
return `${this.currentUser.path}/notifications`;
@ -32,17 +34,10 @@ const CORE_TOP_TABS = [
},
class extends UserMenuTab {
get id() {
return "replies";
}
get icon() {
return "reply";
}
get panelComponent() {
return "user-menu/replies-notifications-list";
}
id = "replies";
icon = "reply";
panelComponent = UserMenuRepliesNotificationsList;
notificationTypes = ["mentioned", "posted", "quoted", "replied"];
get count() {
return (
@ -53,27 +48,15 @@ const CORE_TOP_TABS = [
);
}
get notificationTypes() {
return ["mentioned", "posted", "quoted", "replied"];
}
get linkWhenActive() {
return `${this.currentUser.path}/notifications/responses`;
}
},
class extends UserMenuTab {
get id() {
return "likes";
}
get icon() {
return "heart";
}
get panelComponent() {
return "user-menu/likes-notifications-list";
}
id = "likes";
icon = "heart";
panelComponent = UserMenuLikesNotificationsList;
get shouldDisplay() {
return !this.currentUser.user_option.likes_notifications_disabled;
@ -96,17 +79,10 @@ const CORE_TOP_TABS = [
},
class extends UserMenuTab {
get id() {
return "messages";
}
get icon() {
return "notification.private_message";
}
get panelComponent() {
return "user-menu/messages-list";
}
id = "messages";
icon = "notification.private_message";
panelComponent = UserMenuMessagesList;
notificationTypes = ["private_message", "group_message_summary"];
get count() {
return this.getUnreadCountForType("private_message");
@ -116,53 +92,31 @@ const CORE_TOP_TABS = [
return this.currentUser?.can_send_private_messages;
}
get notificationTypes() {
return ["private_message", "group_message_summary"];
}
get linkWhenActive() {
return `${this.currentUser.path}/messages`;
}
},
class extends UserMenuTab {
get id() {
return "bookmarks";
}
get icon() {
return NO_REMINDER_ICON;
}
get panelComponent() {
return "user-menu/bookmarks-list";
}
id = "bookmarks";
icon = NO_REMINDER_ICON;
panelComponent = UserMenuBookmarksList;
notificationTypes = ["bookmark_reminder"];
get count() {
return this.getUnreadCountForType("bookmark_reminder");
}
get notificationTypes() {
return ["bookmark_reminder"];
}
get linkWhenActive() {
return `${this.currentUser.path}/activity/bookmarks`;
}
},
class extends UserMenuTab {
get id() {
return REVIEW_QUEUE_TAB_ID;
}
get icon() {
return "flag";
}
get panelComponent() {
return "user-menu/reviewables-list";
}
id = REVIEW_QUEUE_TAB_ID;
icon = "flag";
panelComponent = UserMenuReviewablesList;
linkWhenActive = getUrl("/review");
get shouldDisplay() {
return (
@ -173,26 +127,14 @@ const CORE_TOP_TABS = [
get count() {
return this.currentUser.get("reviewable_count");
}
get linkWhenActive() {
return getUrl("/review");
}
},
];
const CORE_BOTTOM_TABS = [
class extends UserMenuTab {
get id() {
return "profile";
}
get icon() {
return "user";
}
get panelComponent() {
return "user-menu/profile-tab-content";
}
id = "profile";
icon = "user";
panelComponent = UserMenuProfileTabContent;
get linkWhenActive() {
return `${this.currentUser.path}/summary`;
@ -201,23 +143,15 @@ const CORE_BOTTOM_TABS = [
];
const CORE_OTHER_NOTIFICATIONS_TAB = class extends UserMenuTab {
id = "other-notifications";
icon = "discourse-other-tab";
panelComponent = UserMenuOtherNotificationsList;
constructor(currentUser, siteSettings, site, otherNotificationTypes) {
super(...arguments);
this.otherNotificationTypes = otherNotificationTypes;
}
get id() {
return "other-notifications";
}
get icon() {
return "discourse-other-tab";
}
get panelComponent() {
return "user-menu/other-notifications-list";
}
get count() {
return this.otherNotificationTypes.reduce((sum, 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 {
@service currentUser;
@service siteSettings;
@ -323,7 +269,10 @@ export default class UserMenu extends Component {
event.preventDefault();
this.currentTabId = tab.id;
this.currentPanelComponent = tab.panelComponent;
this.currentPanelComponent = resolvePanelComponent(
getOwner(this),
tab.panelComponent
);
this.currentNotificationTypes = tab.notificationTypes;
}

View File

@ -2056,9 +2056,9 @@ class PluginApi {
* ```
* api.registerUserMenuTab((UserMenuTab) => {
* return class extends UserMenuTab {
* get id() {
* return "custom-tab-id";
* }
* id = "custom-tab-id";
* panelComponent = MyCustomPanelGlimmerComponent;
* icon = "some-fa5-icon";
*
* get shouldDisplay() {
* return this.siteSettings.enable_custom_tab && this.currentUser.admin;
@ -2067,14 +2067,6 @@ class PluginApi {
* get 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() {
throw new Error("not implemented");

View File

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