2026-06-14 01:11:29 +02:00
|
|
|
// 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).
|
|
|
|
|
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
import { getAccountRaw, LS, setAccountRaw } from "./storage";
|
2026-06-26 03:30:19 +02:00
|
|
|
|
|
|
|
|
export const ONBOARD_ACTIVE = "siftlode.onboarding.active"; // sessionStorage (not in LS, which is localStorage)
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
// Per-account (see accountKey): a fresh account should still get the onboarding nudge even if a
|
|
|
|
|
// different account in the same browser dismissed it.
|
2026-06-26 03:30:19 +02:00
|
|
|
export const ONBOARD_DISMISSED = LS.onboardingDismissed;
|
2026-06-14 01:11:29 +02:00
|
|
|
|
2026-06-21 02:06:37 +02:00
|
|
|
export function shouldAutoOpenOnboarding(me: {
|
|
|
|
|
can_read: boolean;
|
|
|
|
|
is_demo?: boolean;
|
|
|
|
|
google_enabled?: boolean;
|
|
|
|
|
}): boolean {
|
2026-06-16 09:17:34 +02:00
|
|
|
// The shared demo account can never connect YouTube, so never nudge it to.
|
|
|
|
|
if (me.is_demo) return false;
|
2026-06-21 02:06:37 +02:00
|
|
|
// 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;
|
2026-06-14 01:11:29 +02:00
|
|
|
if (sessionStorage.getItem(ONBOARD_ACTIVE)) return true;
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
return !me.can_read && !getAccountRaw(ONBOARD_DISMISSED);
|
2026-06-14 01:11:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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);
|
fix(state): scope all per-account localStorage by account id
Multi-account-in-one-browser (esp. with per-tab accounts) leaked one account's client state
into another via shared localStorage keys. Scope every account-specific key by the tab's active
account (accountKey/readAccount/writeAccount/useAccountPersistedState helpers), so nothing bleeds
across accounts or tabs:
- Real leaks: selected playlist, client notification history + settings, onboarding-dismissed.
- UI position: feed page, channel-manager filter/view + tables, playlist sort, Settings/Stats/
Users/Config tabs.
- Previously DB-adopted caches (theme, hints, performance mode, sidebar layout, nav/filter
collapse) — now the cache is per-account too, so there's no flash of the other account's value
on login.
Kept intentionally global: siftlode.lang (needed pre-login on the Welcome page; the DB pref still
scopes it per-account after sign-in) and siftlode.seenVersion (a per-browser 'new version' banner).
E2EE private keys (IndexedDB) and the chat-dock key were already per-user.
2026-07-02 01:45:16 +02:00
|
|
|
if (dismiss) setAccountRaw(ONBOARD_DISMISSED, "1");
|
2026-06-14 01:11:29 +02:00
|
|
|
}
|