DEV: Convert the entire sidebar to gjs (#26978)

This commit is contained in:
Jarek Radosz 2024-05-12 19:43:51 +02:00 committed by GitHub
parent 2f95628f87
commit fcd2293226
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
75 changed files with 1649 additions and 1464 deletions

View File

@ -0,0 +1,47 @@
import Category from "discourse/models/category";
import i18n from "discourse-common/helpers/i18n";
import AllCategoriesSectionLink from "../common/all-categories-section-link";
import SidebarCommonCategoriesSection from "../common/categories-section";
import Section from "../section";
import SectionLink from "../section-link";
export default class SidebarAnonymousCategoriesSection extends SidebarCommonCategoriesSection {
shouldSortCategoriesByDefault =
!!this.siteSettings.default_navigation_menu_categories;
get categories() {
if (this.siteSettings.default_navigation_menu_categories) {
return Category.findByIds(
this.siteSettings.default_navigation_menu_categories
.split("|")
.map((categoryId) => parseInt(categoryId, 10))
);
} else {
return this.topSiteCategories;
}
}
<template>
<Section
@sectionName="categories"
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
@collapsable={{@collapsable}}
>
{{#each this.sectionLinks as |sectionLink|}}
<SectionLink
@route={{sectionLink.route}}
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@model={{sectionLink.model}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
data-category-id={{sectionLink.category.id}}
/>
{{/each}}
<AllCategoriesSectionLink />
</Section>
</template>
}

View File

@ -1,22 +0,0 @@
<Sidebar::Section
@sectionName="categories"
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
@collapsable={{@collapsable}}
>
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::SectionLink
@route={{sectionLink.route}}
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@model={{sectionLink.model}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
data-category-id={{sectionLink.category.id}}
/>
{{/each}}
<Sidebar::Common::AllCategoriesSectionLink />
</Sidebar::Section>

View File

@ -1,24 +0,0 @@
import SidebarCommonCategoriesSection from "discourse/components/sidebar/common/categories-section";
import Category from "discourse/models/category";
export default class SidebarAnonymousCategoriesSection extends SidebarCommonCategoriesSection {
constructor() {
super(...arguments);
if (!this.siteSettings.default_navigation_menu_categories) {
this.shouldSortCategoriesByDefault = false;
}
}
get categories() {
if (this.siteSettings.default_navigation_menu_categories) {
return Category.findByIds(
this.siteSettings.default_navigation_menu_categories
.split("|")
.map((categoryId) => parseInt(categoryId, 10))
);
} else {
return this.topSiteCategories;
}
}
}

View File

@ -1,4 +1,4 @@
import SidebarCustomSection from "discourse/components/sidebar/common/custom-sections";
import SidebarCustomSection from "../common/custom-sections";
export default class SidebarAnonymousCustomSections extends SidebarCustomSection {
anonymous = true;

View File

@ -0,0 +1,20 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import CategoriesSection from "./categories-section";
import CustomSections from "./custom-sections";
import TagsSection from "./tags-section";
export default class SidebarAnonymousSections extends Component {
@service siteSettings;
<template>
<div class="sidebar-sections sidebar-sections-anonymous">
<CustomSections @collapsable={{@collapsableSections}} />
<CategoriesSection @collapsable={{@collapsableSections}} />
{{#if this.siteSettings.tagging_enabled}}
<TagsSection @collapsable={{@collapsableSections}} />
{{/if}}
</div>
</template>
}

View File

@ -1,10 +0,0 @@
<div class="sidebar-sections sidebar-sections-anonymous">
<Sidebar::Anonymous::CustomSections @collapsable={{@collapsableSections}} />
<Sidebar::Anonymous::CategoriesSection
@collapsable={{@collapsableSections}}
/>
{{#if this.siteSettings.tagging_enabled}}
<Sidebar::Anonymous::TagsSection @collapsable={{@collapsableSections}} />
{{/if}}
</div>

View File

@ -1,6 +0,0 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
export default class SidebarAnonymousSections extends Component {
@service siteSettings;
}

View File

@ -0,0 +1,61 @@
import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { service } from "@ember/service";
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
import i18n from "discourse-common/helpers/i18n";
import AllTagsSectionLink from "../common/all-tags-section-link";
import Section from "../section";
import SectionLink from "../section-link";
export default class SidebarAnonymousTagsSection extends Component {
@service router;
@service topicTrackingState;
@service site;
get displaySection() {
return (
this.site.anonymous_default_navigation_menu_tags?.length > 0 ||
this.site.navigation_menu_site_top_tags?.length > 0
);
}
@cached
get sectionLinks() {
return (
this.site.anonymous_default_navigation_menu_tags ||
this.site.navigation_menu_site_top_tags
).map((tag) => {
return new TagSectionLink({
tag,
topicTrackingState: this.topicTrackingState,
});
});
}
<template>
{{#if this.displaySection}}
<Section
@sectionName="tags"
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
@collapsable={{@collapsable}}
>
{{#each this.sectionLinks as |sectionLink|}}
<SectionLink
@route={{sectionLink.route}}
@content={{sectionLink.text}}
@title={{sectionLink.title}}
@currentWhen={{sectionLink.currentWhen}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@models={{sectionLink.models}}
data-tag-name={{sectionLink.tagName}}
/>
{{/each}}
<AllTagsSectionLink />
</Section>
{{/if}}
</template>
}

View File

@ -1,24 +0,0 @@
{{#if this.displaySection}}
<Sidebar::Section
@sectionName="tags"
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
@collapsable={{@collapsable}}
>
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::SectionLink
@route={{sectionLink.route}}
@content={{sectionLink.text}}
@title={{sectionLink.title}}
@currentWhen={{sectionLink.currentWhen}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@models={{sectionLink.models}}
data-tag-name={{sectionLink.tagName}}
/>
{{/each}}
<Sidebar::Common::AllTagsSectionLink />
</Sidebar::Section>
{{/if}}

View File

@ -1,30 +0,0 @@
import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { service } from "@ember/service";
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
export default class SidebarAnonymousTagsSection extends Component {
@service router;
@service topicTrackingState;
@service site;
get displaySection() {
return (
this.site.anonymous_default_navigation_menu_tags?.length > 0 ||
this.site.navigation_menu_site_top_tags?.length > 0
);
}
@cached
get sectionLinks() {
return (
this.site.anonymous_default_navigation_menu_tags ||
this.site.navigation_menu_site_top_tags
).map((tag) => {
return new TagSectionLink({
tag,
topicTrackingState: this.topicTrackingState,
});
});
}
}

View File

@ -0,0 +1,9 @@
import ApiSections from "./api-sections";
const SidebarApiPanels = <template>
<div class="sidebar-sections">
<ApiSections @collapsable={{@collapsableSections}} />
</div>
</template>;
export default SidebarApiPanels;

View File

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

View File

@ -0,0 +1,94 @@
import Component from "@glimmer/component";
import { getOwner, setOwner } from "@ember/owner";
import { service } from "@ember/service";
import Section from "./section";
import SectionLink from "./section-link";
export default class SidebarApiSection extends Component {
@service sidebarState;
constructor() {
super(...arguments);
this.section = new this.args.sectionConfig();
setOwner(this.section, getOwner(this));
}
get shouldDisplay() {
return (
!this.sidebarState.currentPanel.filterable ||
this.sidebarState.filter.length === 0 ||
this.filteredLinks.length > 0
);
}
get filteredLinks() {
if (!this.sidebarState.filter) {
return this.section.links;
}
if (this.section.text.toLowerCase().match(this.sidebarState.filter)) {
return this.section.links;
}
return this.section.links.filter((link) => {
return (
link.text.toString().toLowerCase().match(this.sidebarState.filter) ||
link.keywords.navigation.some((keyword) =>
keyword.match(this.sidebarState.filter)
)
);
});
}
<template>
{{#if this.shouldDisplay}}
<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}}
@hideSectionHeader={{this.section.hideSectionHeader}}
@collapsedByDefault={{this.section.collapsedByDefault}}
>
{{#each this.filteredLinks key="name" as |link|}}
<SectionLink
@linkName={{link.name}}
@linkClass={{link.classNames}}
@route={{link.route}}
@model={{link.model}}
@query={{link.query}}
@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}}
</Section>
{{/if}}
</template>
}

View File

@ -1,48 +0,0 @@
{{#if this.shouldDisplay}}
<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}}
@hideSectionHeader={{this.section.hideSectionHeader}}
@collapsedByDefault={{this.section.collapsedByDefault}}
>
{{#each this.filteredLinks key="name" as |link|}}
<Sidebar::SectionLink
@linkName={{link.name}}
@linkClass={{link.classNames}}
@route={{link.route}}
@model={{link.model}}
@query={{link.query}}
@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>
{{/if}}

View File

@ -1,40 +0,0 @@
import Component from "@glimmer/component";
import { getOwner, setOwner } from "@ember/application";
import { service } from "@ember/service";
export default class SidebarApiSection extends Component {
@service sidebarState;
constructor() {
super(...arguments);
this.section = new this.args.sectionConfig();
setOwner(this.section, getOwner(this));
}
get shouldDisplay() {
if (!this.sidebarState.currentPanel.filterable) {
return true;
}
return (
this.sidebarState.filter.length === 0 || this.filteredLinks.length > 0
);
}
get filteredLinks() {
if (!this.sidebarState.filter) {
return this.section.links;
}
if (this.section.text.toLowerCase().match(this.sidebarState.filter)) {
return this.section.links;
}
return this.section.links.filter((link) => {
return (
link.text.toString().toLowerCase().match(this.sidebarState.filter) ||
link.keywords.navigation.some((keyword) =>
keyword.match(this.sidebarState.filter)
)
);
});
}
}

View File

@ -0,0 +1,32 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import AdminHeader from "./admin-header";
import ApiSection from "./api-section";
import FilterNoResults from "./filter-no-results";
export default class SidebarApiSections extends Component {
@service sidebarState;
get sections() {
if (this.sidebarState.combinedMode) {
return this.sidebarState.panels
.filter((panel) => !panel.hidden)
.flatMap((panel) => panel.sections);
} else {
return this.sidebarState.currentPanel.sections;
}
}
<template>
<AdminHeader />
{{#each this.sections as |sectionConfig|}}
<ApiSection
@sectionConfig={{sectionConfig}}
@collapsable={{@collapsable}}
/>
{{/each}}
<FilterNoResults />
</template>
}

View File

@ -1,8 +0,0 @@
<Sidebar::AdminHeader />
{{#each this.sections as |sectionConfig|}}
<Sidebar::ApiSection
@sectionConfig={{sectionConfig}}
@collapsable={{@collapsable}}
/>
{{/each}}
<Sidebar::FilterNoResults />

View File

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

View File

@ -3,7 +3,7 @@ import { LinkTo } from "@ember/routing";
import { service } from "@ember/service";
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
import { defaultHomepage } from "discourse/lib/utilities";
import dIcon from "discourse-common/helpers/d-icon";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
export default class BackToForum extends Component {
@ -13,14 +13,13 @@ export default class BackToForum extends Component {
return this.sidebarState.isCurrentPanel(ADMIN_PANEL);
}
get homepage() {
return `discovery.${defaultHomepage()}`;
}
<template>
{{#if this.shouldDisplay}}
<LinkTo @route={{this.homepage}} class="sidebar-sections__back-to-forum">
{{dIcon "arrow-left"}}
<LinkTo
@route="discovery.{{(defaultHomepage)}}"
class="sidebar-sections__back-to-forum"
>
{{icon "arrow-left"}}
<span>{{i18n "admin.back_to_forum"}}</span>
</LinkTo>

View File

@ -0,0 +1,14 @@
import i18n from "discourse-common/helpers/i18n";
import SectionLink from "../section-link";
const SidebarCommonAllCategoriesSectionLink = <template>
<SectionLink
@linkName="all-categories"
@content={{i18n "sidebar.all_categories"}}
@route="discovery.categories"
@prefixType="icon"
@prefixValue="sidebar.all_categories"
/>
</template>;
export default SidebarCommonAllCategoriesSectionLink;

View File

@ -1,7 +0,0 @@
<Sidebar::SectionLink
@linkName="all-categories"
@content={{i18n "sidebar.all_categories"}}
@route="discovery.categories"
@prefixType="icon"
@prefixValue="sidebar.all_categories"
/>

View File

@ -0,0 +1,14 @@
import i18n from "discourse-common/helpers/i18n";
import SectionLink from "../section-link";
const SidebarCommonAllTagsSectionLink = <template>
<SectionLink
@linkName="all-tags"
@content={{i18n "sidebar.all_tags"}}
@route="tags"
@prefixType="icon"
@prefixValue="list"
/>
</template>;
export default SidebarCommonAllTagsSectionLink;

View File

@ -1,7 +0,0 @@
<Sidebar::SectionLink
@linkName="all-tags"
@content={{i18n "sidebar.all_tags"}}
@route="tags"
@prefixType="icon"
@prefixValue="list"
/>

View File

@ -8,9 +8,9 @@ import Category from "discourse/models/category";
export const TOP_SITE_CATEGORIES_TO_SHOW = 5;
export default class SidebarCommonCategoriesSection extends Component {
@service topicTrackingState;
@service siteSettings;
@service site;
@service siteSettings;
@service topicTrackingState;
shouldSortCategoriesByDefault = true;

View File

@ -0,0 +1,109 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { getOwner } from "@ember/owner";
import { service } from "@ember/service";
import { or } from "truth-helpers";
import replaceEmoji from "discourse/helpers/replace-emoji";
import CommonCommunitySection from "discourse/lib/sidebar/common/community-section/section";
import Section from "discourse/lib/sidebar/section";
import AdminCommunitySection from "discourse/lib/sidebar/user/community-section/admin-section";
import MoreSectionLink from "../more-section-link";
import MoreSectionLinks from "../more-section-links";
import SectionComponent from "../section";
import SectionLink from "../section-link";
import SectionLinkButton from "../section-link-button";
export default class SidebarCustomSection extends Component {
@service currentUser;
@service navigationMenu;
@service site;
@service siteSettings;
@tracked section = this.initialSection;
willDestroy() {
super.willDestroy();
this.section.teardown?.();
}
get initialSection() {
const opts = {
section: this.args.sectionData,
owner: getOwner(this),
};
if (this.args.sectionData.section_type !== "community") {
return new Section(opts);
}
if (this.currentUser?.admin) {
return new AdminCommunitySection(opts);
} else {
return new CommonCommunitySection(opts);
}
}
<template>
<SectionComponent
@sectionName={{this.section.slug}}
@headerLinkText={{this.section.decoratedTitle}}
@indicatePublic={{this.section.indicatePublic}}
@collapsable={{@collapsable}}
@headerActions={{this.section.headerActions}}
@headerActionsIcon={{this.section.headerActionIcon}}
@hideSectionHeader={{this.section.hideSectionHeader}}
class={{this.section.dragCss}}
>
{{#each this.section.links as |link|}}
<SectionLink
@badgeText={{link.badgeText}}
@content={{replaceEmoji link.text}}
@currentWhen={{link.currentWhen}}
@href={{or link.value link.href}}
@linkClass={{link.linkDragCss}}
@linkName={{link.name}}
@model={{link.model}}
@models={{link.models}}
@prefixType="icon"
@prefixValue={{link.prefixValue}}
@query={{link.query}}
@route={{link.route}}
@shouldDisplay={{link.shouldDisplay}}
@suffixCSSClass={{link.suffixCSSClass}}
@suffixType={{link.suffixType}}
@suffixValue={{link.suffixValue}}
@title={{link.title}}
/>
{{/each}}
{{#if this.section.moreLinks}}
{{#if this.navigationMenu.isDesktopDropdownMode}}
{{#each this.section.moreLinks as |sectionLink|}}
<MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
{{#if this.section.moreSectionButtonAction}}
<SectionLinkButton
@action={{this.section.moreSectionButtonAction}}
@icon={{this.section.moreSectionButtonIcon}}
@text={{this.section.moreSectionButtonText}}
/>
{{/if}}
{{else if this.section.moreLinks}}
<MoreSectionLinks
@sectionLinks={{this.section.moreLinks}}
@moreButtonAction={{this.section.moreSectionButtonAction}}
@moreButtonText={{this.section.moreSectionButtonText}}
@moreButtonIcon={{this.section.moreSectionButtonIcon}}
/>
{{/if}}
{{else if this.section.moreSectionButtonAction}}
<SectionLinkButton
@action={{this.section.moreSectionButtonAction}}
@icon={{this.section.moreSectionButtonIcon}}
@text={{this.section.moreSectionButtonText}}
/>
{{/if}}
</SectionComponent>
</template>
}

View File

@ -1,61 +0,0 @@
<Sidebar::Section
@sectionName={{this.section.slug}}
@headerLinkText={{this.section.decoratedTitle}}
@indicatePublic={{this.section.indicatePublic}}
@collapsable={{@collapsable}}
@headerActions={{this.section.headerActions}}
@headerActionsIcon={{this.section.headerActionIcon}}
@hideSectionHeader={{this.section.hideSectionHeader}}
class={{this.section.dragCss}}
>
{{#each this.section.links as |link|}}
<Sidebar::SectionLink
@badgeText={{link.badgeText}}
@content={{replace-emoji link.text}}
@currentWhen={{link.currentWhen}}
@href={{or link.value link.href}}
@linkClass={{link.linkDragCss}}
@linkName={{link.name}}
@model={{link.model}}
@models={{link.models}}
@prefixType="icon"
@prefixValue={{link.prefixValue}}
@query={{link.query}}
@route={{link.route}}
@shouldDisplay={{link.shouldDisplay}}
@suffixCSSClass={{link.suffixCSSClass}}
@suffixType={{link.suffixType}}
@suffixValue={{link.suffixValue}}
@title={{link.title}}
/>
{{/each}}
{{#if this.section.moreLinks}}
{{#if this.navigationMenu.isDesktopDropdownMode}}
{{#each this.section.moreLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
{{#if this.section.moreSectionButtonAction}}
<Sidebar::SectionLinkButton
@action={{this.section.moreSectionButtonAction}}
@icon={{this.section.moreSectionButtonIcon}}
@text={{this.section.moreSectionButtonText}}
/>
{{/if}}
{{else if this.section.moreLinks}}
<Sidebar::MoreSectionLinks
@sectionLinks={{this.section.moreLinks}}
@moreButtonAction={{this.section.moreSectionButtonAction}}
@moreButtonText={{this.section.moreSectionButtonText}}
@moreButtonIcon={{this.section.moreSectionButtonIcon}}
/>
{{/if}}
{{else if this.section.moreSectionButtonAction}}
<Sidebar::SectionLinkButton
@action={{this.section.moreSectionButtonAction}}
@icon={{this.section.moreSectionButtonIcon}}
@text={{this.section.moreSectionButtonText}}
/>
{{/if}}
</Sidebar::Section>

View File

@ -1,43 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { getOwner } from "@ember/application";
import { service } from "@ember/service";
import CommonCommunitySection from "discourse/lib/sidebar/common/community-section/section";
import Section from "discourse/lib/sidebar/section";
import AdminCommunitySection from "discourse/lib/sidebar/user/community-section/admin-section";
export default class SidebarCustomSection extends Component {
@service currentUser;
@service navigationMenu;
@service site;
@service siteSettings;
@tracked section;
constructor() {
super(...arguments);
this.section = this.#initializeSection();
}
willDestroy() {
this.section.teardown?.();
super.willDestroy();
}
#initializeSection() {
let sectionClass = Section;
switch (this.args.sectionData.section_type) {
case "community":
sectionClass = this.currentUser?.admin
? AdminCommunitySection
: CommonCommunitySection;
break;
}
return new sectionClass({
section: this.args.sectionData,
owner: getOwner(this),
});
}
}

View File

@ -1,5 +1,6 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import CustomSection from "./custom-section";
export default class SidebarCustomSection extends Component {
@service currentUser;
@ -19,4 +20,12 @@ export default class SidebarCustomSection extends Component {
return this.currentUser.sidebarSections;
}
}
<template>
<div class="sidebar-custom-sections">
{{#each this.sections as |section|}}
<CustomSection @sectionData={{section}} @collapsable={{@collapsable}} />
{{/each}}
</div>
</template>
}

View File

@ -1,8 +0,0 @@
<div class="sidebar-custom-sections">
{{#each this.sections as |section|}}
<Sidebar::Common::CustomSection
@sectionData={{section}}
@collapsable={{@collapsable}}
/>
{{/each}}
</div>

View File

@ -59,7 +59,7 @@ function applyMode(mode, categories, selectedSidebarCategoryIds) {
});
}
export default class extends Component {
export default class SidebarEditNavigationMenuCategoriesModal extends Component {
@service currentUser;
@service site;
@service siteSettings;
@ -67,7 +67,7 @@ export default class extends Component {
@tracked initialLoad = true;
@tracked filteredCategoriesGroupings = [];
@tracked filteredCategoryIds = [];
// TODO: tracked array, no ember array methods
@tracked
selectedSidebarCategoryIds = [...this.currentUser.sidebar_category_ids];
@ -307,31 +307,31 @@ export default class extends Component {
<div class="sidebar-categories-form__loading">
{{loadingSpinner size="small"}}
</div>
{{else if (gt this.filteredCategoriesGroupings.length 0)}}
{{else}}
{{#each this.filteredCategoriesGroupings as |categories|}}
<div
class="sidebar-categories-form__row"
style={{borderColor (get categories "0.color") "left"}}
{{didInsert this.didInsert}}
style={{borderColor (get categories "0.color") "left"}}
class="sidebar-categories-form__row"
>
{{#each categories as |category|}}
<div
class="sidebar-categories-form__category-row"
data-category-id={{category.id}}
data-category-level={{category.level}}
class="sidebar-categories-form__category-row"
>
<label
class="sidebar-categories-form__category-label"
for={{concat
"sidebar-categories-form__input--"
category.id
}}
class="sidebar-categories-form__category-label"
>
<div class="sidebar-categories-form__category-wrapper">
<div class="sidebar-categories-form__category-badge">
{{categoryBadge category}}
</div>
{{#unless category.parentCategory}}
<div
class="sidebar-categories-form__category-description"
@ -345,11 +345,7 @@ export default class extends Component {
</div>
<Input
id={{concat
"sidebar-categories-form__input--"
category.id
}}
class="sidebar-categories-form__input"
{{on "click" (fn this.toggleCategory category.id)}}
@type="checkbox"
@checked={{includes
this.selectedSidebarCategoryIds
@ -358,17 +354,21 @@ export default class extends Component {
disabled={{not
(includes this.filteredCategoryIds category.id)
}}
{{on "click" (fn this.toggleCategory category.id)}}
id={{concat
"sidebar-categories-form__input--"
category.id
}}
class="sidebar-categories-form__input"
/>
</label>
</div>
{{/each}}
</div>
{{else}}
<div class="sidebar-categories-form__no-categories">
{{i18n "sidebar.categories_form_modal.no_categories"}}
</div>
{{/each}}
{{else}}
<div class="sidebar-categories-form__no-categories">
{{i18n "sidebar.categories_form_modal.no_categories"}}
</div>
{{/if}}
</form>
</EditNavigationMenuModal>

View File

@ -6,12 +6,13 @@ import { on } from "@ember/modifier";
import { action } from "@ember/object";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import dIcon from "discourse-common/helpers/d-icon";
import withEventValue from "discourse/helpers/with-event-value";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import I18n from "discourse-i18n";
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
export default class extends Component {
export default class SidebarEditNavigationMenuModal extends Component {
@tracked filter = "";
@tracked filterDropdownValue = "all";
@ -34,11 +35,6 @@ export default class extends Component {
},
];
@action
onFilterInput(input) {
this.args.onFilterInput(input.target.value);
}
@action
onFilterDropdownChange(value) {
this.filterDropdownValue = value;
@ -79,18 +75,18 @@ export default class extends Component {
<:belowHeader>
<div class="sidebar__edit-navigation-menu__filter">
<div class="sidebar__edit-navigation-menu__filter-input">
{{dIcon
{{icon
"search"
class="sidebar__edit-navigation-menu__filter-input-icon"
}}
<Input
class="sidebar__edit-navigation-menu__filter-input-field"
placeholder={{@inputFilterPlaceholder}}
{{on "input" (withEventValue @onFilterInput)}}
@type="text"
@value={{this.filter}}
{{on "input" this.onFilterInput}}
placeholder={{@inputFilterPlaceholder}}
autofocus="true"
class="sidebar__edit-navigation-menu__filter-input-field"
/>
</div>
@ -113,18 +109,18 @@ export default class extends Component {
<:footer>
<div class="sidebar__edit-navigation-menu__footer">
<DButton
@action={{@save}}
@label="save"
@disabled={{@saving}}
@action={{@save}}
class="btn-primary sidebar__edit-navigation-menu__save-button"
/>
{{#if @showResetDefaultsButton}}
<DButton
@icon="undo"
@label="sidebar.edit_navigation_modal_form.reset_to_defaults"
@disabled={{@saving}}
@action={{@resetToDefaults}}
@label="sidebar.edit_navigation_modal_form.reset_to_defaults"
@icon="undo"
@disabled={{@saving}}
class="btn-flat btn-text sidebar__edit-navigation-menu__reset-defaults-button"
/>
{{/if}}

View File

@ -1,12 +1,21 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { concat, fn } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import { gt, includes, or } from "truth-helpers";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import loadingSpinner from "discourse/helpers/loading-spinner";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { INPUT_DELAY } from "discourse-common/config/environment";
import i18n from "discourse-common/helpers/i18n";
import discourseDebounce from "discourse-common/lib/debounce";
import EditNavigationMenuModal from "./modal";
export default class extends Component {
export default class SidebarEditNavigationMenuTagsModal extends Component {
@service currentUser;
@service siteSettings;
@service store;
@ -161,4 +170,72 @@ export default class extends Component {
this.saving = false;
});
}
<template>
<EditNavigationMenuModal
@title="sidebar.tags_form_modal.title"
@saving={{this.saving}}
@save={{this.save}}
@showResetDefaultsButton={{gt
this.siteSettings.default_navigation_menu_tags.length
0
}}
@resetToDefaults={{this.resetToDefaults}}
@deselectAll={{this.deselectAll}}
@deselectAllText={{i18n "sidebar.tags_form_modal.subtitle.text"}}
@inputFilterPlaceholder={{i18n
"sidebar.tags_form_modal.filter_placeholder"
}}
@onFilterInput={{this.onFilterInput}}
@resetFilter={{this.resetFilter}}
@filterSelected={{this.filterSelected}}
@filterUnselected={{this.filterUnselected}}
@closeModal={{@closeModal}}
@loading={{or this.tagsLoading this.disableFiltering}}
class="sidebar__edit-navigation-menu__tags-modal"
>
{{#if this.tagsLoading}}
{{loadingSpinner size="large"}}
{{else}}
<form class="sidebar-tags-form">
{{#each this.tags as |tag|}}
<div
{{didInsert this.didInsertTag}}
data-tag-name={{tag.name}}
class="sidebar-tags-form__tag"
>
<Input
{{on "click" (fn this.toggleTag tag.name)}}
@type="checkbox"
@checked={{includes this.selectedTags tag.name}}
id={{concat "sidebar-tags-form__input--" tag.name}}
class="sidebar-tags-form__input"
/>
<label
for={{concat "sidebar-tags-form__input--" tag.name}}
class="sidebar-tags-form__tag-label"
>
<p>
<span class="sidebar-tags-form__tag-label-name">
{{tag.name}}
</span>
<span class="sidebar-tags-form__tag-label-count">
({{tag.topic_count}})
</span>
</p>
</label>
</div>
{{else}}
<div class="sidebar-tags-form__no-tags">
{{i18n "sidebar.tags_form_modal.no_tags"}}
</div>
{{/each}}
</form>
{{/if}}
<ConditionalLoadingSpinner @condition={{this.tags.loadingMore}} />
</EditNavigationMenuModal>
</template>
}

View File

@ -1,65 +0,0 @@
<Sidebar::EditNavigationMenu::Modal
@title="sidebar.tags_form_modal.title"
@saving={{this.saving}}
@save={{this.save}}
@showResetDefaultsButton={{gt
this.siteSettings.default_navigation_menu_tags.length
0
}}
@resetToDefaults={{this.resetToDefaults}}
@deselectAll={{this.deselectAll}}
@deselectAllText={{i18n "sidebar.tags_form_modal.subtitle.text"}}
@inputFilterPlaceholder={{i18n "sidebar.tags_form_modal.filter_placeholder"}}
@onFilterInput={{this.onFilterInput}}
@resetFilter={{this.resetFilter}}
@filterSelected={{this.filterSelected}}
@filterUnselected={{this.filterUnselected}}
@closeModal={{@closeModal}}
@loading={{or this.tagsLoading this.disableFiltering}}
class="sidebar__edit-navigation-menu__tags-modal"
>
{{#if this.tagsLoading}}
{{loading-spinner size="large"}}
{{else}}
<form class="sidebar-tags-form">
{{#if (gt this.tags.length 0)}}
{{#each this.tags as |tag|}}
<div
class="sidebar-tags-form__tag"
data-tag-name={{tag.name}}
{{did-insert this.didInsertTag}}
>
<Input
id={{concat "sidebar-tags-form__input--" tag.name}}
class="sidebar-tags-form__input"
@type="checkbox"
@checked={{includes this.selectedTags tag.name}}
{{on "click" (fn this.toggleTag tag.name)}}
/>
<label
class="sidebar-tags-form__tag-label"
for={{concat "sidebar-tags-form__input--" tag.name}}
>
<p>
<span class="sidebar-tags-form__tag-label-name">
{{tag.name}}
</span>
<span class="sidebar-tags-form__tag-label-count">
({{tag.topic_count}})
</span>
</p>
</label>
</div>
{{/each}}
{{else}}
<div class="sidebar-tags-form__no-tags">
{{i18n "sidebar.tags_form_modal.no_tags"}}
</div>
{{/if}}
</form>
{{/if}}
<ConditionalLoadingSpinner @condition={{this.tags.loadingMore}} />
</Sidebar::EditNavigationMenu::Modal>

View File

@ -5,7 +5,7 @@ import i18n from "discourse-common/helpers/i18n";
import getURL from "discourse-common/lib/get-url";
import I18n from "discourse-i18n";
export default class FilterNoResulsts extends Component {
export default class FilterNoResults extends Component {
@service sidebarState;
/**

View File

@ -0,0 +1,62 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import ApiPanels from "./api-panels";
import Footer from "./footer";
import Sections from "./sections";
export default class SidebarHamburgerDropdown extends Component {
@service appEvents;
@service currentUser;
@service site;
@service siteSettings;
@service sidebarState;
@action
triggerRenderedAppEvent() {
this.appEvents.trigger("sidebar-hamburger-dropdown:rendered");
}
get collapsableSections() {
if (
this.siteSettings.navigation_menu === "header dropdown" &&
!this.args.collapsableSections
) {
return this.site.mobileView || this.site.narrowDesktopView;
} else {
this.args.collapsableSections;
}
}
<template>
<div class="hamburger-panel">
<div
{{didInsert this.triggerRenderedAppEvent}}
data-max-width="320"
class="revamped menu-panel drop-down"
>
<div class="panel-body">
<div class="panel-body-contents">
<div class="sidebar-hamburger-dropdown">
{{#if this.sidebarState.showMainPanel}}
<Sections
@currentUser={{this.currentUser}}
@collapsableSections={{this.collapsableSections}}
@panel={{this.sidebarState.currentPanel}}
/>
{{else}}
<ApiPanels
@currentUser={{this.currentUser}}
@collapsableSections={{this.collapsableSections}}
/>
{{/if}}
<Footer />
</div>
</div>
</div>
</div>
</div>
</template>
}

View File

@ -1,27 +0,0 @@
<div class="hamburger-panel">
<div
class="revamped menu-panel drop-down"
data-max-width="320"
{{did-insert this.triggerRenderedAppEvent}}
>
<div class="panel-body">
<div class="panel-body-contents">
<div class="sidebar-hamburger-dropdown">
{{#if this.sidebarState.showMainPanel}}
<Sidebar::Sections
@currentUser={{this.currentUser}}
@collapsableSections={{this.collapsableSections}}
@panel={{this.sidebarState.currentPanel}}
/>
{{else}}
<Sidebar::ApiPanels
@currentUser={{this.currentUser}}
@collapsableSections={{this.collapsableSections}}
/>
{{/if}}
<Sidebar::Footer @tagName="" />
</div>
</div>
</div>
</div>
</div>

View File

@ -1,27 +0,0 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { service } from "@ember/service";
export default class SidebarHamburgerDropdown extends Component {
@service appEvents;
@service currentUser;
@service site;
@service siteSettings;
@service sidebarState;
@action
triggerRenderedAppEvent() {
this.appEvents.trigger("sidebar-hamburger-dropdown:rendered");
}
get collapsableSections() {
if (
this.siteSettings.navigation_menu === "header dropdown" &&
!this.args.collapsableSections
) {
return this.site.mobileView || this.site.narrowDesktopView;
} else {
this.args.collapsableSections;
}
}
}

View File

@ -0,0 +1,26 @@
import { or } from "truth-helpers";
import replaceEmoji from "discourse/helpers/replace-emoji";
import SectionLink from "./section-link";
const SidebarMoreSectionLink = <template>
<SectionLink
@badgeText={{@sectionLink.badgeText}}
@content={{replaceEmoji @sectionLink.text}}
@currentWhen={{@sectionLink.currentWhen}}
@href={{or @sectionLink.href @sectionLink.value}}
@linkName={{@sectionLink.name}}
@model={{@sectionLink.model}}
@models={{@sectionLink.models}}
@prefixType="icon"
@prefixValue={{@sectionLink.prefixValue}}
@query={{@sectionLink.query}}
@route={{@sectionLink.route}}
@shouldDisplay={{@sectionLink.shouldDisplay}}
@suffixCSSClass={{@sectionLink.suffixCSSClass}}
@suffixType={{@sectionLink.suffixType}}
@suffixValue={{@sectionLink.suffixValue}}
@title={{@sectionLink.title}}
/>
</template>;
export default SidebarMoreSectionLink;

View File

@ -1,18 +0,0 @@
<Sidebar::SectionLink
@badgeText={{@sectionLink.badgeText}}
@content={{replace-emoji @sectionLink.text}}
@currentWhen={{@sectionLink.currentWhen}}
@href={{or @sectionLink.href @sectionLink.value}}
@linkName={{@sectionLink.name}}
@model={{@sectionLink.model}}
@models={{@sectionLink.models}}
@prefixType="icon"
@prefixValue={{@sectionLink.prefixValue}}
@query={{@sectionLink.query}}
@route={{@sectionLink.route}}
@shouldDisplay={{@sectionLink.shouldDisplay}}
@suffixCSSClass={{@sectionLink.suffixCSSClass}}
@suffixType={{@sectionLink.suffixType}}
@suffixValue={{@sectionLink.suffixValue}}
@title={{@sectionLink.title}}
/>

View File

@ -1,9 +1,16 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
import { service } from "@ember/service";
import { isEmpty } from "@ember/utils";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import { bind } from "discourse-common/utils/decorators";
import MoreSectionLink from "./more-section-link";
import SectionLinkButton from "./section-link-button";
export default class SidebarMoreSectionLinks extends Component {
@service router;
@ -93,7 +100,7 @@ export default class SidebarMoreSectionLinks extends Component {
}
#setActiveSectionLink() {
const activeSectionLink = this.args.sectionLinks.find((sectionLink) => {
this.activeSectionLink = this.args.sectionLinks.find((sectionLink) => {
const args = [sectionLink.route];
if (sectionLink.model) {
@ -108,7 +115,56 @@ export default class SidebarMoreSectionLinks extends Component {
return this.router.isActive(...args) && sectionLink;
});
this.activeSectionLink = activeSectionLink;
}
<template>
{{#if this.activeSectionLink}}
<MoreSectionLink @sectionLink={{this.activeSectionLink}} />
{{/if}}
<li class="sidebar-section-link-wrapper">
<button
{{on "click" this.toggleSectionLinks}}
aria-expanded={{if this.open "true" "false"}}
class="sidebar-section-link sidebar-row sidebar-more-section-links-details-summary --link-button"
>
<span class="sidebar-section-link-prefix icon">
{{icon "ellipsis-v"}}
</span>
<span class="sidebar-section-link-content-text">
{{i18n "sidebar.more"}}
</span>
</button>
</li>
{{#if this.open}}
<div class="sidebar-more-section-links-details">
<div
{{didInsert this.registerClickListener}}
{{willDestroy this.unregisterClickListener}}
class="sidebar-more-section-links-details-content-wrapper"
>
<div class="sidebar-more-section-links-details-content">
<ul class="sidebar-more-section-links-details-content-main">
{{#each this.sectionLinks as |sectionLink|}}
<MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
</ul>
{{#if @moreButtonAction}}
<div class="sidebar-more-section-links-details-content-footer">
<SectionLinkButton
@action={{@moreButtonAction}}
@icon={{@moreButtonIcon}}
@text={{@moreButtonText}}
@name="customize"
/>
</div>
{{/if}}
</div>
</div>
</div>
{{/if}}
</template>
}

View File

@ -1,48 +0,0 @@
{{#if this.activeSectionLink}}
<Sidebar::MoreSectionLink @sectionLink={{this.activeSectionLink}} />
{{/if}}
<li class="sidebar-section-link-wrapper">
<button
class="sidebar-section-link sidebar-row sidebar-more-section-links-details-summary --link-button"
aria-expanded={{if this.open "true" "false"}}
{{on "click" this.toggleSectionLinks}}
>
<span class="sidebar-section-link-prefix icon">
{{d-icon "ellipsis-v"}}
</span>
<span class="sidebar-section-link-content-text">
{{i18n "sidebar.more"}}
</span>
</button>
</li>
{{#if this.open}}
<div class="sidebar-more-section-links-details">
<div
class="sidebar-more-section-links-details-content-wrapper"
{{did-insert this.registerClickListener}}
{{will-destroy this.unregisterClickListener}}
>
<div class="sidebar-more-section-links-details-content">
<ul class="sidebar-more-section-links-details-content-main">
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
</ul>
{{#if @moreButtonAction}}
<div class="sidebar-more-section-links-details-content-footer">
<Sidebar::SectionLinkButton
@action={{@moreButtonAction}}
@icon={{@moreButtonIcon}}
@text={{@moreButtonText}}
@name="customize"
/>
</div>
{{/if}}
</div>
</div>
</div>
{{/if}}

View File

@ -0,0 +1,168 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { fn, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
import withEventValue from "discourse/helpers/with-event-value";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import discourseLater from "discourse-common/lib/later";
import IconPicker from "select-kit/components/icon-picker";
export default class SectionFormLink extends Component {
@service site;
@tracked dragCssClass;
dragCount = 0;
isAboveElement(event) {
event.preventDefault();
const target = event.currentTarget;
const domRect = target.getBoundingClientRect();
return event.offsetY < domRect.height / 2;
}
@action
dragHasStarted(event) {
event.dataTransfer.effectAllowed = "move";
this.args.setDraggedLinkCallback(this.args.link);
this.dragCssClass = "dragging";
}
@action
dragOver(event) {
event.preventDefault();
if (this.dragCssClass !== "dragging") {
if (this.isAboveElement(event)) {
this.dragCssClass = "drag-above";
} else {
this.dragCssClass = "drag-below";
}
}
}
@action
dragEnter() {
this.dragCount++;
}
@action
dragLeave() {
this.dragCount--;
if (
this.dragCount === 0 &&
(this.dragCssClass === "drag-above" || this.dragCssClass === "drag-below")
) {
discourseLater(() => {
this.dragCssClass = null;
}, 10);
}
}
@action
dropItem(event) {
event.stopPropagation();
this.dragCount = 0;
this.args.reorderCallback(this.args.link, this.isAboveElement(event));
this.dragCssClass = null;
}
@action
dragEnd() {
this.dragCount = 0;
this.dragCssClass = null;
}
<template>
<div
{{on "dragstart" this.dragHasStarted}}
{{on "dragover" this.dragOver}}
{{on "dragenter" this.dragEnter}}
{{on "dragleave" this.dragLeave}}
{{on "dragend" this.dragEnd}}
{{on "drop" this.dropItem}}
role="row"
data-row-id={{@link.objectId}}
draggable="true"
class={{concatClass
"sidebar-section-form-link"
"row-wrapper"
this.dragCssClass
}}
>
{{#if this.site.desktopView}}
<div class="draggable" data-link-name={{@link.name}}>
{{icon "grip-lines"}}
</div>
{{/if}}
<div class="input-group" role="cell">
<IconPicker
@name="icon"
@value={{@link.icon}}
@options={{hash
maximum=1
caretDownIcon="caret-down"
caretUpIcon="caret-up"
icons=@link.icon
}}
@onlyAvailable={{true}}
@onChange={{fn (mut @link.icon)}}
aria-label={{i18n "sidebar.sections.custom.links.icon.label"}}
class={{@link.iconCssClass}}
/>
{{#if @link.invalidIconMessage}}
<div class="icon warning" role="alert" aria-live="assertive">
{{@link.invalidIconMessage}}
</div>
{{/if}}
</div>
<div class="input-group" role="cell">
<Input
{{on "input" (withEventValue (fn (mut @link.name)))}}
@type="text"
@value={{@link.name}}
name="link-name"
aria-label={{i18n "sidebar.sections.custom.links.name.label"}}
class={{@link.nameCssClass}}
/>
{{#if @link.invalidNameMessage}}
<div role="alert" aria-live="assertive" class="name warning">
{{@link.invalidNameMessage}}
</div>
{{/if}}
</div>
<div class="input-group" role="cell">
<Input
{{on "input" (withEventValue (fn (mut @link.value)))}}
@type="text"
@value={{@link.value}}
name="link-url"
aria-label={{i18n "sidebar.sections.custom.links.value.label"}}
class={{@link.valueCssClass}}
/>
{{#if @link.invalidValueMessage}}
<div role="alert" aria-live="assertive" class="value warning">
{{@link.invalidValueMessage}}
</div>
{{/if}}
</div>
<DButton
@icon="trash-alt"
@action={{fn @deleteLink @link}}
@title="sidebar.sections.custom.links.delete"
role="cell"
class="btn-flat delete-link"
/>
</div>
</template>
}

View File

@ -1,80 +0,0 @@
<div
class={{concat-class
"sidebar-section-form-link"
"row-wrapper"
this.dragCssClass
}}
draggable="true"
{{on "dragstart" this.dragHasStarted}}
{{on "dragover" this.dragOver}}
{{on "dragenter" this.dragEnter}}
{{on "dragleave" this.dragLeave}}
{{on "dragend" this.dragEnd}}
{{on "drop" this.dropItem}}
role="row"
data-row-id={{@link.objectId}}
>
{{#if this.site.desktopView}}
<div class="draggable" data-link-name={{@link.name}}>
{{d-icon "grip-lines"}}
</div>
{{/if}}
<div class="input-group" role="cell">
<IconPicker
@name="icon"
@value={{@link.icon}}
@options={{hash
maximum=1
caretDownIcon="caret-down"
caretUpIcon="caret-up"
icons=@link.icon
}}
class={{@link.iconCssClass}}
@onlyAvailable={{true}}
@onChange={{fn (mut @link.icon)}}
ariaLabel={{i18n "sidebar.sections.custom.links.icon.label"}}
/>
{{#if @link.invalidIconMessage}}
<div class="icon warning" role="alert" aria-live="assertive">
{{@link.invalidIconMessage}}
</div>
{{/if}}
</div>
<div class="input-group" role="cell">
<Input
name="link-name"
@type="text"
@value={{@link.name}}
class={{@link.nameCssClass}}
ariaLabel={{i18n "sidebar.sections.custom.links.name.label"}}
{{on "input" (with-event-value (fn (mut @link.name)))}}
/>
{{#if @link.invalidNameMessage}}
<div class="name warning" role="alert" aria-live="assertive">
{{@link.invalidNameMessage}}
</div>
{{/if}}
</div>
<div class="input-group" role="cell">
<Input
name="link-url"
@type="text"
@value={{@link.value}}
class={{@link.valueCssClass}}
ariaLabel={{i18n "sidebar.sections.custom.links.value.label"}}
{{on "input" (with-event-value (fn (mut @link.value)))}}
/>
{{#if @link.invalidValueMessage}}
<div class="value warning" role="alert" aria-live="assertive">
{{@link.invalidValueMessage}}
</div>
{{/if}}
</div>
<DButton
@icon="trash-alt"
@action={{fn @deleteLink @link}}
@title="sidebar.sections.custom.links.delete"
class="btn-flat delete-link"
role="cell"
/>
</div>

View File

@ -1,69 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { service } from "@ember/service";
import discourseLater from "discourse-common/lib/later";
export default class SectionFormLink extends Component {
@service site;
@tracked dragCssClass;
dragCount = 0;
isAboveElement(event) {
event.preventDefault();
const target = event.currentTarget;
const domRect = target.getBoundingClientRect();
return event.offsetY < domRect.height / 2;
}
@action
dragHasStarted(event) {
event.dataTransfer.effectAllowed = "move";
this.args.setDraggedLinkCallback(this.args.link);
this.dragCssClass = "dragging";
}
@action
dragOver(event) {
event.preventDefault();
if (this.dragCssClass !== "dragging") {
if (this.isAboveElement(event)) {
this.dragCssClass = "drag-above";
} else {
this.dragCssClass = "drag-below";
}
}
}
@action
dragEnter() {
this.dragCount++;
}
@action
dragLeave() {
this.dragCount--;
if (
this.dragCount === 0 &&
(this.dragCssClass === "drag-above" || this.dragCssClass === "drag-below")
) {
discourseLater(() => {
this.dragCssClass = null;
}, 10);
}
}
@action
dropItem(event) {
event.stopPropagation();
this.dragCount = 0;
this.args.reorderCallback(this.args.link, this.isAboveElement(event));
this.dragCssClass = null;
}
@action
dragEnd() {
this.dragCount = 0;
this.dragCssClass = null;
}
}

View File

@ -0,0 +1,21 @@
import DButton from "discourse/components/d-button";
const SidebarSectionHeader = <template>
{{#if @collapsable}}
<DButton
@title="sidebar.toggle_section"
@action={{@toggleSectionDisplay}}
aria-controls={{@sidebarSectionContentID}}
aria-expanded={{if @isExpanded "true" "false"}}
class="sidebar-section-header sidebar-section-header-collapsable btn-transparent"
>
{{yield}}
</DButton>
{{else}}
<span class="sidebar-section-header">
{{yield}}
</span>
{{/if}}
</template>;
export default SidebarSectionHeader;

View File

@ -1,15 +0,0 @@
{{#if @collapsable}}
<DButton
@title="sidebar.toggle_section"
@action={{@toggleSectionDisplay}}
aria-controls={{@sidebarSectionContentID}}
aria-expanded={{if @isExpanded "true" "false"}}
class="sidebar-section-header sidebar-section-header-collapsable btn-transparent"
>
{{yield}}
</DButton>
{{else}}
<span class="sidebar-section-header">
{{yield}}
</span>
{{/if}}

View File

@ -0,0 +1,22 @@
import { on } from "@ember/modifier";
import icon from "discourse-common/helpers/d-icon";
const SidebarSectionLinkButton = <template>
<div class="sidebar-section-link-wrapper">
<button
{{on "click" @action}}
type="button"
class="sidebar-section-link sidebar-row --link-button"
>
<span class="sidebar-section-link-prefix icon">
{{icon @icon}}
</span>
<span class="sidebar-section-link-content-text">
{{@text}}
</span>
</button>
</div>
</template>;
export default SidebarSectionLinkButton;

View File

@ -1,15 +0,0 @@
<div class="sidebar-section-link-wrapper">
<button
type="button"
class="sidebar-section-link sidebar-row --link-button"
{{on "click" @action}}
>
<span class="sidebar-section-link-prefix icon">
{{d-icon @icon}}
</span>
<span class="sidebar-section-link-content-text">
{{@text}}
</span>
</button>
</div>

View File

@ -0,0 +1,74 @@
import Component from "@glimmer/component";
import { concat } from "@ember/helper";
import { htmlSafe } from "@ember/template";
import { eq } from "truth-helpers";
import { isHex } from "discourse/components/sidebar/section-link";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse-common/helpers/d-icon";
export default class SidebarSectionLinkPrefix extends Component {
get prefixValue() {
if (!this.args.prefixType && !this.args.prefixValue) {
return;
}
switch (this.args.prefixType) {
case "span":
let hexValues = this.args.prefixValue;
hexValues = hexValues.reduce((acc, color) => {
const hexCode = isHex(color);
if (hexCode) {
acc.push(`#${hexCode} 50%`);
}
return acc;
}, []);
if (hexValues.length === 1) {
hexValues.push(hexValues[0]);
}
return hexValues.join(", ");
default:
return this.args.prefixValue;
}
}
<template>
{{#if @prefixType}}
<span
style={{if @prefixColor (htmlSafe (concat "color: " @prefixColor))}}
class={{concatClass
"sidebar-section-link-prefix"
@prefixType
@prefixCSSClass
}}
>
{{#if (eq @prefixType "image")}}
<img src={{this.prefixValue}} class="prefix-image" />
{{else if (eq @prefixType "text")}}
<span class="prefix-text">
{{this.prefixValue}}
</span>
{{else if (eq @prefixType "icon")}}
{{icon this.prefixValue class="prefix-icon"}}
{{else if (eq @prefixType "span")}}
<span
style={{htmlSafe
(concat
"background: linear-gradient(90deg, " this.prefixValue ")"
)
}}
class="prefix-span"
></span>
{{/if}}
{{#if @prefixBadge}}
{{icon @prefixBadge class="prefix-badge"}}
{{/if}}
</span>
{{/if}}
</template>
}

View File

@ -1,32 +0,0 @@
{{#if @prefixType}}
<span
class={{concat-class
"sidebar-section-link-prefix"
@prefixType
@prefixCSSClass
}}
style={{if @prefixColor (html-safe (concat "color: " @prefixColor))}}
>
{{#if (eq @prefixType "image")}}
<img src={{this.prefixValue}} class="prefix-image" />
{{else if (eq @prefixType "text")}}
<span class="prefix-text">
{{this.prefixValue}}
</span>
{{else if (eq @prefixType "icon")}}
{{d-icon this.prefixValue class="prefix-icon"}}
{{else if (eq @prefixType "span")}}
<span
style={{html-safe
(concat "background: linear-gradient(90deg, " this.prefixValue ")")
}}
class="prefix-span"
></span>
{{/if}}
{{#if @prefixBadge}}
{{d-icon @prefixBadge class="prefix-badge"}}
{{/if}}
</span>
{{/if}}

View File

@ -1,33 +0,0 @@
import Component from "@glimmer/component";
import { isHex } from "discourse/components/sidebar/section-link";
export default class extends Component {
get prefixValue() {
if (!this.args.prefixType && !this.args.prefixValue) {
return;
}
switch (this.args.prefixType) {
case "span":
let hexValues = this.args.prefixValue;
hexValues = hexValues.reduce((acc, color) => {
const hexCode = isHex(color);
if (hexCode) {
acc.push(`#${hexCode} 50%`);
}
return acc;
}, []);
if (hexValues.length === 1) {
hexValues.push(hexValues[0]);
}
return hexValues.join(", ");
default:
return this.args.prefixValue;
}
}
}

View File

@ -0,0 +1,199 @@
import Component from "@glimmer/component";
import { hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { LinkTo } from "@ember/routing";
import { service } from "@ember/service";
import { eq, or } from "truth-helpers";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse-common/helpers/d-icon";
import deprecated from "discourse-common/lib/deprecated";
import SectionLinkPrefix from "./section-link-prefix";
/**
* Checks if a given string is a valid color hex code.
*
* @param {String|undefined} input Input string to check if it is a valid color hex code. Can be in the form of "FFFFFF" or "#FFFFFF" or "FFF" or "#FFF".
* @returns {String|undefined} Returns the matching color hex code without the leading `#` if it is valid, otherwise returns undefined. Example: "FFFFFF" or "FFF".
*/
export function isHex(input) {
const match = input?.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/);
if (match) {
return match[1];
} else {
return;
}
}
export default class SectionLink extends Component {
@service currentUser;
constructor() {
super(...arguments);
this.args.didInsert?.();
}
willDestroy() {
super.willDestroy(...arguments);
this.args.willDestroy?.();
}
get shouldDisplay() {
if (this.args.shouldDisplay === undefined) {
return true;
}
return this.args.shouldDisplay;
}
get linkClass() {
let classNames = ["sidebar-section-link", "sidebar-row"];
if (this.args.linkClass) {
classNames.push(this.args.linkClass);
}
if (this.args.class) {
deprecated("SectionLink's @class arg has been renamed to @linkClass", {
id: "discourse.section-link-class-arg",
since: "3.2.0.beta4",
dropFrom: "3.3.0.beta1",
});
classNames.push(this.args.class);
}
return classNames.join(" ");
}
get target() {
return this.currentUser?.user_option?.external_links_in_new_tab &&
this.isExternal
? "_blank"
: "_self";
}
get isExternal() {
return (
this.args.href &&
new URL(this.args.href, window.location.href).origin !==
window.location.origin
);
}
get models() {
if (this.args.model) {
return [this.args.model];
}
if (this.args.models) {
return this.args.models;
}
return [];
}
get prefixColor() {
const hexCode = isHex(this.args.prefixColor);
if (hexCode) {
return `#${hexCode}`;
} else {
return;
}
}
<template>
{{#if this.shouldDisplay}}
<li
data-list-item-name={{@linkName}}
class="sidebar-section-link-wrapper"
...attributes
>
{{#if @href}}
<a
href={{@href}}
rel="noopener noreferrer"
target={{this.target}}
title={{@title}}
data-link-name={{@linkName}}
class={{this.linkClass}}
>
<SectionLinkPrefix
@prefixType={{@prefixType}}
@prefixValue={{@prefixValue}}
@prefixCSSClass={{@prefixCSSClass}}
@prefixColor={{this.prefixColor}}
@prefixBadge={{@prefixBadge}}
/>
<span class="sidebar-section-link-content-text">
{{@content}}
</span>
</a>
{{else}}
<LinkTo
@route={{@route}}
@query={{or @query (hash)}}
@models={{this.models}}
@current-when={{@currentWhen}}
title={{@title}}
data-link-name={{@linkName}}
class={{this.linkClass}}
>
<SectionLinkPrefix
@prefixType={{@prefixType}}
@prefixValue={{@prefixValue}}
@prefixCSSClass={{@prefixCSSClass}}
@prefixColor={{this.prefixColor}}
@prefixBadge={{@prefixBadge}}
/>
<span
class={{concatClass
"sidebar-section-link-content-text"
@contentCSSClass
}}
>
{{@content}}
<@contentComponent />
</span>
{{#if @badgeText}}
<span class="sidebar-section-link-content-badge">
{{@badgeText}}
</span>
{{/if}}
{{#if @suffixValue}}
<span
class={{concatClass
"sidebar-section-link-suffix"
@suffixType
@suffixCSSClass
}}
>
{{#if (eq @suffixType "icon")}}
{{icon @suffixValue}}
{{/if}}
</span>
{{/if}}
{{#if @hoverValue}}
<span class="sidebar-section-link-hover">
<button
{{on "click" @hoverAction}}
type="button"
title={{@hoverTitle}}
class="sidebar-section-hover-button"
>
{{#if (eq @hoverType "icon")}}
{{icon @hoverValue class="hover-icon"}}
{{/if}}
</button>
</span>
{{/if}}
</LinkTo>
{{/if}}
</li>
{{/if}}
</template>
}

View File

@ -1,94 +0,0 @@
{{#if this.shouldDisplay}}
<li
class="sidebar-section-link-wrapper"
data-list-item-name={{@linkName}}
...attributes
>
{{#if @href}}
<a
href={{@href}}
rel="noopener noreferrer"
target={{this.target}}
title={{@title}}
data-link-name={{@linkName}}
class={{this.linkClass}}
>
<Sidebar::SectionLinkPrefix
@prefixType={{@prefixType}}
@prefixValue={{@prefixValue}}
@prefixCSSClass={{@prefixCSSClass}}
@prefixColor={{this.prefixColor}}
@prefixBadge={{@prefixBadge}}
/>
<span class="sidebar-section-link-content-text">
{{@content}}
</span>
</a>
{{else}}
<LinkTo
@route={{@route}}
@query={{or @query (hash)}}
@models={{this.models}}
@current-when={{@currentWhen}}
title={{@title}}
data-link-name={{@linkName}}
class={{this.linkClass}}
>
<Sidebar::SectionLinkPrefix
@prefixType={{@prefixType}}
@prefixValue={{@prefixValue}}
@prefixCSSClass={{@prefixCSSClass}}
@prefixColor={{this.prefixColor}}
@prefixBadge={{@prefixBadge}}
/>
<span
class={{concat-class
"sidebar-section-link-content-text"
@contentCSSClass
}}
>
{{@content}}
{{@contentComponent}}
</span>
{{#if @badgeText}}
<span class="sidebar-section-link-content-badge">
{{@badgeText}}
</span>
{{/if}}
{{#if @suffixValue}}
<span
class={{concat-class
"sidebar-section-link-suffix"
@suffixType
@suffixCSSClass
}}
>
{{#if (eq @suffixType "icon")}}
{{d-icon @suffixValue}}
{{/if}}
</span>
{{/if}}
{{#if @hoverValue}}
<span class="sidebar-section-link-hover">
<button
type="button"
title={{@hoverTitle}}
class="sidebar-section-hover-button"
{{on "click" @hoverAction}}
>
{{#if (eq @hoverType "icon")}}
{{d-icon @hoverValue class="hover-icon"}}
{{/if}}
</button>
</span>
{{/if}}
</LinkTo>
{{/if}}
</li>
{{/if}}

View File

@ -1,96 +0,0 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import deprecated from "discourse-common/lib/deprecated";
/**
* Checks if a given string is a valid color hex code.
*
* @param {String|undefined} input Input string to check if it is a valid color hex code. Can be in the form of "FFFFFF" or "#FFFFFF" or "FFF" or "#FFF".
* @returns {String|undefined} Returns the matching color hex code without the leading `#` if it is valid, otherwise returns undefined. Example: "FFFFFF" or "FFF".
*/
export function isHex(input) {
const match = input?.match(/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/);
if (match) {
return match[1];
} else {
return;
}
}
export default class SectionLink extends Component {
@service currentUser;
constructor() {
super(...arguments);
this.args.didInsert?.();
}
willDestroy() {
super.willDestroy(...arguments);
this.args.willDestroy?.();
}
get shouldDisplay() {
if (this.args.shouldDisplay === undefined) {
return true;
}
return this.args.shouldDisplay;
}
get linkClass() {
let classNames = ["sidebar-section-link", "sidebar-row"];
if (this.args.linkClass) {
classNames.push(this.args.linkClass);
}
if (this.args.class) {
deprecated("SectionLink's @class arg has been renamed to @linkClass", {
id: "discourse.section-link-class-arg",
since: "3.2.0.beta4",
dropFrom: "3.3.0.beta1",
});
classNames.push(this.args.class);
}
return classNames.join(" ");
}
get target() {
return this.currentUser?.user_option?.external_links_in_new_tab &&
this.isExternal
? "_blank"
: "_self";
}
get isExternal() {
return (
this.args.href &&
new URL(this.args.href, window.location.href).origin !==
window.location.origin
);
}
get models() {
if (this.args.model) {
return [this.args.model];
}
if (this.args.models) {
return this.args.models;
}
return [];
}
get prefixColor() {
const hexCode = isHex(this.args.prefixColor);
if (hexCode) {
return `#${hexCode}`;
} else {
return;
}
}
}

View File

@ -0,0 +1,9 @@
const SidebarSectionMessage = <template>
<div class="sidebar-section-message-wrapper sidebar-row">
<div class="sidebar-section-message">
{{yield}}
</div>
</div>
</template>;
export default SidebarSectionMessage;

View File

@ -1,5 +0,0 @@
<div class="sidebar-section-message-wrapper sidebar-row">
<div class="sidebar-section-message">
{{yield}}
</div>
</div>

View File

@ -0,0 +1,177 @@
import Component from "@glimmer/component";
import { hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import { bind } from "discourse-common/utils/decorators";
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
import DTooltip from "float-kit/components/d-tooltip";
import SectionHeader from "./section-header";
export default class SidebarSection extends Component {
@service keyValueStore;
@service sidebarState;
sidebarSectionContentID = `sidebar-section-content-${this.args.sectionName}`;
collapsedSidebarSectionKey = `sidebar-section-${this.args.sectionName}-collapsed`;
willDestroy() {
super.willDestroy(...arguments);
this.args.willDestroy?.();
}
get isCollapsed() {
if (!this.args.collapsable) {
return false;
}
if (
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === undefined
) {
return this.args.collapsedByDefault;
}
return (
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === "true"
);
}
@bind
setExpandedState() {
if (this.isCollapsed) {
this.sidebarState.collapseSection(this.args.sectionName);
} else {
this.sidebarState.expandSection(this.args.sectionName);
}
}
get displaySectionContent() {
return !this.sidebarState.collapsedSections.has(
this.collapsedSidebarSectionKey
);
}
@action
toggleSectionDisplay() {
if (this.displaySectionContent) {
this.sidebarState.collapseSection(this.args.sectionName);
} else {
this.sidebarState.expandSection(this.args.sectionName);
}
// remove focus from the toggle, but only on click
if (!event.key) {
document.activeElement.blur();
}
}
@action
handleMultipleHeaderActions(id) {
this.args.headerActions
.find((headerAction) => headerAction.id === id)
.action();
}
get headerCaretIcon() {
return this.displaySectionContent ? "angle-down" : "angle-right";
}
get isSingleHeaderAction() {
return this.args.headerActions?.length === 1;
}
get isMultipleHeaderActions() {
return this.args.headerActions?.length > 1;
}
get displaySection() {
if (this.args.displaySection === undefined) {
return true;
}
return this.args.displaySection;
}
<template>
{{#if this.displaySection}}
<div
{{didInsert this.setExpandedState}}
data-section-name={{@sectionName}}
class="sidebar-section-wrapper sidebar-section"
...attributes
>
{{#unless @hideSectionHeader}}
<div class="sidebar-section-header-wrapper sidebar-row">
<SectionHeader
@collapsable={{@collapsable}}
@sidebarSectionContentID={{this.sidebarSectionContentID}}
@toggleSectionDisplay={{this.toggleSectionDisplay}}
@isExpanded={{this.displaySectionContent}}
>
{{#if @collapsable}}
<span class="sidebar-section-header-caret">
{{icon this.headerCaretIcon}}
</span>
{{/if}}
<span class="sidebar-section-header-text">
{{@headerLinkText}}
</span>
{{#if @indicatePublic}}
<DTooltip
@icon="globe"
class="sidebar-section-header-global-indicator"
>
<span
class="sidebar-section-header-global-indicator__content"
>
{{icon "shield-alt"}}
{{i18n "sidebar.sections.global_section"}}
</span>
</DTooltip>
{{/if}}
</SectionHeader>
{{#if this.isSingleHeaderAction}}
{{#each @headerActions as |headerAction|}}
<button
{{on "click" headerAction.action}}
type="button"
title={{headerAction.title}}
class="sidebar-section-header-button"
>
{{icon @headerActionsIcon}}
</button>
{{/each}}
{{/if}}
{{#if this.isMultipleHeaderActions}}
<DropdownSelectBox
@options={{hash
icon=@headerActionsIcon
placementStrategy="absolute"
}}
@content={{@headerActions}}
@onChange={{this.handleMultipleHeaderActions}}
class="sidebar-section-header-dropdown"
/>
{{/if}}
</div>
{{/unless}}
{{#if this.displaySectionContent}}
<ul
id={{this.sidebarSectionContentID}}
class="sidebar-section-content"
>
{{yield}}
</ul>
{{/if}}
</div>
{{/if}}
</template>
}

View File

@ -1,73 +0,0 @@
{{#if this.displaySection}}
<div
data-section-name={{@sectionName}}
class="sidebar-section-wrapper sidebar-section"
...attributes
{{did-insert this.setExpandedState}}
>
{{#unless @hideSectionHeader}}
<div class="sidebar-section-header-wrapper sidebar-row">
<Sidebar::SectionHeader
@collapsable={{@collapsable}}
@sidebarSectionContentID={{this.sidebarSectionContentID}}
@toggleSectionDisplay={{this.toggleSectionDisplay}}
@isExpanded={{this.displaySectionContent}}
>
{{#if @collapsable}}
<span class="sidebar-section-header-caret">
{{d-icon this.headerCaretIcon}}
</span>
{{/if}}
<span class="sidebar-section-header-text">
{{@headerLinkText}}
</span>
{{#if @indicatePublic}}
<DTooltip
@icon="globe"
class="sidebar-section-header-global-indicator"
>
<span
class="sidebar-section-header-global-indicator__content"
>{{d-icon "shield-alt"}}{{i18n
"sidebar.sections.global_section"
}}</span>
</DTooltip>
{{/if}}
</Sidebar::SectionHeader>
{{#if this.isSingleHeaderAction}}
{{#each @headerActions as |headerAction|}}
<button
type="button"
class="sidebar-section-header-button"
{{on "click" headerAction.action}}
title={{headerAction.title}}
>
{{d-icon @headerActionsIcon}}
</button>
{{/each}}
{{/if}}
{{#if this.isMultipleHeaderActions}}
<DropdownSelectBox
@options={{hash
icon=@headerActionsIcon
placementStrategy="absolute"
}}
@content={{@headerActions}}
@onChange={{this.handleMultipleHeaderActions}}
class="sidebar-section-header-dropdown"
/>
{{/if}}
</div>
{{/unless}}
{{#if this.displaySectionContent}}
<ul class="sidebar-section-content" id={{this.sidebarSectionContentID}}>
{{yield}}
</ul>
{{/if}}
</div>
{{/if}}

View File

@ -1,87 +0,0 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { bind } from "discourse-common/utils/decorators";
export default class SidebarSection extends Component {
@service keyValueStore;
@service sidebarState;
sidebarSectionContentID = `sidebar-section-content-${this.args.sectionName}`;
collapsedSidebarSectionKey = `sidebar-section-${this.args.sectionName}-collapsed`;
get isCollapsed() {
if (!this.args.collapsable) {
return false;
}
if (
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === undefined
) {
return this.args.collapsedByDefault;
}
return (
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === "true"
);
}
@bind
setExpandedState() {
if (this.isCollapsed) {
this.sidebarState.collapseSection(this.args.sectionName);
} else {
this.sidebarState.expandSection(this.args.sectionName);
}
}
get displaySectionContent() {
return !this.sidebarState.collapsedSections.has(
this.collapsedSidebarSectionKey
);
}
willDestroy() {
super.willDestroy(...arguments);
this.args.willDestroy?.();
}
@action
toggleSectionDisplay() {
if (this.displaySectionContent) {
this.sidebarState.collapseSection(this.args.sectionName);
} else {
this.sidebarState.expandSection(this.args.sectionName);
}
// remove focus from the toggle, but only on click
if (!event.key) {
document.activeElement.blur();
}
}
@action
handleMultipleHeaderActions(id) {
this.args.headerActions
.find((headerAction) => headerAction.id === id)
.action();
}
get headerCaretIcon() {
return this.displaySectionContent ? "angle-down" : "angle-right";
}
get isSingleHeaderAction() {
return this.args.headerActions?.length === 1;
}
get isMultipleHeaderActions() {
return this.args.headerActions?.length > 1;
}
get displaySection() {
if (this.args.displaySection === undefined) {
return true;
}
return this.args.displaySection;
}
}

View File

@ -0,0 +1,15 @@
import AnonymousSections from "./anonymous/sections";
import UserSections from "./user/sections";
const SidebarSections = <template>
{{#if @currentUser}}
<UserSections
@collapsableSections={{@collapsableSections}}
@panel={{@panel}}
/>
{{else}}
<AnonymousSections @collapsableSections={{@collapsableSections}} />
{{/if}}
</template>;
export default SidebarSections;

View File

@ -1,8 +0,0 @@
{{#if @currentUser}}
<Sidebar::User::Sections
@collapsableSections={{@collapsableSections}}
@panel={{@panel}}
/>
{{else}}
<Sidebar::Anonymous::Sections @collapsableSections={{@collapsableSections}} />
{{/if}}

View File

@ -1,13 +1,16 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { fn } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { defaultHomepage } from "discourse/lib/utilities";
import getURL from "discourse-common/lib/get-url";
export default class SwitchPanelButtons extends Component {
@service router;
@service sidebarState;
@tracked currentPanel;
@tracked isSwitching = false;
@ -40,4 +43,17 @@ export default class SwitchPanelButtons extends Component {
}
}
}
<template>
{{#each @buttons as |button|}}
<DButton
@action={{fn this.switchPanel button}}
@icon={{button.switchButtonIcon}}
@disabled={{this.isSwitching}}
@translatedLabel={{button.switchButtonLabel}}
data-key={{button.key}}
class="btn-default sidebar__panel-switch-button"
/>
{{/each}}
</template>
}

View File

@ -1,10 +0,0 @@
{{#each @buttons as |button|}}
<DButton
@action={{fn this.switchPanel button}}
@icon={{button.switchButtonIcon}}
@disabled={{this.isSwitching}}
@translatedLabel={{button.switchButtonLabel}}
data-key={{button.key}}
class="btn-default sidebar__panel-switch-button"
/>
{{/each}}

View File

@ -0,0 +1,124 @@
import { cached } from "@glimmer/tracking";
import { array, hash } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { hasDefaultSidebarCategories } from "discourse/lib/sidebar/helpers";
import Category from "discourse/models/category";
import i18n from "discourse-common/helpers/i18n";
import { debounce } from "discourse-common/utils/decorators";
import AllCategoriesSectionLink from "../common/all-categories-section-link";
import CommonCategoriesSection from "../common/categories-section";
import EditNavigationMenuCategoriesModal from "../edit-navigation-menu/categories-modal";
import Section from "../section";
import SectionLink from "../section-link";
export const REFRESH_COUNTS_APP_EVENT_NAME =
"sidebar:refresh-categories-section-counts";
export default class SidebarUserCategoriesSection extends CommonCategoriesSection {
@service appEvents;
@service currentUser;
@service modal;
@service router;
constructor() {
super(...arguments);
this.callbackId = this.topicTrackingState.onStateChange(() => {
this._refreshCounts();
});
this.appEvents.on(REFRESH_COUNTS_APP_EVENT_NAME, this, this._refreshCounts);
}
willDestroy() {
super.willDestroy(...arguments);
this.topicTrackingState.offStateChange(this.callbackId);
this.appEvents.off(
REFRESH_COUNTS_APP_EVENT_NAME,
this,
this._refreshCounts
);
}
// TopicTrackingState changes or plugins can trigger this function so we debounce to ensure we're not refreshing
// unnecessarily.
@debounce(300)
_refreshCounts() {
this.sectionLinks.forEach((sectionLink) => sectionLink.refreshCounts());
}
@cached
get categories() {
if (this.currentUser.sidebarCategoryIds?.length > 0) {
return Category.findByIds(this.currentUser.sidebarCategoryIds);
} else {
return this.topSiteCategories;
}
}
get shouldDisplayDefaultConfig() {
return this.currentUser.admin && !this.hasDefaultSidebarCategories;
}
get hasDefaultSidebarCategories() {
return hasDefaultSidebarCategories(this.siteSettings);
}
@action
showModal() {
this.modal.show(EditNavigationMenuCategoriesModal);
}
<template>
<Section
@sectionName="categories"
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
@headerActions={{array
(hash
action=this.showModal
title=(i18n "sidebar.sections.categories.header_action_title")
)
}}
@headerActionsIcon="pencil-alt"
@collapsable={{@collapsable}}
>
{{#each this.sectionLinks as |sectionLink|}}
<SectionLink
@route={{sectionLink.route}}
@query={{sectionLink.query}}
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@model={{sectionLink.model}}
@badgeText={{sectionLink.badgeText}}
@prefixBadge={{sectionLink.prefixBadge}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}}
data-category-id={{sectionLink.category.id}}
/>
{{/each}}
<AllCategoriesSectionLink />
{{#if this.shouldDisplayDefaultConfig}}
<SectionLink
@linkName="configure-default-navigation-menu-categories"
@content={{i18n "sidebar.sections.categories.configure_defaults"}}
@prefixType="icon"
@prefixValue="wrench"
@route="adminSiteSettingsCategory"
@model="sidebar"
@query={{hash filter="default_navigation_menu_categories"}}
/>
{{/if}}
</Section>
</template>
}

View File

@ -1,49 +0,0 @@
<Sidebar::Section
@sectionName="categories"
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
@headerActions={{array
(hash
action=this.showModal
title=(i18n "sidebar.sections.categories.header_action_title")
)
}}
@headerActionsIcon="pencil-alt"
@collapsable={{@collapsable}}
>
{{#if (gt this.sectionLinks.length 0)}}
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::SectionLink
@route={{sectionLink.route}}
@query={{sectionLink.query}}
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@model={{sectionLink.model}}
@badgeText={{sectionLink.badgeText}}
@prefixBadge={{sectionLink.prefixBadge}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}}
data-category-id={{sectionLink.category.id}}
/>
{{/each}}
{{/if}}
<Sidebar::Common::AllCategoriesSectionLink />
{{#if this.shouldDisplayDefaultConfig}}
<Sidebar::SectionLink
@linkName="configure-default-navigation-menu-categories"
@content={{i18n "sidebar.sections.categories.configure_defaults"}}
@prefixType="icon"
@prefixValue="wrench"
@route="adminSiteSettingsCategory"
@model="sidebar"
@query={{hash filter="default_navigation_menu_categories"}}
/>
{{/if}}
</Sidebar::Section>

View File

@ -1,71 +0,0 @@
import { cached } from "@glimmer/tracking";
import { action } from "@ember/object";
import { service } from "@ember/service";
import SidebarCommonCategoriesSection from "discourse/components/sidebar/common/categories-section";
import SidebarEditNavigationMenuCategoriesModal from "discourse/components/sidebar/edit-navigation-menu/categories-modal";
import { hasDefaultSidebarCategories } from "discourse/lib/sidebar/helpers";
import Category from "discourse/models/category";
import { debounce } from "discourse-common/utils/decorators";
export const REFRESH_COUNTS_APP_EVENT_NAME =
"sidebar:refresh-categories-section-counts";
export default class SidebarUserCategoriesSection extends SidebarCommonCategoriesSection {
@service appEvents;
@service currentUser;
@service modal;
@service router;
constructor() {
super(...arguments);
this.callbackId = this.topicTrackingState.onStateChange(() => {
this._refreshCounts();
});
this.appEvents.on(REFRESH_COUNTS_APP_EVENT_NAME, this, this._refreshCounts);
}
willDestroy() {
super.willDestroy(...arguments);
this.topicTrackingState.offStateChange(this.callbackId);
this.appEvents.off(
REFRESH_COUNTS_APP_EVENT_NAME,
this,
this._refreshCounts
);
}
// TopicTrackingState changes or plugins can trigger this function so we debounce to ensure we're not refreshing
// unnecessarily.
@debounce(300)
_refreshCounts() {
this.sectionLinks.forEach((sectionLink) => {
sectionLink.refreshCounts();
});
}
@cached
get categories() {
if (this.currentUser.sidebarCategoryIds?.length > 0) {
return Category.findByIds(this.currentUser.sidebarCategoryIds);
} else {
return this.topSiteCategories;
}
}
get shouldDisplayDefaultConfig() {
return this.currentUser.admin && !this.hasDefaultSidebarCategories;
}
get hasDefaultSidebarCategories() {
return hasDefaultSidebarCategories(this.siteSettings);
}
@action
showModal() {
this.modal.show(SidebarEditNavigationMenuCategoriesModal);
}
}

View File

@ -1,22 +1,21 @@
import SidebarCustomSection from "discourse/components/sidebar/common/custom-sections";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse-common/utils/decorators";
import SidebarCustomSection from "../common/custom-sections";
export default class SidebarUserCustomSections extends SidebarCustomSection {
constructor() {
super(...arguments);
this.messageBus.subscribe("/refresh-sidebar-sections", this._refresh);
}
willDestroy() {
super.willDestroy(...arguments);
this.messageBus.unsubscribe("/refresh-sidebar-sections");
}
@bind
_refresh() {
return ajax("/sidebar_sections.json", {}).then((json) => {
this.currentUser.set("sidebar_sections", json.sidebar_sections);
});
async _refresh() {
const json = await ajax("/sidebar_sections.json", {});
this.currentUser.set("sidebar_sections", json.sidebar_sections);
}
}

View File

@ -1,10 +1,15 @@
import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { getOwner } from "@ember/application";
import { array, fn, hash } from "@ember/helper";
import { getOwner } from "@ember/owner";
import { service } from "@ember/service";
import routeAction from "discourse/helpers/route-action";
import GroupMessageSectionLink from "discourse/lib/sidebar/user/messages-section/group-message-section-link";
import PersonalMessageSectionLink from "discourse/lib/sidebar/user/messages-section/personal-message-section-link";
import i18n from "discourse-common/helpers/i18n";
import { bind } from "discourse-common/utils/decorators";
import Section from "../section";
import SectionLink from "../section-link";
export const INBOX = "inbox";
export const UNREAD = "unread";
@ -28,6 +33,8 @@ export default class SidebarUserMessagesSection extends Component {
@service currentUser;
@service router;
_pmTopicTrackingStateKey = "messages-section";
constructor() {
super(...arguments);
@ -37,21 +44,12 @@ export default class SidebarUserMessagesSection extends Component {
this._refreshSectionLinksDisplayState
);
this._pmTopicTrackingStateKey = "messages-section";
this.pmTopicTrackingState.onStateChange(
this._pmTopicTrackingStateKey,
this._refreshSectionLinkCounts
);
}
@bind
_refreshSectionLinkCounts() {
for (const sectionLink of this.allSectionLinks) {
sectionLink.refreshCount();
}
}
willDestroy() {
super.willDestroy(...arguments);
@ -67,6 +65,13 @@ export default class SidebarUserMessagesSection extends Component {
);
}
@bind
_refreshSectionLinkCounts() {
for (const sectionLink of this.allSectionLinks) {
sectionLink.refreshCount();
}
}
_refreshSectionLinksDisplayState() {
const currentRouteName = this.router.currentRoute.name;
const currentRouteParentName = this.router.currentRoute.parent.name;
@ -144,4 +149,53 @@ export default class SidebarUserMessagesSection extends Component {
...this.personalMessagesSectionLinks,
];
}
<template>
<Section
@sectionName="messages"
@headerActionIcon="plus"
@headerActions={{array
(hash
action=(fn (routeAction "composePrivateMessage") null null)
title=(i18n "sidebar.sections.messages.header_action_title")
)
}}
@headerActionsIcon="plus"
@headerLinkText={{i18n "sidebar.sections.messages.header_link_text"}}
@collapsable={{@collapsable}}
>
{{#each
this.personalMessagesSectionLinks
as |personalMessageSectionLink|
}}
{{#if personalMessageSectionLink.shouldDisplay}}
<SectionLink
@linkName={{personalMessageSectionLink.name}}
@linkClass={{personalMessageSectionLink.class}}
@route={{personalMessageSectionLink.route}}
@model={{personalMessageSectionLink.model}}
@prefixType={{personalMessageSectionLink.prefixType}}
@prefixValue={{personalMessageSectionLink.prefixValue}}
@currentWhen={{personalMessageSectionLink.currentWhen}}
@content={{personalMessageSectionLink.text}}
/>
{{/if}}
{{/each}}
{{#each this.groupMessagesSectionLinks as |groupMessageSectionLink|}}
{{#if groupMessageSectionLink.shouldDisplay}}
<SectionLink
@linkName={{groupMessageSectionLink.name}}
@linkClass={{groupMessageSectionLink.class}}
@route={{groupMessageSectionLink.route}}
@prefixType={{groupMessageSectionLink.prefixType}}
@prefixValue={{groupMessageSectionLink.prefixValue}}
@models={{groupMessageSectionLink.models}}
@currentWhen={{groupMessageSectionLink.currentWhen}}
@content={{groupMessageSectionLink.text}}
/>
{{/if}}
{{/each}}
</Section>
</template>
}

View File

@ -1,45 +0,0 @@
<Sidebar::Section
@sectionName="messages"
@headerActionIcon="plus"
@headerActions={{array
(hash
action=(fn (route-action "composePrivateMessage") null null)
title=(i18n "sidebar.sections.messages.header_action_title")
)
}}
@headerActionsIcon="plus"
@headerLinkText={{i18n "sidebar.sections.messages.header_link_text"}}
@collapsable={{@collapsable}}
>
{{#each this.personalMessagesSectionLinks as |personalMessageSectionLink|}}
{{#if personalMessageSectionLink.shouldDisplay}}
<Sidebar::SectionLink
@linkName={{personalMessageSectionLink.name}}
@linkClass={{personalMessageSectionLink.class}}
@route={{personalMessageSectionLink.route}}
@model={{personalMessageSectionLink.model}}
@prefixType={{personalMessageSectionLink.prefixType}}
@prefixValue={{personalMessageSectionLink.prefixValue}}
@currentWhen={{personalMessageSectionLink.currentWhen}}
@content={{personalMessageSectionLink.text}}
/>
{{/if}}
{{/each}}
{{#if (gt this.groupMessagesSectionLinks.length 0)}}
{{#each this.groupMessagesSectionLinks as |groupMessageSectionLink|}}
{{#if groupMessageSectionLink.shouldDisplay}}
<Sidebar::SectionLink
@linkName={{groupMessageSectionLink.name}}
@linkClass={{groupMessageSectionLink.class}}
@route={{groupMessageSectionLink.route}}
@prefixType={{groupMessageSectionLink.prefixType}}
@prefixValue={{groupMessageSectionLink.prefixValue}}
@models={{groupMessageSectionLink.models}}
@currentWhen={{groupMessageSectionLink.currentWhen}}
@content={{groupMessageSectionLink.text}}
/>
{{/if}}
{{/each}}
{{/if}}
</Sidebar::Section>

View File

@ -0,0 +1,28 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
import ApiSections from "../api-sections";
import CategoriesSection from "./categories-section";
import CustomSections from "./custom-sections";
import MessagesSection from "./messages-section";
import TagsSection from "./tags-section";
export default class SidebarUserSections extends Component {
@service currentUser;
<template>
<div class="sidebar-sections">
<CustomSections @collapsable={{@collapsableSections}} />
<CategoriesSection @collapsable={{@collapsableSections}} />
{{#if this.currentUser.display_sidebar_tags}}
<TagsSection @collapsable={{@collapsableSections}} />
{{/if}}
{{#if this.currentUser.can_send_private_messages}}
<MessagesSection @collapsable={{@collapsableSections}} />
{{/if}}
<ApiSections @collapsable={{@collapsableSections}} />
</div>
</template>
}

View File

@ -1,14 +0,0 @@
<div class="sidebar-sections">
<Sidebar::User::CustomSections @collapsable={{@collapsableSections}} />
<Sidebar::User::CategoriesSection @collapsable={{@collapsableSections}} />
{{#if this.currentUser.display_sidebar_tags}}
<Sidebar::User::TagsSection @collapsable={{@collapsableSections}} />
{{/if}}
{{#if this.enableMessagesSection}}
<Sidebar::User::MessagesSection @collapsable={{@collapsableSections}} />
{{/if}}
<Sidebar::ApiSections @collapsable={{@collapsableSections}} />
</div>

View File

@ -1,12 +0,0 @@
import Component from "@glimmer/component";
import { service } from "@ember/service";
export default class SidebarUserSections extends Component {
@service siteSettings;
@service currentUser;
@service site;
get enableMessagesSection() {
return this.currentUser?.can_send_private_messages;
}
}

View File

@ -1,11 +1,16 @@
import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { array, hash } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import SidebarEditNavigationMenuTagsModal from "discourse/components/sidebar/edit-navigation-menu/tags-modal";
import { hasDefaultSidebarTags } from "discourse/lib/sidebar/helpers";
import PMTagSectionLink from "discourse/lib/sidebar/user/tags-section/pm-tag-section-link";
import TagSectionLink from "discourse/lib/sidebar/user/tags-section/tag-section-link";
import i18n from "discourse-common/helpers/i18n";
import AllTagsSectionLink from "../common/all-tags-section-link";
import Section from "../section";
import SectionLink from "../section-link";
export default class SidebarUserTagsSection extends Component {
@service currentUser;
@ -36,7 +41,6 @@ export default class SidebarUserTagsSection extends Component {
@cached
get sectionLinks() {
const links = [];
let tags;
if (this.currentUser.sidebarTags.length > 0) {
@ -79,4 +83,51 @@ export default class SidebarUserTagsSection extends Component {
showModal() {
this.modal.show(SidebarEditNavigationMenuTagsModal);
}
<template>
<Section
@sectionName="tags"
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
@headerActions={{array
(hash
action=this.showModal
title=(i18n "sidebar.sections.tags.header_action_title")
)
}}
@headerActionsIcon="pencil-alt"
@collapsable={{@collapsable}}
>
{{#each this.sectionLinks as |sectionLink|}}
<SectionLink
@route={{sectionLink.route}}
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@badgeText={{sectionLink.badgeText}}
@models={{sectionLink.models}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}}
data-tag-name={{sectionLink.tagName}}
/>
{{/each}}
<AllTagsSectionLink />
{{#if this.shouldDisplayDefaultConfig}}
<SectionLink
@linkName="configure-default-navigation-menu-tags"
@content={{i18n "sidebar.sections.tags.configure_defaults"}}
@prefixType="icon"
@prefixValue="wrench"
@route="adminSiteSettingsCategory"
@model="sidebar"
@query={{hash filter="default_navigation_menu_tags"}}
/>
{{/if}}
</Section>
</template>
}

View File

@ -1,47 +0,0 @@
<Sidebar::Section
@sectionName="tags"
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
@headerActions={{array
(hash
action=this.showModal
title=(i18n "sidebar.sections.tags.header_action_title")
)
}}
@headerActionsIcon="pencil-alt"
@collapsable={{@collapsable}}
>
{{#if (gt this.sectionLinks.length 0)}}
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::SectionLink
@route={{sectionLink.route}}
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@prefixColor={{sectionLink.prefixColor}}
@badgeText={{sectionLink.badgeText}}
@models={{sectionLink.models}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}}
data-tag-name={{sectionLink.tagName}}
/>
{{/each}}
{{/if}}
<Sidebar::Common::AllTagsSectionLink />
{{#if this.shouldDisplayDefaultConfig}}
<Sidebar::SectionLink
@linkName="configure-default-navigation-menu-tags"
@content={{i18n "sidebar.sections.tags.configure_defaults"}}
@prefixType="icon"
@prefixValue="wrench"
@route="adminSiteSettingsCategory"
@model="sidebar"
@query={{hash filter="default_navigation_menu_tags"}}
/>
{{/if}}
</Sidebar::Section>

View File

@ -2,6 +2,7 @@ import { tracked } from "@glimmer/tracking";
import { service } from "@ember/service";
import { dasherize } from "@ember/string";
import { htmlSafe } from "@ember/template";
import UserStatusMessage from "discourse/components/user-status-message";
import { decorateUsername } from "discourse/helpers/decorate-username-selector";
import { withPluginApi } from "discourse/lib/plugin-api";
import { emojiUnescape } from "discourse/lib/text";
@ -315,7 +316,7 @@ export default {
get contentComponent() {
if (this.oneOnOneMessage) {
return "user-status-message";
return UserStatusMessage;
}
}