diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index b494b44..8d0a6c0 100644 Binary files a/public/apple-touch-icon.png and b/public/apple-touch-icon.png differ diff --git a/public/favicon-96x96.png b/public/favicon-96x96.png new file mode 100644 index 0000000..d3b391c Binary files /dev/null and b/public/favicon-96x96.png differ diff --git a/public/favicon.ico b/public/favicon.ico index f6aa227..23976c6 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg index fe412aa..0cb187d 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/providers/NotificationProvider.tsx b/src/providers/NotificationProvider.tsx index a56aead..8cccfaf 100644 --- a/src/providers/NotificationProvider.tsx +++ b/src/providers/NotificationProvider.tsx @@ -68,7 +68,12 @@ export function NotificationProvider({ children }: { children: React.ReactNode } !mutePubkeys.includes(evt.pubkey) && (!hideUntrustedNotifications || isUserTrusted(evt.pubkey)) ) { - setNewNotificationIds((prev) => new Set([...prev, evt.id])) + setNewNotificationIds((prev) => { + if (prev.has(evt.id)) { + return prev + } + return new Set([...prev, evt.id]) + }) } }, onclose: (reasons) => { @@ -127,11 +132,43 @@ export function NotificationProvider({ children }: { children: React.ReactNode } useEffect(() => { const newNotificationCount = newNotificationIds.size + + // Update title if (newNotificationCount > 0) { document.title = `(${newNotificationCount >= 10 ? '9+' : newNotificationCount}) Jumble` } else { document.title = 'Jumble' } + + // Update favicons + const favicons = document.querySelectorAll("link[rel*='icon']") + if (!favicons.length) return + + if (newNotificationCount === 0) { + favicons.forEach((favicon) => { + favicon.href = '/favicon.ico' + }) + } else { + const img = document.createElement('img') + img.src = '/favicon.ico' + img.onload = () => { + const size = Math.max(img.width, img.height, 32) + const canvas = document.createElement('canvas') + canvas.width = size + canvas.height = size + const ctx = canvas.getContext('2d') + if (!ctx) return + ctx.drawImage(img, 0, 0, size, size) + const r = size * 0.16 + ctx.beginPath() + ctx.arc(size - r - 6, r + 6, r, 0, 2 * Math.PI) + ctx.fillStyle = '#FF0000' + ctx.fill() + favicons.forEach((favicon) => { + favicon.href = canvas.toDataURL('image/png') + }) + } + } }, [newNotificationIds]) const getNotificationsSeenAt = () => {