codeburn/app/renderer/components/UpdateBanner.tsx
iamtoruk 8fc9e303b9 feat(app): update-available notifications
Checks the GitHub releases for newer desktop-v tags once per launch
and every 24h (15s timeout, no identifying headers, offline = silent
retry). A dismissible banner and a Settings About row surface the
newer version and link the release page; dismissal persists per
version so each release nags at most once. No auto-install: unsigned
builds cannot, by platform constraint; the checker only notifies.

App 364/364 (+24), typechecks + build green.
2026-07-17 04:58:13 -07:00

40 lines
1.5 KiB
TypeScript

import { useState } from 'react'
import { releasePageUrl, useUpdateStatus } from '../hooks/useUpdateStatus'
import { codeburn } from '../lib/ipc'
const DISMISS_KEY = 'codeburn.updateDismissed'
function readDismissed(): string | null {
try { return globalThis.localStorage?.getItem(DISMISS_KEY) ?? null } catch { return null }
}
/**
* Subtle, dismissible "update available" nudge, in the budget-banner visual
* language. Dismiss persists per release tag (codeburn.updateDismissed), so the
* same version never nags twice but the next release shows fresh. Download opens
* the release page via the https-only openExternal bridge. Never auto-installs.
*/
export function UpdateBanner() {
const status = useUpdateStatus()
const [dismissedTag, setDismissedTag] = useState<string | null>(readDismissed)
if (!status || !status.updateAvailable || !status.tag) return null
if (dismissedTag === status.tag) return null
const tag = status.tag
const dismiss = () => {
try { globalThis.localStorage?.setItem(DISMISS_KEY, tag) } catch { /* storage can be unavailable */ }
setDismissedTag(tag)
}
return (
<div role="status" className="update-banner">
<span>
Update available: CodeBurn {status.latestVersion} ·{' '}
<button type="button" className="set-text-button" onClick={() => { void codeburn.openExternal(releasePageUrl(tag)) }}>Download</button>
</span>
<button type="button" className="set-text-button" onClick={dismiss}>Dismiss</button>
</div>
)
}