list: add load queue

_loadLock set true when a load is happening, if another _load is called
when this is happening, the method call is appended to _loadQueue. When
a running _load finishes, it shift()s the _loadQueue and calls the
method from it if there is one.
This commit is contained in:
Harvey Tindall
2026-01-05 09:48:30 +00:00
parent c10f1e3b36
commit 721b209e1f
4 changed files with 48 additions and 14 deletions

View File

@@ -155,16 +155,15 @@ const tabs: { id: string; url: string; reloader: () => void; unloader?: () => vo
let t: { id: string; url: string; reloader: () => void; unloader?: () => void } = { let t: { id: string; url: string; reloader: () => void; unloader?: () => void } = {
id: p.tabName, id: p.tabName,
url: p.pagePath, url: p.pagePath,
reloader: () => reloader: () => {
p.reload(() => { if (isPageEventBindable(p)) p.bindPageEvents();
if (!navigated && isNavigatable(p)) { if (!navigated && isNavigatable(p) && p.isURL()) {
if (p.isURL()) { navigated = true;
navigated = true; p.navigate();
p.navigate(); } else {
} p.reload(() => {});
} }
if (isPageEventBindable(p)) p.bindPageEvents(); },
}),
}; };
if (isPageEventBindable(p)) t.unloader = p.unbindPageEvents; if (isPageEventBindable(p)) t.unloader = p.unbindPageEvents;
tabs.push(t); tabs.push(t);

View File

@@ -1106,6 +1106,7 @@ export class accountsList extends PaginatedList implements Navigatable, AsTab {
itemsPerPage: 40, itemsPerPage: 40,
maxItemsLoadedForSearch: 200, maxItemsLoadedForSearch: 200,
appendNewItems: (resp: PaginatedDTO) => { appendNewItems: (resp: PaginatedDTO) => {
console.log("append");
for (let u of (resp as UsersDTO).users || []) { for (let u of (resp as UsersDTO).users || []) {
if (this.users.has(u.id)) { if (this.users.has(u.id)) {
this.users.get(u.id).update(u); this.users.get(u.id).update(u);
@@ -1121,6 +1122,7 @@ export class accountsList extends PaginatedList implements Navigatable, AsTab {
); );
}, },
replaceWithNewItems: (resp: PaginatedDTO) => { replaceWithNewItems: (resp: PaginatedDTO) => {
console.log("replace");
let accountsOnDOM = new Map<string, boolean>(); let accountsOnDOM = new Map<string, boolean>();
for (let id of this.users.keys()) { for (let id of this.users.keys()) {

View File

@@ -359,6 +359,8 @@ export abstract class PaginatedList implements PageEventBindable {
return bottomIdx; return bottomIdx;
}; };
private _loadLock: boolean = false;
private _loadQueue: (() => void)[] = [];
private _load = ( private _load = (
itemLimit: number, itemLimit: number,
page: number, page: number,
@@ -367,7 +369,17 @@ export abstract class PaginatedList implements PageEventBindable {
post?: (resp: PaginatedDTO) => void, post?: (resp: PaginatedDTO) => void,
failCallback?: (req: XMLHttpRequest) => void, failCallback?: (req: XMLHttpRequest) => void,
) => { ) => {
if (this._loadLock) {
console.debug("Queuing load, position:", this._loadQueue.length);
const now = Date.now();
this._loadQueue.push(() => {
console.debug("Queued load running, appended at:", now);
this._load(itemLimit, page, appendFunc, pre, post, failCallback);
});
return;
}
this._lastLoad = Date.now(); this._lastLoad = Date.now();
this._loadLock = true;
let params = this._search.inServerSearch ? this._searchParams : this.defaultParams(); let params = this._search.inServerSearch ? this._searchParams : this.defaultParams();
params.limit = itemLimit; params.limit = itemLimit;
params.page = page; params.page = page;
@@ -397,9 +409,14 @@ export abstract class PaginatedList implements PageEventBindable {
this._counter.loaded = this._search.ordering.length; this._counter.loaded = this._search.ordering.length;
this._loadLock = false;
if (post) post(resp); if (post) post(resp);
if (this._c.pageLoadCallback) this._c.pageLoadCallback(req); if (this._c.pageLoadCallback) this._c.pageLoadCallback(req);
const next = this._loadQueue.shift();
if (next) next();
}, },
true, true,
); );
@@ -408,6 +425,7 @@ export abstract class PaginatedList implements PageEventBindable {
// Removes all elements, and reloads the first page. // Removes all elements, and reloads the first page.
public abstract reload: (callback?: (resp: PaginatedDTO) => void) => void; public abstract reload: (callback?: (resp: PaginatedDTO) => void) => void;
protected _reload = (callback?: (resp: PaginatedDTO) => void) => { protected _reload = (callback?: (resp: PaginatedDTO) => void) => {
console.trace("reloading");
this.lastPage = false; this.lastPage = false;
this._counter.reset(); this._counter.reset();
this._counter.getTotal( this._counter.getTotal(
@@ -446,6 +464,7 @@ export abstract class PaginatedList implements PageEventBindable {
// Loads the next page. If "loadAll", all pages will be loaded until the last is reached. // Loads the next page. If "loadAll", all pages will be loaded until the last is reached.
public abstract loadMore: (loadAll?: boolean, callback?: (resp?: PaginatedDTO) => void) => void; public abstract loadMore: (loadAll?: boolean, callback?: (resp?: PaginatedDTO) => void) => void;
protected _loadMore = (loadAll: boolean = false, callback?: (resp: PaginatedDTO) => void) => { protected _loadMore = (loadAll: boolean = false, callback?: (resp: PaginatedDTO) => void) => {
console.trace("loading more");
this._c.loadMoreButtons.forEach((v) => (v.disabled = true)); this._c.loadMoreButtons.forEach((v) => (v.disabled = true));
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
this._c.loadMoreButtons.forEach((v) => (v.disabled = false)); this._c.loadMoreButtons.forEach((v) => (v.disabled = false));

View File

@@ -552,6 +552,7 @@ export class Search implements Navigatable {
return this._ascending; return this._ascending;
} }
// FIXME: This is being called by navigate, and triggering a "load more" when we haven't loaded at all, and loading without a searchc when we have one!
onSearchBoxChange = ( onSearchBoxChange = (
newItems: boolean = false, newItems: boolean = false,
appendedItems: boolean = false, appendedItems: boolean = false,
@@ -721,6 +722,15 @@ export class Search implements Navigatable {
return req; return req;
}; };
private _qps: URLSearchParams = new URLSearchParams();
private _clearWithoutNavigate = false;
// clearQueryParam removes the "search" query parameter --without-- triggering a navigate call.
clearQueryParam = () => {
if (!this._qps.has("search")) return;
this._clearWithoutNavigate = true;
this.setQueryParam("");
};
// setQueryParam sets the ?search query param to the current searchbox content, // setQueryParam sets the ?search query param to the current searchbox content,
// or value if given. If everything is set up correctly, this should trigger a search when it is // or value if given. If everything is set up correctly, this should trigger a search when it is
// set to a new value. // set to a new value.
@@ -754,10 +764,13 @@ export class Search implements Navigatable {
// navigate pulls the current "search" query param, puts it in the search box and searches it. // navigate pulls the current "search" query param, puts it in the search box and searches it.
navigate = (url?: string, then?: () => void) => { navigate = (url?: string, then?: () => void) => {
(window as any).s = this; this._qps = new URLSearchParams(url || window.location.search);
const urlParams = new URLSearchParams(url || window.location.search); if (this._clearWithoutNavigate) {
const searchContent = urlParams.get("search") || ""; this._clearWithoutNavigate = false;
console.log("navigate!, setting search box to ", searchContent); return;
}
const searchContent = this._qps.get("search") || "";
this._c.search.value = searchContent; this._c.search.value = searchContent;
this.onSearchBoxChange(); this.onSearchBoxChange();
this.onServerSearch(then); this.onServerSearch(then);
@@ -772,6 +785,7 @@ export class Search implements Navigatable {
this._c.search.oninput = () => { this._c.search.oninput = () => {
this.inServerSearch = false; this.inServerSearch = false;
this.clearQueryParam();
this.onSearchBoxChange(); this.onSearchBoxChange();
}; };
this._c.search.addEventListener("keyup", (ev: KeyboardEvent) => { this._c.search.addEventListener("keyup", (ev: KeyboardEvent) => {