release(v1.7.34-alpha): re-seed onboarding cache + rotating login bg + drop re-login zoom

- useOnboarding.ts: when the backend gives a definitive answer
  (true/false, not a null retry failure), re-seed the
  neode_onboarding_complete localStorage flag accordingly. Fixes the
  case where a user clears site data on an already-onboarded node —
  OnboardingWrapper's useVideoBackground computed reads localStorage
  synchronously, so without this re-seed the intro video would fire
  again on /login even though RootRedirect correctly sent them
  straight to /login.
- OnboardingWrapper.vue: login background now rotates through
  bg-intro-1..6 on each /login mount, with the current index
  persisted to localStorage (neode_login_bg_idx) so subsequent
  logouts advance rather than repeat the same image.
- Dashboard.vue: subsequent-login branch drops the 1.2s showZoomIn
  entirely. Only the first dashboard entry after onboarding plays
  the full zoom + glitch reveal; every re-login now just fades in
  with the welcome typing (~300ms).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-04-22 05:42:52 -04:00
parent aa0677be57
commit 06feb85aa5
5 changed files with 43 additions and 9 deletions

2
core/Cargo.lock generated
View File

@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]] [[package]]
name = "archipelago" name = "archipelago"
version = "1.7.33-alpha" version = "1.7.34-alpha"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"archipelago-container", "archipelago-container",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "archipelago" name = "archipelago"
version = "1.7.33-alpha" version = "1.7.34-alpha"
edition = "2021" edition = "2021"
description = "Archipelago Bitcoin Node OS - Native backend" description = "Archipelago Bitcoin Node OS - Native backend"
authors = ["Archipelago Team"] authors = ["Archipelago Team"]

View File

@ -24,7 +24,18 @@ export async function isOnboardingComplete(): Promise<boolean> {
// as already-onboarded and skip the wizard entirely). Only fall // as already-onboarded and skip the wizard entirely). Only fall
// back to localStorage if the backend is unreachable. // back to localStorage if the backend is unreachable.
const result = await callWithRetry(() => rpcClient.isOnboardingComplete(), 2) const result = await callWithRetry(() => rpcClient.isOnboardingComplete(), 2)
if (result !== null) return result if (result !== null) {
// Re-seed the localStorage cache so non-async consumers
// (OnboardingWrapper's useVideoBackground computed, etc.) see the
// right answer after the user clears site data on an already-
// onboarded node.
if (result) {
try { localStorage.setItem('neode_onboarding_complete', '1') } catch {}
} else {
try { localStorage.removeItem('neode_onboarding_complete') } catch {}
}
return result
}
return localStorage.getItem('neode_onboarding_complete') === '1' return localStorage.getItem('neode_onboarding_complete') === '1'
} }

View File

@ -285,17 +285,15 @@ onMounted(() => {
loginTransition.setPendingWelcomeTyping(false) loginTransition.setPendingWelcomeTyping(false)
}, 4000) }, 4000)
} else if (loginTransition.justLoggedIn) { } else if (loginTransition.justLoggedIn) {
// Regular re-login quick interface draw, no triple glitch flashes. // Regular re-login no zoom, no glitch. Just land on the
// Just the zoom-in for a short beat, then welcome typing fires fast. // dashboard and kick off the welcome typing quickly.
playDashboardLoadOomph() playDashboardLoadOomph()
showZoomIn.value = true
loginTransition.setPendingWelcomeTyping(true) loginTransition.setPendingWelcomeTyping(true)
loginTransition.setJustLoggedIn(false) loginTransition.setJustLoggedIn(false)
scheduledTimeout(() => { showZoomIn.value = false }, 1200)
scheduledTimeout(() => { scheduledTimeout(() => {
loginTransition.setStartWelcomeTyping(true) loginTransition.setStartWelcomeTyping(true)
loginTransition.setPendingWelcomeTyping(false) loginTransition.setPendingWelcomeTyping(false)
}, 600) }, 300)
} }
window.addEventListener('keydown', handleKioskShortcuts) window.addEventListener('keydown', handleKioskShortcuts)

View File

@ -122,7 +122,32 @@ const routeBackgrounds: Record<string, string> = {
'/login': 'bg-intro.jpg' // Video loops from splash (same as intro) '/login': 'bg-intro.jpg' // Video loops from splash (same as intro)
} }
const loginBackground = 'bg-intro-1.jpg' // Rotate the login background so the lock screen doesn't look
// identical on every logout. Cycles through bg-intro-1..6 using a
// counter persisted to localStorage so subsequent visits advance.
const LOGIN_BACKGROUNDS = [
'bg-intro-1.jpg',
'bg-intro-2.jpg',
'bg-intro-3.jpg',
'bg-intro-4.jpg',
'bg-intro-5.jpg',
'bg-intro-6.jpg',
]
function pickNextLoginBackground(): string {
try {
const raw = localStorage.getItem('neode_login_bg_idx')
const prev = raw !== null ? parseInt(raw, 10) : -1
const next = (Number.isFinite(prev) ? prev + 1 : 0) % LOGIN_BACKGROUNDS.length
localStorage.setItem('neode_login_bg_idx', String(next))
return LOGIN_BACKGROUNDS[next]!
} catch {
return LOGIN_BACKGROUNDS[Math.floor(Math.random() * LOGIN_BACKGROUNDS.length)]!
}
}
const loginBackground = ref(pickNextLoginBackground())
watch(() => route.path, (p) => {
if (p === '/login') loginBackground.value = pickNextLoginBackground()
})
// Restore video time from splash screen for seamless transition // Restore video time from splash screen for seamless transition
function restoreVideoTime() { function restoreVideoTime() {