FEATURE: API to set combined/separated sidebar mode. (#22753)

New API to change sidebar mode. We defined two:
Separated - only sections belonging to specific panel are displayed, and buttons to switch the panel are available as well.
Combined - all sections are displayed together and switch panel buttons are not visible.

In addition, as a part of refactoring, a new service called SidebarState was introduced.
This commit is contained in:
Krzysztof Kotlarek 2023-07-26 10:15:56 +10:00 committed by GitHub
parent 5d1c1e5f62
commit 85e2f67367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 180 additions and 130 deletions

View File

@ -1,13 +1,9 @@
<DSection @pageClass="has-sidebar" @id="d-sidebar" @class="sidebar-container">
{{#if this.showSwitchPanelButtonsOnTop}}
<Sidebar::SwitchPanelButtons
@currentPanel={{this.currentPanel}}
@buttons={{this.switchPanelButtons}}
@setCurrentPanelKey={{this.setCurrentPanelKey}}
/>
<Sidebar::SwitchPanelButtons @buttons={{this.switchPanelButtons}} />
{{/if}}
{{#if this.showMainPanel}}
{{#if this.sidebarState.showMainPanel}}
<Sidebar::Sections
@currentUser={{this.currentUser}}
@collapsableSections={{true}}
@ -17,16 +13,11 @@
<Sidebar::ApiPanels
@currentUser={{this.currentUser}}
@collapsableSections={{true}}
@panel={{this.currentPanel}}
/>
{{/if}}
{{#unless this.showSwitchPanelButtonsOnTop}}
<Sidebar::SwitchPanelButtons
@currentPanel={{this.currentPanel}}
@buttons={{this.switchPanelButtons}}
@setCurrentPanelKey={{this.setCurrentPanelKey}}
/>
<Sidebar::SwitchPanelButtons @buttons={{this.switchPanelButtons}} />
{{/unless}}
<Sidebar::Footer />

View File

@ -1,19 +1,13 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { bind } from "discourse-common/utils/decorators";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import {
currentPanelKey,
customPanels as sidebarCustomPanels,
} from "discourse/lib/sidebar/custom-sections";
export default class Sidebar extends Component {
@service appEvents;
@service site;
@service siteSettings;
@service currentUser;
@tracked currentPanelKey = currentPanelKey;
@service sidebarState;
constructor() {
super(...arguments);
@ -23,26 +17,22 @@ export default class Sidebar extends Component {
}
}
get showMainPanel() {
return this.currentPanelKey === "main";
}
get currentPanel() {
return sidebarCustomPanels.find(
(panel) => panel.key === this.currentPanelKey
);
}
get showSwitchPanelButtonsOnTop() {
return this.siteSettings.default_sidebar_switch_panel_position === "top";
}
get switchPanelButtons() {
if (sidebarCustomPanels.length === 1 || !this.currentUser) {
if (
this.sidebarState.combinedMode ||
this.sidebarState.panels.length === 1 ||
!this.currentUser
) {
return [];
}
return sidebarCustomPanels.filter((panel) => panel !== this.currentPanel);
return this.sidebarState.panels.filter(
(panel) => panel !== this.sidebarState.currentPanel
);
}
@bind
@ -71,9 +61,4 @@ export default class Sidebar extends Component {
document.removeEventListener("click", this.collapseSidebar);
}
}
@action
setCurrentPanelKey(key) {
this.currentPanelKey = key;
}
}

View File

@ -1,6 +1,3 @@
<div class="sidebar-sections">
<Sidebar::ApiSections
@sections={{this.customSections}}
@collapsable={{@collapsableSections}}
/>
<Sidebar::ApiSections @collapsable={{@collapsableSections}} />
</div>

View File

@ -1,20 +0,0 @@
import Component from "@glimmer/component";
import { getOwner, setOwner } from "@ember/application";
import { inject as service } from "@ember/service";
export default class SidebarApiPanels extends Component {
@service siteSettings;
@service currentUser;
@service site;
constructor() {
super(...arguments);
this.customSections =
this.args.panel?.sections?.map((customSection) => {
const section = new customSection();
setOwner(section, getOwner(this));
return section;
}) || [];
}
}

View File

@ -0,0 +1,44 @@
<Sidebar::Section
@sectionName={{this.section.name}}
@headerLinkText={{this.section.text}}
@headerLinkTitle={{this.section.title}}
@headerActionsIcon={{this.section.actionsIcon}}
@headerActions={{this.section.actions}}
@willDestroy={{this.section.willDestroy}}
@collapsable={{@collapsable}}
@displaySection={{this.section.displaySection}}
>
{{#each this.section.links as |link|}}
<Sidebar::SectionLink
@linkName={{link.name}}
@class={{link.classNames}}
@route={{link.route}}
@model={{link.model}}
@models={{link.models}}
@href={{link.href}}
@title={{link.title}}
@contentCSSClass={{link.contentCSSClass}}
@prefixColor={{link.prefixColor}}
@prefixBadge={{link.prefixBadge}}
@prefixType={{link.prefixType}}
@prefixValue={{link.prefixValue}}
@prefixCSSClass={{link.prefixCSSClass}}
@suffixType={{link.suffixType}}
@suffixValue={{link.suffixValue}}
@suffixCSSClass={{link.suffixCSSClass}}
@hoverType={{link.hoverType}}
@hoverValue={{link.hoverValue}}
@hoverAction={{link.hoverAction}}
@hoverTitle={{link.hoverTitle}}
@currentWhen={{link.currentWhen}}
@didInsert={{link.didInsert}}
@willDestroy={{link.willDestroy}}
@content={{link.text}}
@contentComponent={{component
link.contentComponent
status=link.contentComponentArgs
}}
/>
{{/each}}
</Sidebar::Section>

View File

@ -0,0 +1,10 @@
import Component from "@glimmer/component";
import { getOwner, setOwner } from "@ember/application";
export default class SidebarApiSection extends Component {
constructor() {
super(...arguments);
this.section = new this.args.sectionConfig();
setOwner(this.section, getOwner(this));
}
}

View File

@ -1,46 +1,6 @@
{{#each @sections as |customSection|}}
<Sidebar::Section
@sectionName={{customSection.name}}
@headerLinkText={{customSection.text}}
@headerLinkTitle={{customSection.title}}
@headerActionsIcon={{customSection.actionsIcon}}
@headerActions={{customSection.actions}}
@willDestroy={{customSection.willDestroy}}
{{#each this.sections as |sectionConfig|}}
<Sidebar::ApiSection
@sectionConfig={{sectionConfig}}
@collapsable={{@collapsable}}
@displaySection={{customSection.displaySection}}
>
{{#each customSection.links as |link|}}
<Sidebar::SectionLink
@linkName={{link.name}}
@class={{link.classNames}}
@route={{link.route}}
@model={{link.model}}
@models={{link.models}}
@href={{link.href}}
@title={{link.title}}
@contentCSSClass={{link.contentCSSClass}}
@prefixColor={{link.prefixColor}}
@prefixBadge={{link.prefixBadge}}
@prefixType={{link.prefixType}}
@prefixValue={{link.prefixValue}}
@prefixCSSClass={{link.prefixCSSClass}}
@suffixType={{link.suffixType}}
@suffixValue={{link.suffixValue}}
@suffixCSSClass={{link.suffixCSSClass}}
@hoverType={{link.hoverType}}
@hoverValue={{link.hoverValue}}
@hoverAction={{link.hoverAction}}
@hoverTitle={{link.hoverTitle}}
@currentWhen={{link.currentWhen}}
@didInsert={{link.didInsert}}
@willDestroy={{link.willDestroy}}
@content={{link.text}}
@contentComponent={{component
link.contentComponent
status=link.contentComponentArgs
}}
/>
{{/each}}
</Sidebar::Section>
/>
{{/each}}

View File

@ -0,0 +1,14 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
export default class SidebarApiSections extends Component {
@service sidebarState;
get sections() {
if (this.sidebarState.combinedMode) {
return this.sidebarState.panels.map((panel) => panel.sections).flat();
} else {
return this.sidebarState.currentPanel.sections;
}
}
}

View File

@ -4,11 +4,12 @@ import { action } from "@ember/object";
export default class SwitchPanelButtons extends Component {
@service router;
@service sidebarState;
@action
switchPanel(currentPanel, panel) {
currentPanel.lastKnownURL = this.router.currentURL;
this.args.setCurrentPanelKey(panel.key);
this.sidebarState.currentPanel.lastKnownURL = this.router.currentURL;
this.sidebarState.setPanel(panel.key);
const url = panel.lastKnownURL || panel.switchButtonDefaultUrl;
if (url === "/") {
this.router.transitionTo("discovery.latest");

View File

@ -10,8 +10,5 @@
<Sidebar::User::MessagesSection @collapsable={{@collapsableSections}} />
{{/if}}
<Sidebar::ApiSections
@sections={{this.customSections}}
@collapsable={{@collapsableSections}}
/>
<Sidebar::ApiSections @collapsable={{@collapsableSections}} />
</div>

View File

@ -1,5 +1,4 @@
import Component from "@glimmer/component";
import { getOwner, setOwner } from "@ember/application";
import { inject as service } from "@ember/service";
export default class SidebarUserSections extends Component {
@ -7,17 +6,6 @@ export default class SidebarUserSections extends Component {
@service currentUser;
@service site;
constructor() {
super(...arguments);
this.customSections =
this.args.panel?.sections?.map((customSection) => {
const section = new customSection();
setOwner(section, getOwner(this));
return section;
}) || [];
}
get enableMessagesSection() {
return this.currentUser?.can_send_private_messages;
}

View File

@ -108,7 +108,6 @@ import { addSectionLink as addCustomCommunitySectionLink } from "discourse/lib/s
import {
addSidebarPanel,
addSidebarSection,
setSidebarPanel,
} from "discourse/lib/sidebar/custom-sections";
import {
registerCustomCategoryLockIcon,
@ -2068,7 +2067,23 @@ class PluginApi {
* Support for setting a Sidebar panel.
*/
setSidebarPanel(name) {
setSidebarPanel(name);
this._lookupContainer("service:sidebar-state").setPanel(name);
}
/**
* EXPERIMENTAL. Do not use.
* Set combined sidebar section mode. In this mode, sections from all panels are displayed together.
*/
setCombinedSidebarMode() {
this._lookupContainer("service:sidebar-state").setCombinedMode();
}
/**
* EXPERIMENTAL. Do not use.
* Set separated sidebar section mode. In this mode, only sections from the current panel are displayed.
*/
setSeparatedSidebarMode() {
this._lookupContainer("service:sidebar-state").setSeparatedMode();
}
/**

View File

@ -32,10 +32,6 @@ export function addSidebarPanel(func) {
customPanels.push(new panelClass());
}
export function setSidebarPanel(name) {
currentPanelKey = name;
}
export function addSidebarSection(func, panelKey) {
const panel = customPanels.find((p) => p.key === panelKey);
if (!panel) {

View File

@ -0,0 +1,54 @@
import Service from "@ember/service";
import { tracked } from "@glimmer/tracking";
import {
currentPanelKey,
customPanels as panels,
} from "discourse/lib/sidebar/custom-sections";
const COMBINED_MODE = "combined";
const SEPARATED_MODE = "separated";
export default class SidebarState extends Service {
@tracked currentPanelKey = currentPanelKey;
@tracked panels = panels;
@tracked mode = COMBINED_MODE;
constructor() {
super(...arguments);
this.#reset();
}
setPanel(name) {
this.currentPanelKey = name;
this.mode = SEPARATED_MODE;
}
get currentPanel() {
return this.panels.find((panel) => panel.key === this.currentPanelKey);
}
setSeparatedMode() {
this.mode = SEPARATED_MODE;
}
setCombinedMode() {
this.mode = COMBINED_MODE;
this.currentPanelKey = "main";
}
get combinedMode() {
return this.mode === COMBINED_MODE;
}
get showMainPanel() {
return this.currentPanelKey === "main";
}
#reset() {
this.currentPanelKey = currentPanelKey;
this.panels = panels;
this.mode = COMBINED_MODE;
}
}

View File

@ -1049,5 +1049,23 @@ acceptance("Sidebar - Plugin API", function (needs) {
assert
.dom("#d-sidebar .sidebar__panel-switch-button + .sidebar-sections")
.exists();
assert
.dom(
".sidebar-section[data-section-name='test-chat-channels'] .sidebar-section-header-text"
)
.doesNotExist();
withPluginApi(PLUGIN_API_VERSION, (api) => {
api.setCombinedSidebarMode();
});
await visit("/");
assert.dom(".sidebar__panel-switch-button").doesNotExist();
assert
.dom(
".sidebar-section[data-section-name='test-chat-channels'] .sidebar-section-header-text"
)
.exists();
});
});

View File

@ -83,8 +83,8 @@ import {
import { clearTagsHtmlCallbacks } from "discourse/lib/render-tags";
import { clearToolbarCallbacks } from "discourse/components/d-editor";
import { clearExtraHeaderIcons } from "discourse/widgets/header";
import { resetSidebarPanels } from "discourse/lib/sidebar/custom-sections";
import { resetNotificationTypeRenderers } from "discourse/lib/notification-types-manager";
import { resetSidebarPanels } from "discourse/lib/sidebar/custom-sections";
import { resetUserMenuTabs } from "discourse/lib/user-menu/tab";
import { reset as resetLinkLookup } from "discourse/lib/link-lookup";
import { resetMentions } from "discourse/lib/link-mentions";
@ -217,8 +217,8 @@ export function testCleanup(container, app) {
clearResolverOptions();
clearTagsHtmlCallbacks();
clearToolbarCallbacks();
resetSidebarPanels();
resetNotificationTypeRenderers();
resetSidebarPanels();
clearExtraHeaderIcons();
resetOnKeyDownCallbacks();
resetUserMenuTabs();