feat(onboarding): first-login wizard for incremental YouTube consent
After the clean name/email sign-in, a wizard walks the user through granting YouTube read (then optionally write) one step at a time, each with a plain rationale and an up-front heads-up about Google's "unverified app" screen. The visible step is derived from the granted scopes (can_read/can_write) so the flow resumes correctly across the full-page consent redirect; it's dismissible and reopenable from Settings -> Account, which now lists read and write as separate, individually-grantable access rows.
This commit is contained in:
parent
4765db89de
commit
e0980487af
5 changed files with 302 additions and 29 deletions
|
|
@ -24,6 +24,8 @@ import Feed from "./components/Feed";
|
|||
import Channels from "./components/Channels";
|
||||
import Stats from "./components/Stats";
|
||||
import SettingsPanel from "./components/SettingsPanel";
|
||||
import OnboardingWizard from "./components/OnboardingWizard";
|
||||
import { shouldAutoOpenOnboarding } from "./lib/onboarding";
|
||||
import Toaster from "./components/Toaster";
|
||||
|
||||
const DEFAULT_FILTERS: FeedFilters = {
|
||||
|
|
@ -61,6 +63,7 @@ export default function App() {
|
|||
const [sidebarLayout, setSidebarLayoutState] = useState<SidebarLayout>(loadLayout);
|
||||
const [page, setPageState] = useState<Page>(readPage);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [wizardOpen, setWizardOpen] = useState(false);
|
||||
|
||||
function setFilters(next: FeedFilters) {
|
||||
setFiltersState(next);
|
||||
|
|
@ -86,6 +89,13 @@ export default function App() {
|
|||
|
||||
const meQuery = useQuery({ queryKey: ["me"], queryFn: api.me });
|
||||
|
||||
// First-login onboarding: prompt the user to connect YouTube (and resume the flow after
|
||||
// each consent redirect). Derived from granted scopes + storage flags so it's stable
|
||||
// across the full-page OAuth round-trip; dismissible and reopenable from Settings.
|
||||
useEffect(() => {
|
||||
if (meQuery.data && shouldAutoOpenOnboarding(meQuery.data)) setWizardOpen(true);
|
||||
}, [meQuery.data?.id, meQuery.data?.can_read, meQuery.data?.can_write]);
|
||||
|
||||
// On login, adopt server-stored preferences.
|
||||
useEffect(() => {
|
||||
const prefs = meQuery.data?.preferences;
|
||||
|
|
@ -181,8 +191,15 @@ export default function App() {
|
|||
view={view}
|
||||
setView={changeView}
|
||||
onClose={() => setSettingsOpen(false)}
|
||||
onOpenWizard={() => {
|
||||
setSettingsOpen(false);
|
||||
setWizardOpen(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{wizardOpen && meQuery.data && (
|
||||
<OnboardingWizard me={meQuery.data} onClose={() => setWizardOpen(false)} />
|
||||
)}
|
||||
<Toaster />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue