Commit Graph
9 Commits
Author SHA1 Message Date
Linus Rath 7533fc516b fix: renew push for every connected account, not only the active one #1054
The background resync only touched the active client, so accounts
turned on from the all-accounts panel lapsed when the server's push
subscription expired after seven days. The resync now walks every
connected login in turn, active one first, and runs again once the
background logins finish connecting. Single-account behaviour is
unchanged.

The panel also lists which accounts failed to enable and why, instead
of only a count.
2026-10-10 19:24:43 +02:00
KazNIISA IT e7d3782344 feat(pwa): badge the installed app icon with unread count (Badging API)
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.
2026-09-03 11:37:36 +05:00
Michael Richter 84617d6186 feat: OAuth handler for plugins; extended contacts and addressbook API 2026-08-31 09:55:06 +02:00
Linus Rath 4bf3a3f8bb feat: re-sync existing push registrations on app start 2026-08-26 21:56:58 +02:00
Linus Rath 5b8ddc352b feat: pick push relay from admin-defined list instead of a URL field 2026-08-16 23:04:22 +02:00
Linus Rath e9c4929913 fix: address review feedback on push onboarding
- track session-only dismissal per account instead of a single account id
- label the close button with a dedicated dismiss aria string in all locales
- read PWA install prompt visibility from a shared flag and subscribe in a
  layout effect so an early visibility event cannot be missed
- restore real timers after the push onboarding tests
2026-08-06 14:46:32 +02:00
Eduard Polonskyi e036e10004 fix: sequence PWA and push onboarding 2026-08-03 15:31:17 +02:00
Eduard Polonskyi 573fab8b83 feat: add background notification onboarding 2026-08-03 03:23:08 +02:00
honzup 334fdbfb86 feat: show unread count badge on favicon
Closes #560.

Composes the active inbox's unread count over the base favicon as an SVG
badge, served as a percent-encoded data: URL, so new mail is visible on a
tab that is not focused — including when the browser collapses tabs to
icon-only, where a title-based count disappears entirely.

The base icon is read from the rendered <link rel="icon"> rather than from
config, so admin and per-domain branding overrides are inherited for free:
the count is drawn on whatever logo the deployment actually serves. Keeping
the badge in SVG rather than rasterising to a canvas also means the browser
can rasterise it at whatever size it asks for, so a HiDPI tab is not served a
16px bitmap.

Notes on the approach:

- The badge link is an *additional* icon link that we append and mark as
  ours; we never remove or mutate a link we did not create. Next's metadata
  icons are rendered by React, which keeps a fiber pointing at that DOM node,
  so removing it would leave React holding a detached node and throw
  "Cannot read properties of null (reading 'removeChild')" on the next
  commit that deletes the fiber. Appending instead means the last-declared
  icon wins, and non-SVG fallback links survive with their type/sizes intact.
  (The usual recipe for this feature — assign canvas.toDataURL() to the
  existing link's href — does both of the things that break here.)

- Every change of state is an *insertion* of a fresh link of ours, never a
  mutation or a removal, because that is the only signal a browser reliably
  re-reads the favicon on. Firefox ignores an in-place href change, and it
  equally ignores a removal — so clearing the badge by deleting our link left
  a stale count painted on the tab until a hard reload. Clearing it instead
  inserts a new link of ours carrying the original base href.

- Holding last place has to be defended: on a client-side navigation React
  re-hoists its metadata icon link into <head>, landing after ours, and the
  base icon silently wins again. A MutationObserver on <head> moves our own
  link back to the end whenever a foreign icon link appears — moving only our
  node, never anyone else's. It no-ops once ours is last again, so a move
  cannot feed itself.

- The badge is a full-width band across the foot of the icon, drawn to the
  metrics measured from Gmail's own 16px favicon: band height 0.625 of the
  icon, digit cap height 0.44, flush to the edges, corners rounded by about a
  pixel. Full width is what keeps a three-glyph label legible — rounded ends
  waste exactly the horizontal space it needs. Neutral white with black digits
  rather than the conventional red: faviconUrl is admin-overridable and
  Bulwark's own icon is rgb(219,45,84), so a red badge sat red-on-red.

- The base SVG may be admin-uploaded, and the branding route deliberately
  serves it under a sandboxing CSP because SVG can carry script. Re-emitting
  it as a same-origin data: URL would un-fence that, so script, foreignObject
  and every on* handler are stripped before serialising.

- Mounted in the root layout, not on the mail route: the badge belongs to the
  tab, so mounting it on the page would clear it on every hop to settings,
  calendar or contacts.
2026-07-16 18:00:41 +02:00