From 2ac2e861b7b5b06c8c5543b852b1b66e458549bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 12:47:48 +0200 Subject: [PATCH 1/9] feat(angular): remember which dashboard widgets were opened --- src/angular/index.js | 1 + .../modules/rememberDashboardWidgets.js | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/angular/modules/rememberDashboardWidgets.js diff --git a/src/angular/index.js b/src/angular/index.js index 73e73e6..ec0f636 100644 --- a/src/angular/index.js +++ b/src/angular/index.js @@ -7,6 +7,7 @@ const modules = [ require("./modules/addVersion"), require("./modules/removeTodoBanner"), require("./modules/infiniteSession"), + require("./modules/rememberDashboardWidgets"), ]; let loadedModules = []; diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js new file mode 100644 index 0000000..4139bdf --- /dev/null +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -0,0 +1,68 @@ +const identifier = ["angular", "rememberDashboardWidgets"]; + +const $ = window.jQuery; +const utils = require("../utils"); +const storage = require("../../shared/storage"); + +function isOnDashboard() { + const uri = utils.getCurrentPage().pathname.split("/"); + return uri[uri.length - 1] === "dashboard"; +} + +function getWidgets() { + // Root elements of Upcoming events, Exams, Messages... + const widgetRoots = $(".widget"); + + // Areas of the widgets that + // - are clickable + // - have an ID that is hopefully stable + // - have a class that indicates if it is open or closed (mat-expanded) + return widgetRoots.find("mat-expansion-panel-header"); +} + +function loadWidgetStates() { + const openWidgets = storage.getForUser("dashboardOpenWidgets"); + if (!openWidgets) { + return; + } + + const widgets = getWidgets(); + openWidgets.forEach(widgetId => { + const widget = widgets.filter(`#${widgetId}`); + widget.click(); + }) + + console.debug("[rememberDashboardWidgets] loaded open widgets:", openWidgets) +} + +function saveOpenWidgets() { + const openWidgets = getWidgets() + .filter(function() { + return $(this).hasClass("mat-expanded"); + }) + .map(function() { + return $(this).attr("id"); + }) + .get(); + + storage.setForUser("dashboardOpenWidgets", openWidgets); + console.debug("[rememberDashboardWidgets] saved open widgets:", openWidgets) +} + +function init() { + loadWidgetStates() + getWidgets().on("click", saveOpenWidgets); +} + +function destroy() { + getWidgets().off("click", saveOpenWidgets); +} + +module.exports = { + identifier: identifier.join("."), + shouldActivate: isOnDashboard, + shouldNotDestroy: isOnDashboard, + initialize: init, + // Do actions before destroying + destroy, +} From 325198e9264d44e49066817be340728a6b1702bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 13:26:22 +0200 Subject: [PATCH 2/9] fix(angular): use text instead of id for identifying widgets --- src/angular/modules/rememberDashboardWidgets.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js index 4139bdf..e38b4b2 100644 --- a/src/angular/modules/rememberDashboardWidgets.js +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -27,9 +27,13 @@ function loadWidgetStates() { } const widgets = getWidgets(); - openWidgets.forEach(widgetId => { - const widget = widgets.filter(`#${widgetId}`); - widget.click(); + openWidgets.forEach(widgetTitle => { + widgets.each(function() { + const titleElement = $(this).find(".widget__title"); + if (titleElement.text().trim() === widgetTitle) { + $(this).click(); + } + }); }) console.debug("[rememberDashboardWidgets] loaded open widgets:", openWidgets) @@ -41,7 +45,9 @@ function saveOpenWidgets() { return $(this).hasClass("mat-expanded"); }) .map(function() { - return $(this).attr("id"); + // Use the widget's title as an identifier because the widget's ID is unreliable + // As a downside remembered widgets will be forgotten when the user switches languages + return $(this).find(".widget__title").text().trim(); }) .get(); From 9ebb9c3e5104d6e106b8868fa57e3e3167aa15a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 13:46:19 +0200 Subject: [PATCH 3/9] fix(angular): wait for widgets to load --- .../modules/rememberDashboardWidgets.js | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js index e38b4b2..76520ce 100644 --- a/src/angular/modules/rememberDashboardWidgets.js +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -9,24 +9,38 @@ function isOnDashboard() { return uri[uri.length - 1] === "dashboard"; } -function getWidgets() { - // Root elements of Upcoming events, Exams, Messages... - const widgetRoots = $(".widget"); +async function getWidgets() { + return new Promise((resolve) => { + function checkWidgets() { + // Root elements of Upcoming events, Exams, Messages... + const widgetRoots = $(".widget"); - // Areas of the widgets that - // - are clickable - // - have an ID that is hopefully stable - // - have a class that indicates if it is open or closed (mat-expanded) - return widgetRoots.find("mat-expansion-panel-header"); + // Areas of the widgets that + // - are clickable + // - have an ID that is hopefully stable + // - have a class that indicates if it is open or closed (mat-expanded) + const widgets = widgetRoots.find("mat-expansion-panel-header"); + + // In case the widgets were not loaded yet, try again later + if (widgets.length === 0) { + setTimeout(checkWidgets, 100); + return + } + + resolve(widgets); + } + + checkWidgets(); + }); } -function loadWidgetStates() { +async function loadWidgetStates() { const openWidgets = storage.getForUser("dashboardOpenWidgets"); if (!openWidgets) { return; } - const widgets = getWidgets(); + const widgets = await getWidgets(); openWidgets.forEach(widgetTitle => { widgets.each(function() { const titleElement = $(this).find(".widget__title"); @@ -39,8 +53,9 @@ function loadWidgetStates() { console.debug("[rememberDashboardWidgets] loaded open widgets:", openWidgets) } -function saveOpenWidgets() { - const openWidgets = getWidgets() +async function saveOpenWidgets() { + const widgets = await getWidgets(); + const openWidgets = widgets .filter(function() { return $(this).hasClass("mat-expanded"); }) @@ -55,13 +70,15 @@ function saveOpenWidgets() { console.debug("[rememberDashboardWidgets] saved open widgets:", openWidgets) } -function init() { - loadWidgetStates() - getWidgets().on("click", saveOpenWidgets); +async function init() { + await loadWidgetStates(); + const widgets = await getWidgets(); + widgets.on("click", saveOpenWidgets); } -function destroy() { - getWidgets().off("click", saveOpenWidgets); +async function destroy() { + const widgets = await getWidgets(); + widgets.off("click", saveOpenWidgets); } module.exports = { From 5ac7f802760c447d697112c91fe827fd4e17eb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 13:55:57 +0200 Subject: [PATCH 4/9] feat(angular): don't try to find widgets indefinitely, retry every 50ms --- src/angular/modules/rememberDashboardWidgets.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js index 76520ce..1be103d 100644 --- a/src/angular/modules/rememberDashboardWidgets.js +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -10,8 +10,8 @@ function isOnDashboard() { } async function getWidgets() { - return new Promise((resolve) => { - function checkWidgets() { + return new Promise((resolve, reject) => { + function checkWidgets(attempts = 0) { // Root elements of Upcoming events, Exams, Messages... const widgetRoots = $(".widget"); @@ -23,7 +23,11 @@ async function getWidgets() { // In case the widgets were not loaded yet, try again later if (widgets.length === 0) { - setTimeout(checkWidgets, 100); + const maxAttempts = 100; + if (attempts === maxAttempts) { + reject(new Error("[rememberDashboardWidgets] failed to find widgets")); + } + setTimeout(() => checkWidgets(attempts + 1), 50); return } From df3428e4a14cdeebf30b9a0f25eab0024a250bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 14:03:12 +0200 Subject: [PATCH 5/9] docs: mention remember dashboard widgets module --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a137ea..bb234b0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A program **felturbózza a Neptun-odat**: gyorsabb tárgy- és vizsgafelvétel, > Ez a fork egy work-in-progress, így nem fog minden tökéletesen működni. > Egyenlőre az implementált modulok a következőek az új Neptun felületén: > -> `autoLogin, addVersion, removeTodoBanner, infiniteSession` +> `autoLogin, addVersion, removeTodoBanner, infiniteSession, rememberDashboardWidgets` > > Az egyetlen tesztelt felület egyenlőre az [Óbudai Egyetem](https://neptun.uni-obuda.hu/ujhallgato/) új Neptun felülete. > Testers welcome! @@ -127,11 +127,17 @@ Eleged van abból, hogy minden egyes alkalommal be kell állítanod, hogy 500 el ## Újdonságok +#### 2025. május 11. + +- #### Angular + - **Újdonság:** Az új felület kezdőoldalán az NPU megjegyzi melyik widgeteket (Pl. Vizsgák, Közelgő események) nyitottad ki. + #### 2025. március 31. - #### Angular - **Újdonság:** Az új felületen mostantól nem dob ki a rendszer 5-10 perc után. 🎉 - **Javítva:** NPU nem töltött be, ha a kezdő URL nem tartalmazta a jelenlegi oldalt. (Pl. https://url/hallgato nem töltötte be az NPU-t) + #### 2025. március 27. - #### Angular From 37ce4c61a53753f33f45d8bbd1db9c7c1f532b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 14:11:20 +0200 Subject: [PATCH 6/9] fixup! feat(angular): don't try to find widgets indefinitely, retry every 50ms --- src/angular/modules/rememberDashboardWidgets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js index 1be103d..2e7c0a5 100644 --- a/src/angular/modules/rememberDashboardWidgets.js +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -26,9 +26,10 @@ async function getWidgets() { const maxAttempts = 100; if (attempts === maxAttempts) { reject(new Error("[rememberDashboardWidgets] failed to find widgets")); + return; } setTimeout(() => checkWidgets(attempts + 1), 50); - return + return; } resolve(widgets); From dc3de934d0a49fc185c606fa0106f086138f8180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 14:47:04 +0200 Subject: [PATCH 7/9] fix(angular): cache result of getWidgets The module works fine without this change, but otherwise there's an error in the console because widgets are not available when the module gets destroyed. --- src/angular/modules/rememberDashboardWidgets.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js index 2e7c0a5..a878ed4 100644 --- a/src/angular/modules/rememberDashboardWidgets.js +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -4,12 +4,18 @@ const $ = window.jQuery; const utils = require("../utils"); const storage = require("../../shared/storage"); +let cachedWidgets = null; + function isOnDashboard() { const uri = utils.getCurrentPage().pathname.split("/"); return uri[uri.length - 1] === "dashboard"; } async function getWidgets() { + if (cachedWidgets) { + return cachedWidgets; + } + return new Promise((resolve, reject) => { function checkWidgets(attempts = 0) { // Root elements of Upcoming events, Exams, Messages... @@ -32,6 +38,7 @@ async function getWidgets() { return; } + cachedWidgets = widgets; resolve(widgets); } @@ -84,6 +91,7 @@ async function init() { async function destroy() { const widgets = await getWidgets(); widgets.off("click", saveOpenWidgets); + cachedWidgets = null; } module.exports = { From 73bb95a6aaa9801a6963f4073c04ed0af0709112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 14:56:12 +0200 Subject: [PATCH 8/9] style(angular): enforce semicolons at the end of statements --- eslint.config.mjs | 1 + src/angular/modules/rememberDashboardWidgets.js | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9bed7ca..1174a8c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -66,6 +66,7 @@ export default defineConfig([globalIgnores(["**/*.js", "**/*.mjs", "!src/**/*.js "one-var": ["error", "never"], "prefer-const": "error", "prefer-template": "error", + "semi": ["error", "always"], radix: "error", }, }]); diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js index a878ed4..07398ab 100644 --- a/src/angular/modules/rememberDashboardWidgets.js +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -60,9 +60,9 @@ async function loadWidgetStates() { $(this).click(); } }); - }) + }); - console.debug("[rememberDashboardWidgets] loaded open widgets:", openWidgets) + console.debug("[rememberDashboardWidgets] loaded open widgets:", openWidgets); } async function saveOpenWidgets() { @@ -79,7 +79,7 @@ async function saveOpenWidgets() { .get(); storage.setForUser("dashboardOpenWidgets", openWidgets); - console.debug("[rememberDashboardWidgets] saved open widgets:", openWidgets) + console.debug("[rememberDashboardWidgets] saved open widgets:", openWidgets); } async function init() { @@ -101,4 +101,4 @@ module.exports = { initialize: init, // Do actions before destroying destroy, -} +}; From 3056e0116e2b12c069f38875878c1dc22bbf9479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gutyina=20Gerg=C5=91?= Date: Sun, 11 May 2025 15:03:51 +0200 Subject: [PATCH 9/9] fixup! fix(angular): use text instead of id for identifying widgets --- src/angular/modules/rememberDashboardWidgets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/angular/modules/rememberDashboardWidgets.js b/src/angular/modules/rememberDashboardWidgets.js index 07398ab..8e6cdca 100644 --- a/src/angular/modules/rememberDashboardWidgets.js +++ b/src/angular/modules/rememberDashboardWidgets.js @@ -23,7 +23,7 @@ async function getWidgets() { // Areas of the widgets that // - are clickable - // - have an ID that is hopefully stable + // - have a child element who's text can be used for identification (widget__title class) // - have a class that indicates if it is open or closed (mat-expanded) const widgets = widgetRoots.find("mat-expansion-panel-header");