From 2b6738d35bb6efec33e031e589e3e0abd4136cb0 Mon Sep 17 00:00:00 2001 From: Untone Date: Sat, 16 Dec 2023 17:13:14 +0300 Subject: [PATCH] rewritten-session-context --- src/components/Nav/AuthModal/EmailConfirm.tsx | 42 +++++----- src/components/Nav/AuthModal/types.ts | 3 +- src/context/session.tsx | 80 ++++++++++--------- 3 files changed, 68 insertions(+), 57 deletions(-) diff --git a/src/components/Nav/AuthModal/EmailConfirm.tsx b/src/components/Nav/AuthModal/EmailConfirm.tsx index 3038fb73..12e14acb 100644 --- a/src/components/Nav/AuthModal/EmailConfirm.tsx +++ b/src/components/Nav/AuthModal/EmailConfirm.tsx @@ -14,35 +14,37 @@ import styles from './AuthModal.module.scss' export const EmailConfirm = () => { const { t } = useLocalize() const { - actions: { confirmEmail }, + actions: { confirmEmail, loadSession, loadAuthor }, + session, } = useSession() - const { user } = useSession() + const confirmedEmail = createMemo(() => session()?.user?.email_verified) const [isTokenExpired, setIsTokenExpired] = createSignal(false) const [isTokenInvalid, setIsTokenInvalid] = createSignal(false) - - const confirmedEmail = createMemo(() => user?.email || '') - - const { searchParams } = useRouter() + const { searchParams, changeSearchParam } = useRouter() onMount(async () => { - const token = searchParams().token - try { - await confirmEmail({ token }) - } catch (error) { - if (error instanceof ApiError) { - if (error.code === 'token_expired') { - setIsTokenExpired(true) - return + const token = searchParams().access_token + if (token) { + try { + await confirmEmail({ token }) + await loadSession() + await loadAuthor() + } catch (error) { + if (error instanceof ApiError) { + if (error.code === 'token_expired') { + setIsTokenExpired(true) + return + } + + if (error.code === 'token_invalid') { + setIsTokenInvalid(true) + return + } } - if (error.code === 'token_invalid') { - setIsTokenInvalid(true) - return - } + console.log(error) } - - console.log(error) } }) diff --git a/src/components/Nav/AuthModal/types.ts b/src/components/Nav/AuthModal/types.ts index 905b77cf..ee5a798e 100644 --- a/src/components/Nav/AuthModal/types.ts +++ b/src/components/Nav/AuthModal/types.ts @@ -14,7 +14,8 @@ export type AuthModalSearchParams = { } export type ConfirmEmailSearchParams = { - token: string + access_token?: string + token?: string } export type CreateChatSearchParams = { diff --git a/src/context/session.tsx b/src/context/session.tsx index f2c4aa06..bb7a9235 100644 --- a/src/context/session.tsx +++ b/src/context/session.tsx @@ -26,6 +26,7 @@ import { showModal } from '../stores/ui' import { useLocalize } from './localize' import { useSnackbar } from './snackbar' +import { useRouter } from '../stores/router' const config: ConfigType = { authorizerURL: 'https://auth.discours.io', @@ -34,17 +35,18 @@ const config: ConfigType = { } export type SessionContextType = { - user: User | null config: ConfigType session: Resource + author: Resource isSessionLoaded: Accessor subscriptions: Accessor - author: Resource isAuthenticated: Accessor isAuthWithCallback: Accessor<() => void> actions: { getToken: () => string loadSession: () => AuthToken | Promise + setSession: (token: AuthToken | null) => void // setSession + loadAuthor: (info?: unknown) => Author | Promise loadSubscriptions: () => Promise requireAuthentication: ( callback: (() => Promise) | (() => void), @@ -52,10 +54,8 @@ export type SessionContextType = { ) => void signIn: (params: LoginInput) => Promise signOut: () => Promise - confirmEmail: (input: VerifyEmailInput) => Promise + confirmEmail: (input: VerifyEmailInput) => Promise // email confirm callback is in auth.discours.io setIsSessionLoaded: (loaded: boolean) => void - setToken: (token: AuthToken | null) => void // setSession - setUser: (user: User | null) => void authorizer: () => Authorizer } } @@ -79,28 +79,9 @@ export const SessionProvider = (props: { const { actions: { showSnackbar }, } = useSnackbar() - + const { searchParams, changeSearchParam } = useRouter() const [isSessionLoaded, setIsSessionLoaded] = createSignal(false) const [subscriptions, setSubscriptions] = createSignal(EMPTY_SUBSCRIPTIONS) - const [token, setToken] = createSignal() - const [user, setUser] = createSignal() - - const loadSubscriptions = async (): Promise => { - const result = await apiClient.getMySubscriptions() - if (result) { - setSubscriptions(result) - } else { - setSubscriptions(EMPTY_SUBSCRIPTIONS) - } - } - - const setAuth = (auth: AuthToken | void) => { - if (auth) { - setToken(auth) - setUser(auth.user) - mutate(auth) - } - } const getSession = async (): Promise => { try { @@ -110,14 +91,14 @@ export const SessionProvider = (props: { Authorization: tkn, }) if (authResult?.access_token) { - setAuth(authResult) + mutate(authResult) console.debug('[context.session] token after: ', authResult.access_token) await loadSubscriptions() return authResult } } catch (error) { console.error('[context.session] getSession error:', error) - setAuth(null) + mutate(null) return null } finally { setTimeout(() => { @@ -131,6 +112,32 @@ export const SessionProvider = (props: { initialValue: null, }) + const user = createMemo(() => session().user) + + createEffect(() => { + // detect confirm redirect + const params = searchParams() + if (params?.access_token) { + console.debug('[context.session] access token presented, changing search params') + changeSearchParam({ modal: 'auth', mode: 'confirm-email', access_token: params?.access_token }) + } + }) + + createEffect(() => { + // authorized graphql client + const tkn = getToken() + if (tkn) apiClient.connect(tkn) + }) + + const loadSubscriptions = async (): Promise => { + const result = await apiClient.private?.getMySubscriptions() + if (result) { + setSubscriptions(result) + } else { + setSubscriptions(EMPTY_SUBSCRIPTIONS) + } + } + const [author, { refetch: loadAuthor }] = createResource( async () => { const u = session()?.user @@ -151,7 +158,7 @@ export const SessionProvider = (props: { const authResult: AuthToken | void = await authorizer().login(params) if (authResult && authResult.access_token) { - setAuth(authResult) + mutate(authResult) await loadSubscriptions() console.debug('[context.session] signed in') } else { @@ -172,7 +179,7 @@ export const SessionProvider = (props: { on( () => props.onStateChangeCallback, () => { - props.onStateChangeCallback(token()) + props.onStateChangeCallback(session()) }, { defer: true }, ), @@ -200,7 +207,7 @@ export const SessionProvider = (props: { setIsAuthWithCallback(() => callback) const userdata = await authorizer().getProfile() - if (userdata) setUser(userdata) + if (userdata) mutate({ ...session(), user: userdata }) if (!isAuthenticated()) { showModal('auth', modalSource) @@ -209,17 +216,19 @@ export const SessionProvider = (props: { const signOut = async () => { await authorizer().logout() - setAuth(null) + mutate(null) setSubscriptions(EMPTY_SUBSCRIPTIONS) showSnackbar({ body: t("You've successfully logged out") }) } const confirmEmail = async (input: VerifyEmailInput) => { + console.log(`[context.session] calling authorizer's verify email with ${input}`) const at: void | AuthToken = await authorizer().verifyEmail(input) - setAuth(at) + if (at) mutate(at) + console.log(`[context.session] confirmEmail got result ${at}`) } - const getToken = createMemo(() => token()?.access_token) + const getToken = createMemo(() => session()?.access_token) const actions = { getToken, @@ -230,12 +239,11 @@ export const SessionProvider = (props: { signOut, confirmEmail, setIsSessionLoaded, - setToken, - setUser, + setSession: mutate, authorizer, + loadAuthor, } const value: SessionContextType = { - user: user(), config: configuration(), session, subscriptions,