feat(desktop): surface runtime alerts as system notifications
Add a runtime system notifier that pushes danger-severity activity (and forced account-access alerts) to the OS via the tray balloon on Windows, falling back to Electron Notification elsewhere, with a 30s per-key dedupe window. Route risk-control / human-verification account alerts through it so users are notified when an account needs attention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { nativeImage } from 'electron/common'
|
||||
import type { Tray as ElectronTray } from 'electron/main'
|
||||
import { Menu, Tray } from 'electron/main'
|
||||
import { Menu, Notification, Tray } from 'electron/main'
|
||||
|
||||
import { TRAY_DANGER_ICON_BASE64, TRAY_ICON_BASE64, TRAY_WINDOWS_ICON_BASE64 } from './brand-icons'
|
||||
|
||||
@@ -52,12 +52,12 @@ function formatIssueCount(count: number): string {
|
||||
// Windows-only: tray.displayBalloon shows a system toast next to the tray
|
||||
// icon. macOS and Linux ignore this call (Electron returns silently), so the
|
||||
// guard here is just to avoid the no-op log noise on non-Windows.
|
||||
export function showTrayBalloon(title: string, content: string): void {
|
||||
function displayTrayBalloon(title: string, content: string): boolean {
|
||||
if (process.platform !== 'win32') {
|
||||
return
|
||||
return false
|
||||
}
|
||||
if (!tray || tray.isDestroyed()) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
try {
|
||||
tray.displayBalloon({
|
||||
@@ -66,11 +66,38 @@ export function showTrayBalloon(title: string, content: string): void {
|
||||
icon: createTrayIcon('normal'),
|
||||
iconType: 'custom',
|
||||
})
|
||||
return true
|
||||
} catch (error) {
|
||||
console.warn('[desktop-tray] displayBalloon failed', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function showTrayBalloon(title: string, content: string): void {
|
||||
displayTrayBalloon(title, content)
|
||||
}
|
||||
|
||||
export function showSystemNotification(title: string, body: string): void {
|
||||
const normalizedTitle = title.trim()
|
||||
const normalizedBody = body.trim()
|
||||
if (!normalizedTitle || !normalizedBody) {
|
||||
return
|
||||
}
|
||||
if (displayTrayBalloon(normalizedTitle, normalizedBody)) {
|
||||
return
|
||||
}
|
||||
if (!Notification.isSupported()) {
|
||||
return
|
||||
}
|
||||
|
||||
const notification = new Notification({
|
||||
title: normalizedTitle,
|
||||
body: normalizedBody,
|
||||
silent: false,
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
export function updateTrayIssueIndicator(issueCount: number): void {
|
||||
if (!tray || tray.isDestroyed()) {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user