diff --git a/apps/desktop/src/auth.tsx b/apps/desktop/src/auth.tsx index 31227cb204..3ce14e6d4f 100644 --- a/apps/desktop/src/auth.tsx +++ b/apps/desktop/src/auth.tsx @@ -16,6 +16,7 @@ import { useCallback, useContext, useEffect, + useMemo, useState, } from "react"; @@ -104,41 +105,44 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const [session, setSession] = useState(null); const [serverReachable, setServerReachable] = useState(true); - const setSessionFromTokens = async ( - accessToken: string, - refreshToken: string, - ) => { - if (!supabase) { - console.error("Supabase client not found"); - return; - } + const setSessionFromTokens = useCallback( + async (accessToken: string, refreshToken: string) => { + if (!supabase) { + console.error("Supabase client not found"); + return; + } - const res = await supabase.auth.setSession({ - access_token: accessToken, - refresh_token: refreshToken, - }); + const res = await supabase.auth.setSession({ + access_token: accessToken, + refresh_token: refreshToken, + }); - if (res.error) { - console.error(res.error); - } else { - setSession(res.data.session); - setServerReachable(true); - void supabase.auth.startAutoRefresh(); - } - }; - - const handleAuthCallback = async (url: string) => { - const parsed = new URL(url); - const accessToken = parsed.searchParams.get("access_token"); - const refreshToken = parsed.searchParams.get("refresh_token"); - - if (!accessToken || !refreshToken) { - console.error("invalid_callback_url"); - return; - } + if (res.error) { + console.error(res.error); + } else { + setSession(res.data.session); + setServerReachable(true); + void supabase.auth.startAutoRefresh(); + } + }, + [], + ); + + const handleAuthCallback = useCallback( + async (url: string) => { + const parsed = new URL(url); + const accessToken = parsed.searchParams.get("access_token"); + const refreshToken = parsed.searchParams.get("refresh_token"); + + if (!accessToken || !refreshToken) { + console.error("invalid_callback_url"); + return; + } - await setSessionFromTokens(accessToken, refreshToken); - }; + await setSessionFromTokens(accessToken, refreshToken); + }, + [setSessionFromTokens], + ); useEffect(() => { if (!supabase) { @@ -242,13 +246,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { }; }, []); - const signIn = async () => { + const signIn = useCallback(async () => { const base = env.VITE_APP_URL ?? "http://localhost:3000"; const scheme = await getScheme(); await openUrl(`${base}/auth?flow=desktop&scheme=${scheme}`); - }; + }, []); - const signOut = async () => { + const signOut = useCallback(async () => { if (!supabase) { return; } @@ -275,9 +279,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { setSession(null); } } - }; + }, []); - const refreshSession = async (): Promise => { + const refreshSession = useCallback(async (): Promise => { if (!supabase) { return null; } @@ -291,7 +295,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { return data.session; } return null; - }; + }, []); const getHeaders = useCallback(() => { if (!session) { @@ -320,17 +324,29 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { return `https://gravatar.com/avatar/${hash}`; }, [session]); - const value = { - session, - supabase, - signIn, - signOut, - refreshSession, - handleAuthCallback, - setSessionFromTokens, - getHeaders, - getAvatarUrl, - }; + const value = useMemo( + () => ({ + session, + supabase, + signIn, + signOut, + refreshSession, + handleAuthCallback, + setSessionFromTokens, + getHeaders, + getAvatarUrl, + }), + [ + session, + signIn, + signOut, + refreshSession, + handleAuthCallback, + setSessionFromTokens, + getHeaders, + getAvatarUrl, + ], + ); return {children}; } diff --git a/apps/desktop/src/components/onboarding/calendar.tsx b/apps/desktop/src/components/onboarding/calendar.tsx index eeda15bdef..240e9088db 100644 --- a/apps/desktop/src/components/onboarding/calendar.tsx +++ b/apps/desktop/src/components/onboarding/calendar.tsx @@ -1,13 +1,13 @@ import { Icon } from "@iconify-icon/react"; -import { platform } from "@tauri-apps/plugin-os"; import { useAuth } from "../../auth"; -import { getNextAfterConfigureNotice, type StepProps } from "./config"; +import { Route } from "../../routes/app/onboarding"; +import { getNext, type StepProps } from "./config"; import { Divider, IntegrationRow, OnboardingContainer } from "./shared"; export function Calendars({ onNavigate }: StepProps) { + const search = Route.useSearch(); const auth = useAuth(); - const currentPlatform = platform(); const isLoggedIn = !!auth?.session; return ( @@ -51,7 +51,7 @@ export function Calendars({ onNavigate }: StepProps) { diff --git a/apps/desktop/src/routes/app/onboarding/index.tsx b/apps/desktop/src/routes/app/onboarding/index.tsx index 7fac32ace0..8d9868a9e8 100644 --- a/apps/desktop/src/routes/app/onboarding/index.tsx +++ b/apps/desktop/src/routes/app/onboarding/index.tsx @@ -1,24 +1,26 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router"; +import { platform } from "@tauri-apps/plugin-os"; import { useCallback } from "react"; import { z } from "zod"; import { commands as windowsCommands } from "@hypr/plugin-windows"; import { - type OnboardingStepId, + type NavigateTarget, STEP_CONFIGS, + STEP_IDS, } from "../../../components/onboarding/config"; import { commands } from "../../../types/tauri.gen"; -const ALL_STEP_IDS = STEP_CONFIGS.map((s) => s.id) as [ - OnboardingStepId, - ...OnboardingStepId[], -]; - const validateSearch = z.object({ - step: z.enum(ALL_STEP_IDS).default("welcome"), + step: z.enum(STEP_IDS).default("welcome"), + local: z.boolean().default(false), + pro: z.boolean().default(false), + platform: z.string().default(platform()), }); +export type Search = z.infer; + export const Route = createFileRoute("/app/onboarding/")({ validateSearch, component: Component, @@ -32,21 +34,22 @@ function finishOnboarding() { } function Component() { - const { step } = Route.useSearch(); + const search = Route.useSearch(); const navigate = useNavigate(); const onNavigate = useCallback( - (target: OnboardingStepId | "done") => { - if (target === "done") { + (ctx: NavigateTarget) => { + const { step, ...rest } = ctx; + if (step === "done") { finishOnboarding(); } else { - void navigate({ to: "/app/onboarding", search: { step: target } }); + void navigate({ to: "/app/onboarding", search: { step, ...rest } }); } }, [navigate], ); - const currentConfig = STEP_CONFIGS.find((s) => s.id === step); + const currentConfig = STEP_CONFIGS.find((s) => s.id === search.step); if (!currentConfig) { return null; }