From cdb73ee49cc68884e0a93f9c82d153400ac219ac Mon Sep 17 00:00:00 2001 From: Sli Date: Sat, 17 Aug 2024 02:57:00 +0200 Subject: [PATCH] Don't rely on waiting for pedagogy history --- core/static/core/js/script.js | 14 ++++-- pedagogy/templates/pedagogy/guide.jinja | 59 ++++++++++++++++++++----- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/core/static/core/js/script.js b/core/static/core/js/script.js index 13613740..c55cf51f 100644 --- a/core/static/core/js/script.js +++ b/core/static/core/js/script.js @@ -74,6 +74,7 @@ const initialUrlParams = new URLSearchParams(window.location.search); * @enum {number} */ const History = { + NONE: 0, PUSH: 1, REPLACE: 2, }; @@ -82,9 +83,12 @@ const History = { * @param {string} key * @param {string | string[] | null} value * @param {History} action + * @param {URL | null} url */ -function update_query_string(key, value, action = History.REPLACE) { - const url = new URL(window.location.href); +function update_query_string(key, value, action = History.REPLACE, url = null) { + if (!url){ + url = new URL(window.location.href); + } if (!value) { // If the value is null, undefined or empty => delete it url.searchParams.delete(key) @@ -96,8 +100,10 @@ function update_query_string(key, value, action = History.REPLACE) { } if (action === History.PUSH) { - history.pushState(null, document.title, url.toString()); + history.pushState(null, "", url.toString()); } else if (action === History.REPLACE) { - history.replaceState(null, document.title, url.toString()); + history.replaceState(null, "", url.toString()); } + + return url; } diff --git a/pedagogy/templates/pedagogy/guide.jinja b/pedagogy/templates/pedagogy/guide.jinja index e2ab4d22..169c6d99 100644 --- a/pedagogy/templates/pedagogy/guide.jinja +++ b/pedagogy/templates/pedagogy/guide.jinja @@ -128,6 +128,27 @@ #} const page_default = 1; const page_size_default = 100; + function debouncePromise (fn, ms = 0) { + let timeoutId; + const pending = []; + return (...args) => + new Promise((res, rej) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + const currentPending = [...pending]; + pending.length = 0; + Promise.resolve(fn.apply(this, args)).then( + data => { + currentPending.forEach(({ resolve }) => resolve(data)); + }, + error => { + currentPending.forEach(({ reject }) => reject(error)); + } + ); + }, ms); + pending.push({ resolve: res, reject: rej }); + }); + }; document.addEventListener("alpine:init", () => { Alpine.data("uv_search", () => ({ uvs: [], @@ -138,8 +159,11 @@ department: [], credit_type: [], semester: [], + to_change: [], pushstate: History.PUSH, + update: undefined, + async initialize_args() { let url = new URLSearchParams(window.location.search); this.pushstate = History.REPLACE; @@ -155,19 +179,28 @@ this.semester = url.has("semester") ? url.get("semester").split("_AND_") : []; - {# Wait for all watch callbacks to be called #} - await (new Promise(resolve => setTimeout(resolve, 100))); - + await this.update() this.pushstate = History.PUSH; }, async init() { - await this.initialize_args(); + this.update = debouncePromise(async () => { + await Promise.all(this.to_change).then(async (data) => { + {# Create the whole url before changing everything all at once #} + let first = data.shift(); + let url = update_query_string(first[0], first[1], History.NONE); + data.forEach((data) => { + url = update_query_string(data[0], data[1], History.NONE, url); + }) + update_query_string(first[0], first[1], this.pushstate, url); + await this.fetch_data(); {# reload data on form change #} + this.to_change = []; + }) + }, 50); + let search_params = ["search", "department", "credit_type", "semester"]; let pagination_params = ["page", "page_size"]; - this.fetch_data(); {# load initial data #} - search_params.forEach((param) => { this.$watch(param, async (value) => { if (this.pushstate != History.PUSH){ @@ -181,16 +214,20 @@ }); search_params.concat(pagination_params).forEach((param) => { this.$watch(param, async (value) => { - console.log(param + " " + value) - update_query_string(param, value, this.pushstate); - await this.fetch_data(); {# reload data on form change #} + this.to_change.push(new Promise((resolve) => { + resolve([param, value]); + }) + ); + this.update(); }); }); - window.addEventListener("popstate", () => { - this.initialize_args(); + window.addEventListener("popstate", async (event) => { + await this.initialize_args(); }); + await this.initialize_args(); }, + async fetch_data() { this.loading = true; const url = "{{ url("api:fetch_uvs") }}" + window.location.search;