mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
0126ef3401
Three gaps in the notice from the previous commit, found in review. A page served from the network and claimed later by its own build's worker counted as already taken over, so Reload went to that worker, got the old build again and spent the session's reload: no notice and no new build until the next start. Only a worker that replaced the one that served the page skips the handover now, as at launch. A reload that came before the new worker could take over (a slow precache, a failed update, an install gone redundant) also spent that guard, so the launch behind it could no longer finish the handover with its own reload. That reload leaves the guard alone now. An install whose server never reports the bundle's version (a hand-set APP_VERSION) showed the notice in every session, and Reload could not clear it. The store keeps the first version a page hears of, from the app config at launch, and the notice only asks when a later report differs: a different build at launch is the launch handover's to fix. The server's wire-envelope test pins the welcome frame's fields and now expects the version beside the socket id. The wiki's update page says what an open tab shows after an update.
71 lines
3.1 KiB
TypeScript
71 lines
3.1 KiB
TypeScript
/**
|
|
* NewVersionNotice: a release was deployed while this page stayed open.
|
|
*
|
|
* The server names its version on every socket (re)connect, and a deploy
|
|
* restarts it, so every open client hears of a new release within seconds. A
|
|
* launch brings an outdated page onto the current build by itself
|
|
* (versionHandover); a page in use is never reloaded under the user's hands, so
|
|
* this offers the reload instead, for a version that changed while the page was
|
|
* open, and the reload goes through the same handover.
|
|
*
|
|
* One notice for both shells, rendered once in App. Hidden offline, where a
|
|
* reload could not fetch the new build, and once dismissed for that version.
|
|
* It reads the error boundary's texts for the same situation, a tab that
|
|
* outlived a deploy.
|
|
*/
|
|
import React, { useState } from 'react'
|
|
import { RefreshCw, X } from 'lucide-react'
|
|
import { useServerVersionStore } from '../../store/serverVersionStore'
|
|
import { useNetworkMode } from '../../hooks/useNetworkMode'
|
|
import { offersNewBuild, switchToServerVersion } from '../../utils/versionHandover'
|
|
import { useTranslation } from '../../i18n'
|
|
|
|
export default function NewVersionNotice(): React.ReactElement | null {
|
|
const { t } = useTranslation()
|
|
const reported = useServerVersionStore(s => s.reported)
|
|
const first = useServerVersionStore(s => s.first)
|
|
const { offline } = useNetworkMode()
|
|
const [dismissed, setDismissed] = useState<string | null>(null)
|
|
const [switching, setSwitching] = useState(false)
|
|
|
|
// Only a version that changed while this page was open: a different build at
|
|
// launch is the launch handover's to fix, and an install whose server never
|
|
// reports the bundle's version would otherwise be asked every session.
|
|
if (offline || !reported || reported === first || reported === dismissed || !offersNewBuild(reported)) return null
|
|
|
|
const reload = (): void => {
|
|
setSwitching(true)
|
|
void switchToServerVersion(reported)
|
|
}
|
|
|
|
return (
|
|
<div
|
|
role="status"
|
|
aria-live="polite"
|
|
// Above the OfflineBanner pill, which sits 16px over the bottom nav.
|
|
style={{ bottom: 'calc(var(--bottom-nav-h) + 56px)' }}
|
|
className="fixed inset-x-4 z-[9999] flex items-center gap-3 rounded-xl border border-edge bg-surface-elevated px-4 py-3 text-content shadow-lg sm:inset-x-auto sm:start-4 sm:max-w-sm"
|
|
>
|
|
<RefreshCw size={16} className={`shrink-0 text-accent-on ${switching ? 'animate-spin' : ''}`} aria-hidden="true" />
|
|
<span className="min-w-0 flex-1 text-sm">{t('common.errorUpdateTitle')}</span>
|
|
<button
|
|
type="button"
|
|
onClick={reload}
|
|
disabled={switching}
|
|
className="shrink-0 rounded-lg bg-accent px-3 py-1.5 text-sm font-semibold text-accent-text hover:bg-accent-hover disabled:opacity-60"
|
|
>
|
|
{t('common.errorReload')}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setDismissed(reported)}
|
|
disabled={switching}
|
|
aria-label={t('common.close')}
|
|
className="shrink-0 rounded-md p-1 text-content-muted hover:bg-surface-hover disabled:opacity-60"
|
|
>
|
|
<X size={14} aria-hidden="true" />
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|