Skip to content

feat(pwa): badge the installed app icon with unread count (Badging API) - #934

Open
dev-hive-kazniisa wants to merge 1 commit into
bulwarkmail:mainfrom
dev-hive-kazniisa:feat/pwa-app-badge
Open

feat(pwa): badge the installed app icon with unread count (Badging API)#934
dev-hive-kazniisa wants to merge 1 commit into
bulwarkmail:mainfrom
dev-hive-kazniisa:feat/pwa-app-badge

Conversation

@dev-hive-kazniisa

Copy link
Copy Markdown
Contributor

What

Adds useAppBadge, which mirrors the existing favicon unread badge onto the installed app icon via the Badging API (navigator.setAppBadge / clearAppBadge).

Chrome, Edge and WebKit render a separate icon for an installed PWA — taskbar, dock, Home Screen, pinned app shortcut — and that icon does not react to favicon changes at all. So a user who installs Bulwark as an app currently loses the unread indicator that tab users get. A merely pinned browser tab is unaffected either way; this only touches an actually installed app.

How

  • New hook hooks/use-app-badge.ts, mounted from the same FaviconBadge component and gated by the same faviconUnreadBadge setting — no new setting, no new i18n keys.
  • Feature-detected; a no-op where the API is unavailable.
  • No unmount cleanup on purpose. The badge is scoped to the installed app/origin, not to this component or window, so clearing it on every unmount would blank another still-open window's accurate badge. A count of zero, or the setting being turned off, already clears it through the main effect.

i18n

No new keys. The existing favicon_unread_badge label/description wording in en now mentions the app icon as well, and the ru translation for that key is filled in (it was still English). Per CONTRIBUTING, the other 22 locales fall back to English for the reworded string — happy to add them in this PR or a follow-up if you'd prefer them updated now.

Tests

hooks/__tests__/use-app-badge.test.tsx (new, 9 cases) and 4 added cases in components/__tests__/favicon-badge.test.tsx — 13 passing. npm run typecheck and npm run lint clean.

Notes

Running in production on a ~280-mailbox Stalwart deployment since 2026-08-14.

Adds useAppBadge alongside the existing useFaviconBadge, mounted from the
same FaviconBadge component and gated by the same faviconUnreadBadge
setting. Targets the separate icon Chrome/Edge/WebKit show for an
installed PWA (taskbar/dock/Home Screen/pinned-app-shortcut), which does
not react to favicon changes at all - a merely pinned browser tab is
unaffected, only an actually installed app.

No unmount cleanup: the badge is scoped to the installed app/origin, not
to this component or window, so clearing on every unmount would blank
another still-open window's accurate badge. A count of zero or the
setting being off already clears it through the main effect.
@dev-hive-kazniisa

Copy link
Copy Markdown
Contributor Author

Rebased onto current main — it had gone conflicting. The only conflict was in locales/{en,ru}/common.json, which main re-checked-in with CRLF line endings since this branch was cut; re-applied the two favicon_unread_badge string edits on top of the CRLF files, no key added or removed.

Verified on the rebased branch: npx tsc --noEmit clean, npx vitest run hooks/__tests__/use-app-badge.test.tsx components/__tests__/favicon-badge.test.tsx 13/13 green. (npm run test:translations fails identically on unmodified main — 5 keys missing in zh-TW — so it is unrelated to this branch.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant