feat(dashboard): pages about one row update in place instead of reloading

Nav.refresh() fetches the current page and compares the server's HTML
now with the HTML the page started from; only what differs is patched
into the live page (text, attributes, class changes, rows added or
taken away). Typed input stays (the browser keeps what the user
changed while the default value follows the server), and parts built
by the page's scripts are left alone. Where a patch would lose
script-built or script-bound parts, or the page's layout changed, the
page is swapped in again with its scripts, keeping the scroll
position, open tabs, open folding parts and typed input; while the
user is typing it offers a refresh instead.

Live.refreshPage (account, character, bug report and play key pages)
uses it instead of reloading the page, and the "This changed while you
were editing" banner's Refresh does too.

Check: open a play key; in another tab change its uses or active
switch: the first tab's values and badge change without a reload,
and notes typed there stay. Same on a bug report page with a half
typed resolution when it's resolved elsewhere. On a character page,
an in-game save updates it without losing the open tab or scroll.
Test: NavRulesJs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 01:57:17 -05:00
parent f58255ab8a
commit b66bae6a9c
5 changed files with 215 additions and 16 deletions

View File

@@ -189,7 +189,10 @@
button.type = 'button';
button.className = 'btn btn-sm btn-primary';
button.textContent = 'Refresh';
button.addEventListener('click', reloadKeepingScroll);
button.addEventListener('click', function () {
banner.remove();
if (window.Nav) Nav.refresh({ force: true }); else reloadKeepingScroll();
});
banner.append(text, button);
document.body.appendChild(banner);
}
@@ -237,15 +240,19 @@
Live.on(table, throttle(function () { if (generation === pageGeneration) dataTable.ajax.reload(null, false); }, RELOAD_THROTTLE_MS));
},
/**
* For server-rendered pages about one row: reload the page when that row changes in any of the given tables
* (or any row, with anyRow). If the user is typing or has a dialog open, offer a refresh instead.
* For server-rendered pages about one row: update the page in place when that row changes in any of the given
* tables (or any row, with anyRow). Nav.refresh patches only what the server shows differently, so what is being
* typed stays; where it can't (the page would be rebuilt) and the user is busy, it offers a refresh instead.
* Without nav.js the page reloads, or offers a refresh while the user is busy.
*/
refreshPage: function (tables, id, options) {
options = options || {};
var generation = pageGeneration;
var refresh = throttle(function () {
if (generation !== pageGeneration) return;
if (isBusy()) showStaleBanner(); else reloadKeepingScroll();
if (window.Nav) Nav.refresh();
else if (isBusy()) showStaleBanner();
else reloadKeepingScroll();
}, PAGE_REFRESH_MIN_MS);
[].concat(tables).forEach(function (table) {
Live.on(table, function (e) {
@@ -254,6 +261,8 @@
});
},
throttle: throttle,
isBusy: isBusy,
showStaleBanner: showStaleBanner,
onStatus: function (cb) { statusWatchers.push(cb); },
// Forget what the page registered (nav.js, when it swaps in another page). Topics only it asked for are dropped.
resetPage: function () {