feat: detect new web builds + hard-refresh escape hatch (0.1.17/batch78)
Root cause of "the fix works on mobile but not on desktop/web": nothing was wrong with the code — the desktop app now CLOSES TO TRAY, so it can run for weeks on the page it loaded on day one and never re-fetch after a deploy. There was also no way to force it off a stale page (no menu bar → no reload accelerator). - Server: GET /api/build returns home.html's __BUILD marker. - Client: polls it (boot, on focus/visibility, every 10 min); when the server's build differs from the running one, shows a branded "A new version is available — Refresh" banner. Settings gains an always-available "Refresh app" with the current build shown. - hardReloadApp(): in the browser it unregisters service workers + clears CacheStorage then reloads cache-busted; in the shell it calls the native hard reload. - Desktop: hard-reload IPC (clears the session HTTP cache + reloadIgnoringCache), wired to Ctrl+R (reload), Ctrl+Shift+R / F5 (hard reload), and a "Refresh app (get latest)" tray item. Previously there was literally no way to clear the cache from the app. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,17 @@ function avatarToTempPng(src) {
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Hard refresh -------------------------------------------------------------------------------
|
||||
// The app closes to TRAY, so it can run for weeks on the page it first loaded and never see a new web
|
||||
// deploy. This clears the shell's HTTP cache and reloads ignoring cache, so a stale UI is always
|
||||
// recoverable. Reachable from: the in-app "Refresh" banner / Settings, Ctrl+R (reload),
|
||||
// Ctrl+Shift+R or F5 (hard reload), and the tray menu.
|
||||
async function hardReloadWin() {
|
||||
try { await session.fromPartition('persist:bizconnect').clearCache(); } catch (_) {}
|
||||
try { if (win && !win.isDestroyed()) win.webContents.reloadIgnoringCache(); } catch (_) {}
|
||||
}
|
||||
ipcMain.handle('hard-reload', async () => { await hardReloadWin(); return true; });
|
||||
|
||||
// ---- Remote control: OS input injection for a screen the local user is SHARING ----
|
||||
// The renderer (share flow) forwards a viewer's mouse/keyboard events here for injection. Injection is
|
||||
// HARD-GATED behind an explicit consent flag (rcArmed): nothing is injected until the local user clicks
|
||||
@@ -207,6 +218,7 @@ function createTray() {
|
||||
const showApp = () => { if (!win) return createWindow(); if (win.isMinimized()) win.restore(); win.show(); win.focus(); };
|
||||
tray.setContextMenu(Menu.buildFromTemplate([
|
||||
{ label: 'Open Biz Connect', click: showApp },
|
||||
{ label: 'Refresh app (get latest)', click: () => { showApp(); hardReloadWin(); } },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Quit', click: () => { isQuitting = true; app.quit(); } },
|
||||
]));
|
||||
@@ -257,6 +269,19 @@ function createWindow() {
|
||||
win.once('ready-to-show', reveal);
|
||||
setTimeout(reveal, 12000);
|
||||
|
||||
// The menu bar is hidden, so the usual reload accelerators don't exist — wire them by hand. Without
|
||||
// these there was literally no way to force the app off a stale page.
|
||||
win.webContents.on('before-input-event', (e, input) => {
|
||||
if (input.type !== 'keyDown') return;
|
||||
const k = String(input.key || '').toLowerCase();
|
||||
const mod = input.control || input.meta;
|
||||
if ((mod && k === 'r') || k === 'f5') {
|
||||
e.preventDefault();
|
||||
if (input.shift || k === 'f5') hardReloadWin(); // hard: clear cache + reload
|
||||
else { try { win.webContents.reload(); } catch (_) {} } // plain reload
|
||||
}
|
||||
});
|
||||
|
||||
// Close = hide to tray (keep running for notifications). First time, tell the user where it went.
|
||||
let toldTray = false;
|
||||
win.on('close', (e) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "biz-connect-desktop",
|
||||
"version": "0.1.16",
|
||||
"version": "0.1.17",
|
||||
"description": "Biz Connect technician desktop client — loads the Connect web UI with native screen capture",
|
||||
"author": {
|
||||
"name": "BizGaze",
|
||||
|
||||
@@ -34,6 +34,9 @@ contextBridge.exposeInMainWorld('bizConnectNative', Object.freeze({
|
||||
rcAvailable: () => { try { return !!ipcRenderer.sendSync('rc-available'); } catch (_) { return false; } },
|
||||
rcArm: (on) => { try { ipcRenderer.send('rc-arm', !!on); } catch (_) {} },
|
||||
rcInput: (evt) => { try { ipcRenderer.send('rc-input', evt); } catch (_) {} },
|
||||
// Force the newest web build: clears the shell's HTTP cache and reloads ignoring cache. The app closes
|
||||
// to tray and can run for weeks, so without this it would keep serving the page it first loaded.
|
||||
hardReload: () => { try { return ipcRenderer.invoke('hard-reload'); } catch (_) { return Promise.resolve(false); } },
|
||||
// Manual "Check for updates" from Settings. Resolves {status:'available'|'current'|'dev'|'error', version?}.
|
||||
// On 'available' the shell downloads in the background and prompts to restart when ready.
|
||||
checkForUpdates: () => ipcRenderer.invoke('check-updates'),
|
||||
|
||||
Reference in New Issue
Block a user