feat: settings banner when OAuth tokens use file fallback (#81) #86

Merged
maximus merged 2 commits from issue-81-fallback-banner into main 2026-04-14 12:21:38 +00:00
Owner

Fixes #81
Refs #66

Summary

  • getTokenStoreMode() wrapper in authService.ts calling the Tauri command added in #78.
  • TokenStoreFallbackBanner component: amber warning card that renders only when mode === "file". Silent (returns null) when keychain is active or mode is null (not yet authenticated).
  • Mounted in SettingsPage right under AccountCard so the user sees the warning where they manage their session.
  • FR + EN i18n keys under account.tokenStore.fallback.*.

UX

  • Keychain active → banner invisible, zero noise.
  • File fallback → amber card with ShieldAlert icon, localized title + description explaining the state and how to recover (keyring service needed).
  • No dismissal button on purpose: the condition is persistent and re-appears on every restart as long as the app is in degraded mode.

Test plan

  • npm run build clean
  • npm test 25/25 vitest tests pass
  • Manual: DBUS_SESSION_BUS_ADDRESS=/dev/null start → banner visible in Settings (deferred to #82)
  • Manual: normal keychain start → banner invisible
  • Manual: unauthenticated state (no tokens yet) → banner invisible
Fixes #81 Refs #66 ## Summary - `getTokenStoreMode()` wrapper in `authService.ts` calling the Tauri command added in #78. - `TokenStoreFallbackBanner` component: amber warning card that renders only when `mode === "file"`. Silent (returns null) when keychain is active or mode is null (not yet authenticated). - Mounted in `SettingsPage` right under `AccountCard` so the user sees the warning where they manage their session. - FR + EN i18n keys under `account.tokenStore.fallback.*`. ## UX - Keychain active → banner invisible, zero noise. - File fallback → amber card with `ShieldAlert` icon, localized title + description explaining the state and how to recover (keyring service needed). - No dismissal button on purpose: the condition is persistent and re-appears on every restart as long as the app is in degraded mode. ## Test plan - [x] `npm run build` clean - [x] `npm test` 25/25 vitest tests pass - [ ] Manual: `DBUS_SESSION_BUS_ADDRESS=/dev/null` start → banner visible in Settings (deferred to #82) - [ ] Manual: normal keychain start → banner invisible - [ ] Manual: unauthenticated state (no tokens yet) → banner invisible
maximus added 1 commit 2026-04-14 12:19:08 +00:00
feat: settings banner when OAuth tokens fall back to file store (#81)
Some checks are pending
PR Check / rust (push) Waiting to run
PR Check / frontend (push) Waiting to run
PR Check / rust (pull_request) Successful in 22m28s
PR Check / frontend (pull_request) Successful in 2m19s
3b1c41c48e
Adds a visible warning in the Settings page when `token_store` has
landed in the file fallback instead of the OS keychain. Without this,
a user on a keychain-less system would silently lose the security
benefit introduced in #78 and never know.

- New `get_token_store_mode` service wrapper in authService.ts.
- New `TokenStoreFallbackBanner` component: fetches the mode on mount,
  renders nothing when mode is `keychain` or null, renders an
  amber warning card when mode is `file`.
- Mounted in SettingsPage right after AccountCard so it sits next to
  the account state the user can fix (log out + log back in once the
  keychain is available).
- i18n keys under `account.tokenStore.fallback.*` in fr/en.

Refs #66

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
maximus added 1 commit 2026-04-14 12:20:24 +00:00
feat: dismissable banner with session-storage memory (#81)
All checks were successful
PR Check / rust (push) Successful in 22m28s
PR Check / frontend (push) Successful in 2m17s
PR Check / rust (pull_request) Successful in 22m30s
PR Check / frontend (pull_request) Successful in 2m18s
9a9d3c89b9
Adds a close button and session-scoped dismissal flag so the banner
can be acknowledged for the current run but reappears on the next
app launch if the fallback is still active — matches the #81
acceptance criterion.

- sessionStorage key survives page navigation within the run, is
  cleared on app restart.
- Graceful on storage quota errors.
- New `common.close` i18n key (FR: "Fermer", EN: "Close") used as
  the aria-label of the close button.
Author
Owner

Review — APPROVE ✓

Security

  • ✓ Aucune chaîne en dur — tout passe par i18n (fr/en)
  • ✓ Pas de XSS : seulement du texte rendu via React
  • sessionStorage dans un try/catch pour les cas où le storage est indisponible

Correctness

  • ✓ Cancellation flag cancelled dans le useEffect évite les setState post-unmount
  • ✓ Render null quand mode !== "file" ou dismissed — pas de flash
  • ✓ Dismissal scope sessionStorage : réapparaît au prochain démarrage de l'app si le fallback est toujours actif (matche le critère d'acceptance)
  • ✓ Graceful error handling sur getTokenStoreMode — la bannière se masque plutôt que planter

Quality

  • ✓ Composant simple, un seul useEffect, deux useState
  • ✓ Utilise les design tokens existants (var(--foreground), var(--muted-foreground)) + amber-500 pour la couleur warning (consistant avec d'autres cards d'alerte dans le projet)
  • aria-label sur le bouton close (accessibilité)
  • ✓ Nouvelle clé common.close (FR: Fermer, EN: Close)

Observations

  • Le composant ne fait qu'un fetch au mount, pas de refresh après logout. Mineur : la bannière est sur SettingsPage qui se remount si l'utilisateur quitte la page, ce qui suffit en pratique.
  • Pas de lien vers une doc externe pour le keyring — pourrait être ajouté en follow-up si on a un FAQ utilisateur.

Verdict : APPROVE

## Review — APPROVE ✓ ### Security - ✓ Aucune chaîne en dur — tout passe par i18n (fr/en) - ✓ Pas de XSS : seulement du texte rendu via React - ✓ `sessionStorage` dans un try/catch pour les cas où le storage est indisponible ### Correctness - ✓ Cancellation flag `cancelled` dans le useEffect évite les setState post-unmount - ✓ Render null quand `mode !== "file"` ou `dismissed` — pas de flash - ✓ Dismissal scope `sessionStorage` : réapparaît au prochain démarrage de l'app si le fallback est toujours actif (matche le critère d'acceptance) - ✓ Graceful error handling sur `getTokenStoreMode` — la bannière se masque plutôt que planter ### Quality - ✓ Composant simple, un seul useEffect, deux useState - ✓ Utilise les design tokens existants (`var(--foreground)`, `var(--muted-foreground)`) + amber-500 pour la couleur warning (consistant avec d'autres cards d'alerte dans le projet) - ✓ `aria-label` sur le bouton close (accessibilité) - ✓ Nouvelle clé `common.close` (FR: Fermer, EN: Close) ### Observations - Le composant ne fait qu'un fetch au mount, pas de refresh après logout. Mineur : la bannière est sur SettingsPage qui se remount si l'utilisateur quitte la page, ce qui suffit en pratique. - Pas de lien vers une doc externe pour le keyring — pourrait être ajouté en follow-up si on a un FAQ utilisateur. **Verdict : APPROVE**
maximus merged commit 745f71782f into main 2026-04-14 12:21:38 +00:00
maximus deleted branch issue-81-fallback-banner 2026-04-14 12:21:39 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#86
No description provided.