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 = () => {