Skip to content

Commit

Permalink
DEV: Make edit sidebar categories modal load more results incrementally
Browse files Browse the repository at this point in the history
  • Loading branch information
danielwaterworth committed May 2, 2024
1 parent 12cb557 commit 3c0af55
Showing 1 changed file with 105 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Input } from "@ember/component";
import { concat, fn, get } 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, not } from "truth-helpers";
import EditNavigationMenuModal from "discourse/components/sidebar/edit-navigation-menu/modal";
Expand Down Expand Up @@ -45,6 +46,19 @@ function findAncestors(categories) {
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 extends Component {
@service currentUser;
@service site;
Expand All @@ -60,18 +74,27 @@ export default class extends Component {
constructor() {
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");
}

setFilteredCategories(categories) {
this.filteredCategories = categories;
const ancestors = findAncestors(categories);
const allCategories = categories.concat(ancestors).uniqBy((c) => c.id);

if (this.siteSettings.fixed_category_positions) {
allCategories.sort((a, b) => a.position - b.position);
}

this.filteredCategoriesGroupings = splitWhere(
Category.sortCategories(allCategories),
(category) => category.parent_category_id === undefined
Expand All @@ -80,55 +103,107 @@ export default class extends Component {
this.filteredCategoryIds = categories.map((c) => c.id);
}

concatFilteredCategories(categories) {
this.setFilteredCategories(this.filteredCategories.concat(categories));
}

setFetchedCategories(mode, categories) {
this.setFilteredCategories(
applyMode(mode, categories, this.selectedSidebarCategoryIds)
);
}

concatFetchedCategories(mode, categories) {
this.concatFilteredCategories(
applyMode(mode, categories, this.selectedSidebarCategoryIds)
);
}

@action
didInsert(element) {
this.observer.disconnect();
this.observer.observe(element);
}

async searchCategories(filter, mode) {
if (filter === "" && mode === "only-selected") {
this.setFilteredCategories(
await Category.asyncFindByIds(this.selectedSidebarCategoryIds)
);

this.loadedPage = null;
this.hasMorePages = false;
} else {
const { categories } = await Category.asyncSearch(filter, {
includeAncestors: true,
includeUncategorized: false,
});

const filteredFetchedCategories = categories.filter((c) => {
switch (mode) {
case "everything":
return true;
case "only-selected":
return this.selectedSidebarCategoryIds.includes(c.id);
case "only-unselected":
return !this.selectedSidebarCategoryIds.includes(c.id);
}
});
this.setFetchedCategories(mode, categories);

this.setFilteredCategories(filteredFetchedCategories);
this.loadedPage = 1;
this.hasMorePages = true;
}
}

async setFilterAndMode(newFilter, newMode) {
this.filter = newFilter;
this.mode = newMode;
this.requestedFilter = newFilter;
this.requestedMode = newMode;

if (!this.processing) {
this.processing = true;

try {
while (true) {
const filter = this.filter;
const mode = this.mode;
while (
this.loadedFilter !== this.requestedFilter ||
this.loadedMode !== this.requestedMode
) {
const filter = this.requestedFilter;
const mode = this.requestedMode;

await this.searchCategories(filter, mode);

this.loadedFilter = filter;
this.loadedMode = mode;
this.initialLoad = false;
}
} finally {
this.processing = false;
}
}
}

if (filter === this.filter && mode === this.mode) {
break;
async loadMore() {
if (!this.processing && this.hasMorePages) {
this.processing = true;

try {
const page = this.loadedPage + 1;
const { categories } = await Category.asyncSearch(
this.requestedFilter,
{
includeAncestors: true,
includeUncategorized: false,
page,
}
);
this.loadedPage = page;

if (categories.length === 0) {
this.hasMorePages = false;
} else {
this.concatFetchedCategories(this.requestedMode, categories);
}
} finally {
this.processing = false;
}

if (
this.loadedFilter !== this.requestedFilter ||
this.loadedMode !== this.requestedMode
) {
await this.setFilterAndMode(this.requestedFilter, this.requestedMode);
}
}
}

Expand All @@ -138,22 +213,25 @@ export default class extends Component {

@action
resetFilter() {
this.debouncedSetFilterAndMode(this.filter, "everything");
this.debouncedSetFilterAndMode(this.requestedFilter, "everything");
}

@action
filterSelected() {
this.debouncedSetFilterAndMode(this.filter, "only-selected");
this.debouncedSetFilterAndMode(this.requestedFilter, "only-selected");
}

@action
filterUnselected() {
this.debouncedSetFilterAndMode(this.filter, "only-unselected");
this.debouncedSetFilterAndMode(this.requestedFilter, "only-unselected");
}

@action
onFilterInput(filter) {
this.debouncedSetFilterAndMode(filter.toLowerCase().trim(), this.mode);
this.debouncedSetFilterAndMode(
filter.toLowerCase().trim(),
this.requestedMode
);
}

@action
Expand Down Expand Up @@ -234,6 +312,7 @@ export default class extends Component {
<div
class="sidebar-categories-form__row"
style={{borderColor (get categories "0.color") "left"}}
{{didInsert this.didInsert}}
>

{{#each categories as |category|}}
Expand Down

0 comments on commit 3c0af55

Please sign in to comment.