OverviewFilesCommitsIssuesDocsReleasesPeers

cthuloid commits / 63d6b5e

android: always show the installed version; tap to check for updates
A slim line above the page: "Cthuloid Lights <version> · up to date · checked hh:mm" (or why the check failed). Tapping it checks now; it turns into the blue Install strip when a newer release exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63d6b5eae76cf7f8133385efe6995c2f6e98757d
by plankatron <fleeky@gmail.com> · 2026-10-02T17:31:24.000Z

Changed files (1) +36 -18

android/app/App.jsx +36 -18
……@@ -36,35 +36,43 @@ export default function App () {
3636 )
3737}
3838
39// Self-update (DESIGN-android.md §6): checks the forge on start and every 6 h;
40// shows a strip only when a newer complete release exists. Above every screen,
41// so a build whose controller will not start can still update itself.
39// Self-update (DESIGN-android.md §6): checks the forge on start, every 6 h and
40// on tap. A slim line always shows the installed version and the last check;
41// it turns into a blue Install strip when a newer complete release exists.
42// Above every screen, so a build whose controller will not start can still
43// update itself.
4244const CHECK_EVERY = 6 * 60 * 60 * 1000
45const hhmm = (d) => String(d.getHours()).padStart(2, '0') + ':' + String(d.getMinutes()).padStart(2, '0')
4346
4447function UpdateBanner () {
4548 const [u, setU] = useState(null) // { version, apkUrl, shaUrl }
4649 const [phase, setPhase] = useState('available')
4750 const [note, setNote] = useState('')
51 const [checked, setChecked] = useState('checking for updates…')
4852 const busy = useRef(false)
4953 const path = useRef(null) // checked APK on disk, for u.version only
5054
51 useEffect(() => {
52 const run = () => {
53 if (busy.current) return
54 updater.check()
55 .then((next) => {
56 if (busy.current) return
57 setU((prev) => {
58 if (!prev || !next || prev.version !== next.version) { path.current = null; setPhase('available'); setNote('') }
59 return next
60 })
55 const run = useCallback(() => {
56 if (busy.current) return
57 setChecked('checking for updates…')
58 updater.check()
59 .then((next) => {
60 setChecked('up to date · checked ' + hhmm(new Date()))
61 if (busy.current) return
62 setU((prev) => {
63 if (!prev || !next || prev.version !== next.version) { path.current = null; setPhase('available'); setNote('') }
64 return next
6165 })
62 .catch((e) => console.warn('update check failed: ' + e.message)) // not 'up to date'
63 }
66 })
67 // A failed check is said as such, never shown as 'up to date'.
68 .catch((e) => setChecked('update check failed: ' + e.message))
69 }, [])
70
71 useEffect(() => {
6472 run()
6573 const t = setInterval(run, CHECK_EVERY)
6674 return () => clearInterval(t)
67 }, [])
75 }, [run])
6876
6977 const go = useCallback(async () => {
7078 if (busy.current) return
……@@ -85,7 +93,13 @@ function UpdateBanner () {
8593 }
8694 }, [u])
8795
88 if (!u) return null
96 if (!u) {
97 return (
98 <Pressable style={s.version} onPress={run}>
99 <Text style={s.versionText}>Cthuloid Lights {updater.installedVersion || '?'} · {checked}</Text>
100 </Pressable>
101 )
102 }
89103 const label = {
90104 available: 'Update ' + u.version + ' available — Install',
91105 downloading: 'Downloading ' + u.version + '… ' + note,
……@@ -96,6 +110,7 @@ function UpdateBanner () {
96110 return (
97111 <Pressable style={s.banner} onPress={go} disabled={phase === 'downloading'}>
98112 <Text style={s.bannerText}>{label}</Text>
113 <Text style={s.bannerSub}>installed {updater.installedVersion}</Text>
99114 </Pressable>
100115 )
101116}
……@@ -185,5 +200,8 @@ const s = StyleSheet.create({
185200 err: { color: '#c33', textAlign: 'center' },
186201 retry: { marginTop: 16, textDecorationLine: 'underline' },
187202 banner: { backgroundColor: '#2d5fd0', paddingVertical: 10, paddingHorizontal: 16 },
188 bannerText: { color: '#fff', textAlign: 'center' }
203 bannerText: { color: '#fff', textAlign: 'center' },
204 bannerSub: { color: '#fff', opacity: 0.75, fontSize: 11, textAlign: 'center' },
205 version: { paddingVertical: 4, paddingHorizontal: 12 },
206 versionText: { fontSize: 11, opacity: 0.55, textAlign: 'center' }
189207})