From 1ca0b836e1ec91de934595f2e8b884958737d6ba Mon Sep 17 00:00:00 2001 From: sravan Date: Wed, 1 Jul 2026 16:28:15 +0530 Subject: [PATCH] feat(desktop): unread-chat count badge on the taskbar icon Mirrors the rail's unread-chat count onto the Windows taskbar icon (overlay) and the macOS/Linux dock badge. updateRailUnread() draws a small red count badge on a canvas and hands it to the shell via bizConnectNative.setUnread(count, dataUrl); main sets it with win.setOverlayIcon + app.setBadgeCount. Clears at 0, shows 99+ past 99. No-op in a browser/mobile. (home.html build batch17) Co-Authored-By: Claude Opus 4.8 --- desktop/main.js | 14 +++++++++++++- desktop/preload.js | 3 +++ server/public/home.html | 20 +++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/desktop/main.js b/desktop/main.js index 7d4622f..62476b4 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -8,7 +8,7 @@ // - external links open in the user's browser, not inside the app // // Server origin is configurable so the same build works against prod or a dev server. -const { app, BrowserWindow, session, desktopCapturer, shell, Menu, ipcMain } = require('electron'); +const { app, BrowserWindow, session, desktopCapturer, shell, Menu, ipcMain, nativeImage } = require('electron'); const path = require('path'); // Renderer asks (via preload) to raise the window — e.g. when an OS notification is clicked. @@ -19,6 +19,18 @@ ipcMain.on('focus-window', () => { win.focus(); }); +// Unread badge on the taskbar icon. The renderer computes the count (chats with unread) and +// draws the badge image (it has a canvas); Windows shows it via an overlay icon, macOS/Linux +// via the dock badge count. +ipcMain.on('set-unread', (_e, { count, dataUrl } = {}) => { + try { + if (typeof app.setBadgeCount === 'function') app.setBadgeCount(count || 0); // macOS/Linux dock + if (!win) return; + const overlay = (count > 0 && dataUrl) ? nativeImage.createFromDataURL(dataUrl) : null; + win.setOverlayIcon(overlay, count > 0 ? (count + ' unread chats') : ''); // Windows taskbar + } catch (_) {} +}); + const SERVER_URL = (process.env.SERVER_URL || 'https://remote.bizgaze.com').replace(/\/+$/, ''); let win; diff --git a/desktop/preload.js b/desktop/preload.js index a66424e..4aaa106 100644 --- a/desktop/preload.js +++ b/desktop/preload.js @@ -10,4 +10,7 @@ contextBridge.exposeInMainWorld('bizConnectNative', Object.freeze({ // Bring the app window to the foreground (e.g. when a notification is clicked) — the web // Notification's window.focus() can't raise an Electron window; the main process must. focusApp: () => ipcRenderer.send('focus-window'), + // Show the unread-chat count as a badge on the taskbar icon. count=number of chats with + // unread; dataUrl=a small PNG badge the renderer drew (null to clear). + setUnread: (count, dataUrl) => ipcRenderer.send('set-unread', { count, dataUrl }), })); diff --git a/server/public/home.html b/server/public/home.html index c1628bc..153011a 100644 --- a/server/public/home.html +++ b/server/public/home.html @@ -721,7 +721,7 @@ - +
Loading…
@@ -1097,6 +1097,24 @@ function updateRailUnread(){ let chats=0; ROWS.forEach(it=>{ if((it.unread||0)>0) chats++; }); // number of chats with unread, not total messages const d=document.getElementById('railUnread'); if(chats>0){ d.textContent=chats>99?'99+':chats; d.style.display='grid'; } else d.style.display='none'; + setDesktopBadge(chats); // mirror the count onto the desktop taskbar icon +} +// Desktop taskbar icon badge: draw a small red count badge and hand it to the Electron shell +// (which sets it as a taskbar overlay). No-op in a browser or on mobile. +function setDesktopBadge(count){ + try{ + if(!(window.bizConnectNative && window.bizConnectNative.setUnread)) return; + let dataUrl=null; + if(count>0){ + const s=32, c=document.createElement('canvas'); c.width=s; c.height=s; const g=c.getContext('2d'); + g.fillStyle='#ef4444'; g.beginPath(); g.arc(s/2,s/2,s/2,0,2*Math.PI); g.fill(); + g.fillStyle='#fff'; g.textAlign='center'; g.textBaseline='middle'; + const label=count>99?'99+':String(count); g.font='bold '+(label.length>2?15:20)+'px system-ui,Segoe UI,sans-serif'; + g.fillText(label, s/2, s/2+1); + dataUrl=c.toDataURL('image/png'); + } + window.bizConnectNative.setUnread(count, dataUrl); + }catch(_){} } async function loadSidebar(){ let convos=[], contacts=[];