// 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. // ONBOARDING_DISMISSED_PREF (an — set when the user skips the essential read step; // account preference, usePref) suppresses the auto-popup on future logins, on every // device (they can still reopen it from Settings → Account). import { connectGoogle } from "./googleConnect"; import { LS } from "./storage"; import type { UsePrefOptions } from "./usePref"; const ONBOARD_ACTIVE = LS.onboardingActive; // sessionStorage, this tab only // The account's, so a fresh account still gets the nudge where another one dismissed it. A copy this // browser kept before the server held it is the string "1". export const ONBOARDING_DISMISSED_PREF = "onboardingDismissed"; export const ONBOARDING_DISMISSED_OPTIONS: UsePrefOptions = { localKey: LS.onboardingDismissed, parse: (raw) => raw === true || raw === "1" || raw === 1, }; export function shouldAutoOpenOnboarding( me: { can_read: boolean; google_enabled?: boolean }, dismissed: boolean, ): boolean { // 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 && !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"); connectGoogle(access); } /** Leave the wizard's flow (its dismissal, if any, is the caller's preference to set). */ export function endOnboarding(): void { sessionStorage.removeItem(ONBOARD_ACTIVE); }