DEV: Clean up sidebar modals (#26999)

1. async/await
2. TrackedSet
3. don't rely on ember array methods
4. list used props
5. move stuff out of constructors
6. don't use ember's Input component
7. convert a function to a method (to avoid passing in a class prop)
8. add missing `@tracked`
9. remove tracking from props that don't need it (not used in templates)
This commit is contained in:
Jarek Radosz 2024-05-21 14:35:22 +02:00 committed by GitHub
parent fe12cfeab2
commit eb2df2b7d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 109 additions and 117 deletions

View File

@ -1,12 +1,12 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { concat, fn, get } from "@ember/helper"; import { concat, fn, get } from "@ember/helper";
import { on } from "@ember/modifier"; import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert"; import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { gt, includes, not } from "truth-helpers"; import { TrackedSet } from "@ember-compat/tracked-built-ins";
import { gt, has, includes, not } from "truth-helpers";
import EditNavigationMenuModal from "discourse/components/sidebar/edit-navigation-menu/modal"; import EditNavigationMenuModal from "discourse/components/sidebar/edit-navigation-menu/modal";
import borderColor from "discourse/helpers/border-color"; import borderColor from "discourse/helpers/border-color";
import categoryBadge from "discourse/helpers/category-badge"; import categoryBadge from "discourse/helpers/category-badge";
@ -46,19 +46,6 @@ function findAncestors(categories) {
return ancestors; return ancestors;
} }
function applyMode(mode, categories, selectedSidebarCategoryIds) {
return categories.filter((c) => {
switch (mode) {
case "everything":
return true;
case "only-selected":
return selectedSidebarCategoryIds.includes(c.id);
case "only-unselected":
return !selectedSidebarCategoryIds.includes(c.id);
}
});
}
export default class SidebarEditNavigationMenuCategoriesModal extends Component { export default class SidebarEditNavigationMenuCategoriesModal extends Component {
@service currentUser; @service currentUser;
@service site; @service site;
@ -67,26 +54,32 @@ export default class SidebarEditNavigationMenuCategoriesModal extends Component
@tracked initialLoad = true; @tracked initialLoad = true;
@tracked filteredCategoriesGroupings = []; @tracked filteredCategoriesGroupings = [];
@tracked filteredCategoryIds = []; @tracked filteredCategoryIds = [];
// TODO: tracked array, no ember array methods
@tracked @tracked
selectedSidebarCategoryIds = [...this.currentUser.sidebar_category_ids]; selectedSidebarCategoryIds = new TrackedSet([
...this.currentUser.sidebar_category_ids,
]);
hasMorePages;
loadedFilter;
loadedMode;
loadedPage;
processing = false;
requestedFilter;
requestedMode;
saving = false;
observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
this.observer.disconnect();
this.loadMore();
}
},
{
threshold: 1.0,
}
);
constructor() { constructor() {
super(...arguments); super(...arguments);
this.observer = new IntersectionObserver(
(entries) => {
if (entries.some((entry) => entry.isIntersecting)) {
this.observer.disconnect();
this.loadMore();
}
},
{
threshold: 1.0,
}
);
this.processing = false;
this.setFilterAndMode("", "everything"); this.setFilterAndMode("", "everything");
} }
@ -108,15 +101,24 @@ export default class SidebarEditNavigationMenuCategoriesModal extends Component
} }
setFetchedCategories(mode, categories) { setFetchedCategories(mode, categories) {
this.setFilteredCategories( this.setFilteredCategories(this.applyMode(mode, categories));
applyMode(mode, categories, this.selectedSidebarCategoryIds)
);
} }
concatFetchedCategories(mode, categories) { concatFetchedCategories(mode, categories) {
this.concatFilteredCategories( this.concatFilteredCategories(this.applyMode(mode, categories));
applyMode(mode, categories, this.selectedSidebarCategoryIds) }
);
applyMode(mode, categories) {
return categories.filter((c) => {
switch (mode) {
case "everything":
return true;
case "only-selected":
return this.selectedSidebarCategoryIds.has(c.id);
case "only-unselected":
return !this.selectedSidebarCategoryIds.has(c.id);
}
});
} }
@action @action
@ -128,7 +130,7 @@ export default class SidebarEditNavigationMenuCategoriesModal extends Component
async searchCategories(filter, mode) { async searchCategories(filter, mode) {
if (filter === "" && mode === "only-selected") { if (filter === "" && mode === "only-selected") {
this.setFilteredCategories( this.setFilteredCategories(
await Category.asyncFindByIds(this.selectedSidebarCategoryIds) await Category.asyncFindByIds([...this.selectedSidebarCategoryIds])
); );
this.loadedPage = null; this.loadedPage = null;
@ -241,43 +243,40 @@ export default class SidebarEditNavigationMenuCategoriesModal extends Component
@action @action
toggleCategory(categoryId) { toggleCategory(categoryId) {
if (this.selectedSidebarCategoryIds.includes(categoryId)) { if (this.selectedSidebarCategoryIds.has(categoryId)) {
this.selectedSidebarCategoryIds.removeObject(categoryId); this.selectedSidebarCategoryIds.delete(categoryId);
} else { } else {
this.selectedSidebarCategoryIds.addObject(categoryId); this.selectedSidebarCategoryIds.add(categoryId);
} }
} }
@action @action
resetToDefaults() { resetToDefaults() {
this.selectedSidebarCategoryIds = this.selectedSidebarCategoryIds = new TrackedSet(
this.siteSettings.default_navigation_menu_categories this.siteSettings.default_navigation_menu_categories
.split("|") .split("|")
.map((id) => parseInt(id, 10)); .map((id) => parseInt(id, 10))
);
} }
@action @action
save() { async save() {
this.saving = true; this.saving = true;
const initialSidebarCategoryIds = this.currentUser.sidebar_category_ids; const initialSidebarCategoryIds = this.currentUser.sidebar_category_ids;
this.currentUser.set( this.currentUser.set("sidebar_category_ids", [
"sidebar_category_ids", ...this.selectedSidebarCategoryIds,
this.selectedSidebarCategoryIds ]);
);
this.currentUser try {
.save(["sidebar_category_ids"]) await this.currentUser.save(["sidebar_category_ids"]);
.then(() => { this.args.closeModal();
this.args.closeModal(); } catch (error) {
}) this.currentUser.set("sidebar_category_ids", initialSidebarCategoryIds);
.catch((error) => { popupAjaxError(error);
this.currentUser.set("sidebar_category_ids", initialSidebarCategoryIds); } finally {
popupAjaxError(error); this.saving = false;
}) }
.finally(() => {
this.saving = false;
});
} }
<template> <template>
@ -344,10 +343,10 @@ export default class SidebarEditNavigationMenuCategoriesModal extends Component
{{/unless}} {{/unless}}
</div> </div>
<Input <input
{{on "click" (fn this.toggleCategory category.id)}} {{on "click" (fn this.toggleCategory category.id)}}
@type="checkbox" type="checkbox"
@checked={{includes checked={{has
this.selectedSidebarCategoryIds this.selectedSidebarCategoryIds
category.id category.id
}} }}

View File

@ -1,7 +1,6 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component"; import { fn, hash } from "@ember/helper";
import { hash } from "@ember/helper";
import { on } from "@ember/modifier"; import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import DButton from "discourse/components/d-button"; import DButton from "discourse/components/d-button";
@ -15,7 +14,6 @@ import DropdownSelectBox from "select-kit/components/dropdown-select-box";
export default class SidebarEditNavigationMenuModal extends Component { export default class SidebarEditNavigationMenuModal extends Component {
@tracked filter = ""; @tracked filter = "";
@tracked filterDropdownValue = "all"; @tracked filterDropdownValue = "all";
filterDropdownContent = [ filterDropdownContent = [
{ {
id: "all", id: "all",
@ -80,10 +78,11 @@ export default class SidebarEditNavigationMenuModal extends Component {
class="sidebar__edit-navigation-menu__filter-input-icon" class="sidebar__edit-navigation-menu__filter-input-icon"
}} }}
<Input <input
{{on "input" (withEventValue (fn (mut this.filter)))}}
{{on "input" (withEventValue @onFilterInput)}} {{on "input" (withEventValue @onFilterInput)}}
@type="text" type="text"
@value={{this.filter}} value={{this.filter}}
placeholder={{@inputFilterPlaceholder}} placeholder={{@inputFilterPlaceholder}}
autofocus="true" autofocus="true"
class="sidebar__edit-navigation-menu__filter-input-field" class="sidebar__edit-navigation-menu__filter-input-field"

View File

@ -1,12 +1,12 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { concat, fn } from "@ember/helper"; import { concat, fn } from "@ember/helper";
import { on } from "@ember/modifier"; import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert"; import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { gt, includes, or } from "truth-helpers"; import { TrackedSet } from "@ember-compat/tracked-built-ins";
import { gt, has, or } from "truth-helpers";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner"; import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import loadingSpinner from "discourse/helpers/loading-spinner"; import loadingSpinner from "discourse/helpers/loading-spinner";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
@ -20,13 +20,14 @@ export default class SidebarEditNavigationMenuTagsModal extends Component {
@service siteSettings; @service siteSettings;
@service store; @service store;
@tracked filter = ""; @tracked disableFiltering = false;
@tracked onlySelected = false; @tracked saving = false;
@tracked onlyUnSelected = false; @tracked selectedTags = new TrackedSet([...this.currentUser.sidebarTagNames]);
@tracked tags = []; @tracked tags = [];
@tracked tagsLoading; @tracked tagsLoading = false;
@tracked disableFiltering; observer;
@tracked selectedTags = [...this.currentUser.sidebarTagNames]; onlySelected = false;
onlyUnselected = false;
constructor() { constructor() {
super(...arguments); super(...arguments);
@ -38,30 +39,25 @@ export default class SidebarEditNavigationMenuTagsModal extends Component {
const findArgs = {}; const findArgs = {};
if (this.filter !== "") { if (this.filter) {
findArgs.filter = this.filter; findArgs.filter = this.filter;
} }
if (this.onlySelected) { if (this.onlySelected) {
findArgs.only_tags = this.selectedTags.join(","); findArgs.only_tags = [...this.selectedTags].join(",");
} else if (this.onlyUnselected) {
findArgs.exclude_tags = [...this.selectedTags].join(",");
} }
if (this.onlyUnselected) { try {
findArgs.exclude_tags = this.selectedTags.join(","); const tags = await this.store.findAll("listTag", findArgs);
this.tags = tags;
} catch (error) {
popupAjaxError(error);
} finally {
this.tagsLoading = false;
this.disableFiltering = false;
} }
await this.store
.findAll("listTag", findArgs)
.then((tags) => {
this.tags = tags;
})
.catch((error) => {
popupAjaxError(error);
})
.finally(() => {
this.tagsLoading = false;
this.disableFiltering = false;
});
} }
@action @action
@ -137,38 +133,36 @@ export default class SidebarEditNavigationMenuTagsModal extends Component {
@action @action
resetToDefaults() { resetToDefaults() {
this.selectedTags = this.selectedTags = new TrackedSet(
this.siteSettings.default_navigation_menu_tags.split("|"); this.siteSettings.default_navigation_menu_tags.split("|")
);
} }
@action @action
toggleTag(tag) { toggleTag(tag) {
if (this.selectedTags.includes(tag)) { if (this.selectedTags.has(tag)) {
this.selectedTags.removeObject(tag); this.selectedTags.delete(tag);
} else { } else {
this.selectedTags.addObject(tag); this.selectedTags.add(tag);
} }
} }
@action @action
save() { async save() {
this.saving = true; this.saving = true;
const initialSidebarTags = this.currentUser.sidebar_tags; const initialSidebarTags = this.currentUser.sidebar_tags;
this.currentUser.set("sidebar_tag_names", this.selectedTags); this.currentUser.set("sidebar_tag_names", [...this.selectedTags]);
this.currentUser try {
.save(["sidebar_tag_names"]) const result = await this.currentUser.save(["sidebar_tag_names"]);
.then((result) => { this.currentUser.set("sidebar_tags", result.user.sidebar_tags);
this.currentUser.set("sidebar_tags", result.user.sidebar_tags); this.args.closeModal();
this.args.closeModal(); } catch (error) {
}) this.currentUser.set("sidebar_tags", initialSidebarTags);
.catch((error) => { popupAjaxError(error);
this.currentUser.set("sidebar_tags", initialSidebarTags); } finally {
popupAjaxError(error); this.saving = false;
}) }
.finally(() => {
this.saving = false;
});
} }
<template> <template>
@ -204,10 +198,10 @@ export default class SidebarEditNavigationMenuTagsModal extends Component {
data-tag-name={{tag.name}} data-tag-name={{tag.name}}
class="sidebar-tags-form__tag" class="sidebar-tags-form__tag"
> >
<Input <input
{{on "click" (fn this.toggleTag tag.name)}} {{on "click" (fn this.toggleTag tag.name)}}
@type="checkbox" type="checkbox"
@checked={{includes this.selectedTags tag.name}} checked={{has this.selectedTags tag.name}}
id={{concat "sidebar-tags-form__input--" tag.name}} id={{concat "sidebar-tags-form__input--" tag.name}}
class="sidebar-tags-form__input" class="sidebar-tags-form__input"
/> />