feat(desktop): Phase D — inline-reply notifications (Windows toast reply box)
Reply to a chat straight from the OS notification, no app switch:
- node-notifier (bundles SnoreToast) renders a native Windows toast with a reply box —
Electron's own Notification can't do Windows inline reply.
- main.js reply-notification handler resolves {text}|{open}|null; preload exposes replyNotify.
- home.html notify() routes chat toasts through it on desktop: a typed reply -> sendReplyTo()
POSTs to /api/messages without opening the app; a click opens the chat. Web/PWA path unchanged.
- Works only in the installed app (needs the installer's AppUserModelID). desktop 0.1.2, build batch21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,33 @@ ipcMain.on('get-install-info', (e) => {
|
||||
// on the next restart. No-op in dev (unpackaged).
|
||||
let autoUpdater = null;
|
||||
try { ({ autoUpdater } = require('electron-updater')); } catch (_) { /* not installed in dev */ }
|
||||
// node-notifier bundles SnoreToast, which renders a native Windows toast WITH a reply box
|
||||
// (Electron's own Notification can't do Windows inline reply). Only works in the installed app
|
||||
// (needs the AppUserModelID shortcut the NSIS installer registers).
|
||||
let notifier = null;
|
||||
try { notifier = require('node-notifier'); } catch (_) { /* optional */ }
|
||||
|
||||
// Show a chat notification with an inline reply box. Resolves with the typed reply, an "open"
|
||||
// intent (toast clicked), or null (dismissed/timeout). The renderer sends the reply / opens chat.
|
||||
ipcMain.handle('reply-notification', (_e, payload = {}) => new Promise((resolve) => {
|
||||
if (!notifier) return resolve(null);
|
||||
let done = false; const finish = (v) => { if (!done) { done = true; resolve(v); } };
|
||||
try {
|
||||
notifier.notify({
|
||||
appID: 'com.bizgaze.connect.desktop',
|
||||
title: payload.title || 'Biz Connect',
|
||||
message: payload.body || '',
|
||||
reply: true, wait: true, timeout: 20,
|
||||
}, (err, response, metadata) => {
|
||||
if (err) return finish(null);
|
||||
const text = (metadata && metadata.activationValue) ? String(metadata.activationValue).trim() : '';
|
||||
const resp = String(response || '').toLowerCase();
|
||||
if (text && resp !== 'activate') finish({ kind: payload.kind, id: payload.id, text });
|
||||
else if (resp === 'activate' || resp === 'clicked') finish({ kind: payload.kind, id: payload.id, open: true });
|
||||
else finish(null);
|
||||
});
|
||||
} catch (_) { finish(null); }
|
||||
}));
|
||||
|
||||
// Windows attributes notifications to the AppUserModelID. Without setting it, toasts read
|
||||
// "electron.app.<name>"; setting it to the installer's appId makes Windows resolve the
|
||||
|
||||
Reference in New Issue
Block a user