Fix/dismissible infinite toasts (#1319)
* fix: make PWA install and OpenCode update toasts dismissible Both 'Install OpenChamber' and 'OpenCode update available' toasts use duration: Infinity with no close affordance, so they persist on screen until the user accepts (install/update) or reloads the tab. For users who do not want to install the PWA or upgrade right now, this is intrusive and there is no opt-out. Add a Dismiss button (sonner cancel action) to both toasts. When the user dismisses: - PWA: persist a flag in localStorage so the prompt does not reappear on future sessions. Accepting Install still works as before. - OpenCode update: persist the dismissed version in localStorage. The toast will appear again only when a newer version becomes available. Adds new i18n keys pwa.installPrompt.dismiss and opencodeUpdate.toast.actions.dismiss across all seven locales (en, es, ko, pl, pt-BR, uk, zh-CN). * test: extract toast dedup helpers and cover with 28 unit tests Lift the dismissal-decision logic out of usePwaInstallPrompt and OpenCodeUpdateToast into a React-free sibling module so it can be unit-tested directly. The React surfaces remain sole owners of side effects (storage writes, toast.info, event listeners); the new module only answers 'should we show?'. New module openCodeUpdateDedup.ts exposes four helpers: - shouldShowPwaInstallToast(input) - three gates: dismissed, sessionShown, hasActiveToast. - shouldShowOpenCodeUpdateToast(input) - empty version, seen set, dismissed===version gates; a different dismissed version lets the toast resurface for the new release. - resolveOpenCodeUpdateVersion(detail) - parses CustomEvent payloads defensively (null/non-object/non-string -> ''). - resolveOpenCodeUpgradeStatusVersion(status) - parses upgrade status payloads (status falsy / available!==true / latestVersion non-string -> ''). Consumers now call the helpers and only run the side-effect when the decision is true. Behaviour is unchanged. Coverage: 28 tests via bun:test, 33 expects, all pass first try.
This commit is contained in:
@@ -18,6 +18,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'common.revealPath.fileManager': 'Otwórz w Menedżerze plików',
|
||||
'pwa.installPrompt.description': 'Zainstaluj OpenChamber, aby mieć szybszy dostęp',
|
||||
'pwa.installPrompt.action': 'Zainstaluj',
|
||||
'pwa.installPrompt.dismiss': 'Odrzuć',
|
||||
'pwa.installPrompt.started': 'Instalacja rozpoczęta',
|
||||
'pwa.installPrompt.installed': 'OpenChamber został zainstalowany',
|
||||
'layout.mainTab.chat': 'Czat',
|
||||
@@ -2191,6 +2192,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'opencodeUpdate.toast.available.title': 'Dostępna aktualizacja OpenCode',
|
||||
'opencodeUpdate.toast.available.description': 'Wersja {version} jest gotowa do instalacji.',
|
||||
'opencodeUpdate.toast.actions.update': 'Aktualizuj',
|
||||
'opencodeUpdate.toast.actions.dismiss': 'Odrzuć',
|
||||
'opencodeUpdate.toast.actions.reload': 'Przeładuj OpenCode',
|
||||
'opencodeUpdate.toast.upgrading.title': 'Aktualizowanie OpenCode...',
|
||||
'opencodeUpdate.toast.upgrading.description': 'Pozostaw OpenChamber otwarte.',
|
||||
|
||||
Reference in New Issue
Block a user