desktop v0.1.20 + web: notify on download complete (was silent) (batch135)
Suppressing the save dialog made downloads completely silent — "nothing happened" even though the file saved to Downloads. Now on download 'done': (1) tell the web UI → branded toast "Saved X to your Downloads folder" when the window is focused; (2) native OS notification (click → reveal in Explorer) when the app is minimized/in the tray, so it's never double-noticed; (3) a failure notice. preload exposes onDownloadDone; home.html shows the toast. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -465,6 +465,24 @@ function configureSession() {
|
|||||||
let target = path.join(dir, name), n = 1;
|
let target = path.join(dir, name), n = 1;
|
||||||
while (fs.existsSync(target)) target = path.join(dir, `${base} (${n++})${ext}`);
|
while (fs.existsSync(target)) target = path.join(dir, `${base} (${n++})${ext}`);
|
||||||
item.setSavePath(target);
|
item.setSavePath(target);
|
||||||
|
// Since we suppressed the save dialog, the download would otherwise be completely silent. Give feedback
|
||||||
|
// when it finishes: a notification (click → reveal the file in Explorer) so the user knows it saved and
|
||||||
|
// where. Also tell the web UI so it can show its own toast. On failure, say so.
|
||||||
|
item.once('done', (_ev, state) => {
|
||||||
|
try {
|
||||||
|
const ok = state === 'completed';
|
||||||
|
const savedName = ok ? path.basename(target) : (item.getFilename() || 'file');
|
||||||
|
// Always tell the web UI (it shows a branded toast when the window is up).
|
||||||
|
try { if (win && !win.isDestroyed()) win.webContents.send('download-done', { name: savedName, path: ok ? target : '', ok }); } catch (_) {}
|
||||||
|
// If the window is focused, that toast is enough — skip the native notification to avoid a double
|
||||||
|
// notice. If the app is minimized/in the tray, show a native notification instead (click → reveal).
|
||||||
|
const focused = win && !win.isDestroyed() && win.isVisible() && win.isFocused();
|
||||||
|
if (!focused && Notification.isSupported()) {
|
||||||
|
if (ok) { const note = new Notification({ title: 'Download complete', body: savedName + ' — saved to your Downloads folder. Click to show it.' }); note.on('click', () => { try { shell.showItemInFolder(target); } catch (_) {} }); note.show(); }
|
||||||
|
else new Notification({ title: 'Download failed', body: savedName + ' could not be saved.' }).show();
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "biz-connect-desktop",
|
"name": "biz-connect-desktop",
|
||||||
"version": "0.1.19",
|
"version": "0.1.20",
|
||||||
"description": "Biz Connect technician desktop client — loads the Connect web UI with native screen capture",
|
"description": "Biz Connect technician desktop client — loads the Connect web UI with native screen capture",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "BizGaze",
|
"name": "BizGaze",
|
||||||
|
|||||||
@@ -43,5 +43,6 @@ contextBridge.exposeInMainWorld('bizConnectNative', Object.freeze({
|
|||||||
// #3: subscribe to auto-update lifecycle events so the UI can show download progress + "ready".
|
// #3: subscribe to auto-update lifecycle events so the UI can show download progress + "ready".
|
||||||
// cb receives {phase:'checking'|'available'|'downloading'|'ready'|'current'|'error', percent?, version?}.
|
// cb receives {phase:'checking'|'available'|'downloading'|'ready'|'current'|'error', percent?, version?}.
|
||||||
onUpdateEvent: (cb) => { try { ipcRenderer.on('update-event', (_e, data) => { try { cb(data); } catch (_) {} }); } catch (_) {} },
|
onUpdateEvent: (cb) => { try { ipcRenderer.on('update-event', (_e, data) => { try { cb(data); } catch (_) {} }); } catch (_) {} },
|
||||||
|
onDownloadDone: (cb) => { try { ipcRenderer.on('download-done', (_e, data) => { try { cb(data); } catch (_) {} }); } catch (_) {} }, // desktop: a file finished downloading to the Downloads folder → show a toast
|
||||||
restartToUpdate: () => ipcRenderer.invoke('restart-to-update'),
|
restartToUpdate: () => ipcRenderer.invoke('restart-to-update'),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1158,7 +1158,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="/icons.js?v=6"></script>
|
<script src="/icons.js?v=6"></script>
|
||||||
<script>window.__BUILD='2026-07-19-batch134';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
<script>window.__BUILD='2026-07-19-batch135';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
||||||
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
|
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
|
||||||
//
|
//
|
||||||
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
|
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
|
||||||
@@ -3408,6 +3408,10 @@ function wireUpdateBanner(){
|
|||||||
else if(d.phase==='ready'){ ensure().innerHTML=ic('check',15)+' <span>Update ready</span> <button id="updRestart">Restart now</button>'; el.classList.add('show','ready'); const b=el.querySelector('#updRestart'); if(b) b.onclick=()=>{ try{ n.restartToUpdate&&n.restartToUpdate(); }catch(_){} }; }
|
else if(d.phase==='ready'){ ensure().innerHTML=ic('check',15)+' <span>Update ready</span> <button id="updRestart">Restart now</button>'; el.classList.add('show','ready'); const b=el.querySelector('#updRestart'); if(b) b.onclick=()=>{ try{ n.restartToUpdate&&n.restartToUpdate(); }catch(_){} }; }
|
||||||
else if(d.phase==='current'||d.phase==='error'){ if(el){ el.classList.remove('show'); setTimeout(()=>{ if(el&&!el.classList.contains('show')) el.remove(); }, 400); } }
|
else if(d.phase==='current'||d.phase==='error'){ if(el){ el.classList.remove('show'); setTimeout(()=>{ if(el&&!el.classList.contains('show')) el.remove(); }, 400); } }
|
||||||
});
|
});
|
||||||
|
// Desktop: a download finished (saved straight to the Downloads folder, no dialog) → confirm with a toast
|
||||||
|
// so it never feels like "nothing happened". The native app also shows a click-to-reveal OS notification
|
||||||
|
// when its window isn't focused.
|
||||||
|
if(n.onDownloadDone) n.onDownloadDone((d)=>{ if(!d) return; toast(d.ok ? ('Saved “'+(d.name||'file')+'” to your Downloads folder') : ('Couldn’t download '+(d.name||'the file'))); });
|
||||||
}
|
}
|
||||||
// ---- New-web-build detection ----------------------------------------------------------------
|
// ---- New-web-build detection ----------------------------------------------------------------
|
||||||
// The desktop app closes to TRAY, so it can stay open for weeks and keep running the page it loaded on
|
// The desktop app closes to TRAY, so it can stay open for weeks and keep running the page it loaded on
|
||||||
|
|||||||
Reference in New Issue
Block a user