// Onboarding wizard visibility, kept stable across the OAuth redirect round-trip. // // Granting a YouTube scope navigates away to Google's consent screen and back, which // fully reloads the SPA — so we can't hold the wizard's progress in React state. Instead // we derive the current step from the granted scopes (me.can_read / me.can_write) and use // two storage flags to decide whether to auto-open: // // ONBOARD_ACTIVE (sessionStorage) — set while the user is mid-flow (e.g. just clicked // "Connect"); makes the wizard reopen after the // redirect so they land on the next step. // ONBOARD_DISMISSED (localStorage) — set when the user skips the essential read step; // suppresses the auto-popup on future logins (they // can still reopen it from Settings → Account). export const ONBOARD_ACTIVE = "siftlode.onboarding.active"; export const ONBOARD_DISMISSED = "siftlode.onboarding.dismissed"; export function shouldAutoOpenOnboarding(me: { can_read: boolean; is_demo?: boolean; google_enabled?: boolean; }): boolean { // The shared demo account can never connect YouTube, so never nudge it to. if (me.is_demo) return false; // YouTube connect needs Google OAuth configured on this instance. A self-host instance may run // email+password only — then there's nothing to connect, so don't nudge. if (me.google_enabled === false) return false; if (sessionStorage.getItem(ONBOARD_ACTIVE)) return true; return !me.can_read && !localStorage.getItem(ONBOARD_DISMISSED); } /** Mark a grant as in progress, then send the user to Google's consent screen. */ export function beginGrant(access: "read" | "write"): void { sessionStorage.setItem(ONBOARD_ACTIVE, "1"); window.location.href = `/auth/upgrade?access=${access}`; } /** Leave the wizard. `dismiss` suppresses the future auto-popup (used when read is skipped). */ export function endOnboarding(dismiss: boolean): void { sessionStorage.removeItem(ONBOARD_ACTIVE); if (dismiss) localStorage.setItem(ONBOARD_DISMISSED, "1"); }