feat(pwa): app-icon badge with the highlight count via navigator.setAppBadge (#154)
CI / Build & Quality Checks (push) Successful in 1m29s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 15s
CI / Trigger Desktop Build (push) Successful in 11s
CI / Playwright smoke (e2e) (push) Successful in 2m4s
CI / Build & Quality Checks (push) Successful in 1m29s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 15s
CI / Trigger Desktop Build (push) Successful in 11s
CI / Playwright smoke (e2e) (push) Successful in 2m4s
Same number as the tab title (leaf-room highlights), cleared at zero. Skipped under Tauri where the native set_badge_count owns the badge, and silently absent where the Badging API is not available. Closes #154 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -1179,6 +1179,10 @@ Links you paste, send, edit, or merely _see_ lose ad/analytics identifiers — `
|
||||
|
||||
The syncable subset of Lotus settings (theme, composer toolbar order, notification/quiet-hour preferences, call keys, privacy toggles, …) is mirrored to the `io.lotus.settings` account-data event on the user's own homeserver and applied on every other device. Device-bound keys stay local (`DEVICE_LOCAL_KEYS` in `src/app/utils/settingsSync.ts`: page zoom, media auto-load, animation pause, glassmorphism, noise-suppression tier/model, bitrates, volumes, notification permission, developer tools, PTT mode, camera-on-join, drawer state). Conflicts are last-write-wins on an `updatedAt` stamp forced monotonic per device; a per-account `lastSyncedAt` marker in localStorage stops a device from echoing a snapshot it just applied. **Settings → General → Sync** has the toggle (itself device-local), **Push now** (make this device win everywhere) and **Clear synced copy**. Hook: `src/app/hooks/useSettingsSync.ts`, mounted from `ClientNonUIFeatures`.
|
||||
|
||||
### PWA App-Icon Badge (Gitea #154)
|
||||
|
||||
When Lotus Chat is installed as a PWA (Android Chrome, desktop Chrome/Edge), the app icon carries a numeric badge via the Badging API (`navigator.setAppBadge`). The number is the same highlight count (mentions/DMs, leaf rooms only) that the tab title shows, so the two can never disagree; it clears when the count reaches zero. Lives in `FaviconUpdater` (`ClientNonUIFeatures.tsx`) next to the title/favicon logic. No-op in a plain browser tab (the API is absent) and under Tauri, where the native `set_badge_count` already owns the badge.
|
||||
|
||||
### Media Gallery
|
||||
|
||||
`MediaGallery.tsx` — a right-side drawer for browsing room media.
|
||||
|
||||
@@ -59,7 +59,7 @@ import { toastQueueAtom } from '../../state/toast';
|
||||
import { useReminders } from '../../hooks/useReminders';
|
||||
import { getRoomRetentionMs, isExpired } from '../../utils/retention';
|
||||
import { useTauriUpdater } from '../../hooks/useTauriUpdater';
|
||||
import { invokeTauri } from '../../hooks/useTauri';
|
||||
import { invokeTauri, isTauri as isTauriApp } from '../../hooks/useTauri';
|
||||
import { TauriDesktopFeatures } from '../../components/TauriDesktopFeatures';
|
||||
import { KeyboardShortcutsDialog, useKeyboardShortcutsTrigger } from '../../features/shortcuts';
|
||||
import { useRoomsListener } from '../../hooks/useRoomsListener';
|
||||
@@ -153,6 +153,23 @@ function FaviconUpdater() {
|
||||
} else {
|
||||
document.title = 'Lotus Chat';
|
||||
}
|
||||
|
||||
// [Gitea #154] Installed-PWA app-icon badge (Android Chrome, desktop
|
||||
// Chrome/Edge). Same number as the title so the two never disagree:
|
||||
// highlights only, mirroring the desktop app's native `set_badge_count`
|
||||
// (which owns the badge under Tauri, so skip there). Plain tabs and
|
||||
// browsers without the Badging API simply have no `setAppBadge`.
|
||||
if (!isTauriApp()) {
|
||||
const nav = navigator as Navigator & {
|
||||
setAppBadge?: (count?: number) => Promise<void>;
|
||||
clearAppBadge?: () => Promise<void>;
|
||||
};
|
||||
if (totalHighlight > 0) {
|
||||
nav.setAppBadge?.(totalHighlight).catch(() => {});
|
||||
} else {
|
||||
nav.clearAppBadge?.().catch(() => {});
|
||||
}
|
||||
}
|
||||
}, [roomToUnread]);
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user