rewritten-session-context

This commit is contained in:
Untone 2023-12-16 17:13:14 +03:00
parent 9e6ba5a523
commit 2b6738d35b
3 changed files with 68 additions and 57 deletions

View File

@ -14,35 +14,37 @@ import styles from './AuthModal.module.scss'
export const EmailConfirm = () => { export const EmailConfirm = () => {
const { t } = useLocalize() const { t } = useLocalize()
const { const {
actions: { confirmEmail }, actions: { confirmEmail, loadSession, loadAuthor },
session,
} = useSession() } = useSession()
const { user } = useSession() const confirmedEmail = createMemo(() => session()?.user?.email_verified)
const [isTokenExpired, setIsTokenExpired] = createSignal(false) const [isTokenExpired, setIsTokenExpired] = createSignal(false)
const [isTokenInvalid, setIsTokenInvalid] = createSignal(false) const [isTokenInvalid, setIsTokenInvalid] = createSignal(false)
const { searchParams, changeSearchParam } = useRouter<ConfirmEmailSearchParams>()
const confirmedEmail = createMemo(() => user?.email || '')
const { searchParams } = useRouter<ConfirmEmailSearchParams>()
onMount(async () => { onMount(async () => {
const token = searchParams().token const token = searchParams().access_token
try { if (token) {
await confirmEmail({ token }) try {
} catch (error) { await confirmEmail({ token })
if (error instanceof ApiError) { await loadSession()
if (error.code === 'token_expired') { await loadAuthor()
setIsTokenExpired(true) } catch (error) {
return 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') { console.log(error)
setIsTokenInvalid(true)
return
}
} }
console.log(error)
} }
}) })

View File

@ -14,7 +14,8 @@ export type AuthModalSearchParams = {
} }
export type ConfirmEmailSearchParams = { export type ConfirmEmailSearchParams = {
token: string access_token?: string
token?: string
} }
export type CreateChatSearchParams = { export type CreateChatSearchParams = {

View File

@ -26,6 +26,7 @@ import { showModal } from '../stores/ui'
import { useLocalize } from './localize' import { useLocalize } from './localize'
import { useSnackbar } from './snackbar' import { useSnackbar } from './snackbar'
import { useRouter } from '../stores/router'
const config: ConfigType = { const config: ConfigType = {
authorizerURL: 'https://auth.discours.io', authorizerURL: 'https://auth.discours.io',
@ -34,17 +35,18 @@ const config: ConfigType = {
} }
export type SessionContextType = { export type SessionContextType = {
user: User | null
config: ConfigType config: ConfigType
session: Resource<AuthToken> session: Resource<AuthToken>
author: Resource<Author | null>
isSessionLoaded: Accessor<boolean> isSessionLoaded: Accessor<boolean>
subscriptions: Accessor<Result> subscriptions: Accessor<Result>
author: Resource<Author | null>
isAuthenticated: Accessor<boolean> isAuthenticated: Accessor<boolean>
isAuthWithCallback: Accessor<() => void> isAuthWithCallback: Accessor<() => void>
actions: { actions: {
getToken: () => string getToken: () => string
loadSession: () => AuthToken | Promise<AuthToken> loadSession: () => AuthToken | Promise<AuthToken>
setSession: (token: AuthToken | null) => void // setSession
loadAuthor: (info?: unknown) => Author | Promise<Author>
loadSubscriptions: () => Promise<void> loadSubscriptions: () => Promise<void>
requireAuthentication: ( requireAuthentication: (
callback: (() => Promise<void>) | (() => void), callback: (() => Promise<void>) | (() => void),
@ -52,10 +54,8 @@ export type SessionContextType = {
) => void ) => void
signIn: (params: LoginInput) => Promise<void> signIn: (params: LoginInput) => Promise<void>
signOut: () => Promise<void> signOut: () => Promise<void>
confirmEmail: (input: VerifyEmailInput) => Promise<void> confirmEmail: (input: VerifyEmailInput) => Promise<void> // email confirm callback is in auth.discours.io
setIsSessionLoaded: (loaded: boolean) => void setIsSessionLoaded: (loaded: boolean) => void
setToken: (token: AuthToken | null) => void // setSession
setUser: (user: User | null) => void
authorizer: () => Authorizer authorizer: () => Authorizer
} }
} }
@ -79,28 +79,9 @@ export const SessionProvider = (props: {
const { const {
actions: { showSnackbar }, actions: { showSnackbar },
} = useSnackbar() } = useSnackbar()
const { searchParams, changeSearchParam } = useRouter()
const [isSessionLoaded, setIsSessionLoaded] = createSignal(false) const [isSessionLoaded, setIsSessionLoaded] = createSignal(false)
const [subscriptions, setSubscriptions] = createSignal<Result>(EMPTY_SUBSCRIPTIONS) const [subscriptions, setSubscriptions] = createSignal<Result>(EMPTY_SUBSCRIPTIONS)
const [token, setToken] = createSignal<AuthToken>()
const [user, setUser] = createSignal<User>()
const loadSubscriptions = async (): Promise<void> => {
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<AuthToken> => { const getSession = async (): Promise<AuthToken> => {
try { try {
@ -110,14 +91,14 @@ export const SessionProvider = (props: {
Authorization: tkn, Authorization: tkn,
}) })
if (authResult?.access_token) { if (authResult?.access_token) {
setAuth(authResult) mutate(authResult)
console.debug('[context.session] token after: ', authResult.access_token) console.debug('[context.session] token after: ', authResult.access_token)
await loadSubscriptions() await loadSubscriptions()
return authResult return authResult
} }
} catch (error) { } catch (error) {
console.error('[context.session] getSession error:', error) console.error('[context.session] getSession error:', error)
setAuth(null) mutate(null)
return null return null
} finally { } finally {
setTimeout(() => { setTimeout(() => {
@ -131,6 +112,32 @@ export const SessionProvider = (props: {
initialValue: null, 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<void> => {
const result = await apiClient.private?.getMySubscriptions()
if (result) {
setSubscriptions(result)
} else {
setSubscriptions(EMPTY_SUBSCRIPTIONS)
}
}
const [author, { refetch: loadAuthor }] = createResource<Author | null>( const [author, { refetch: loadAuthor }] = createResource<Author | null>(
async () => { async () => {
const u = session()?.user const u = session()?.user
@ -151,7 +158,7 @@ export const SessionProvider = (props: {
const authResult: AuthToken | void = await authorizer().login(params) const authResult: AuthToken | void = await authorizer().login(params)
if (authResult && authResult.access_token) { if (authResult && authResult.access_token) {
setAuth(authResult) mutate(authResult)
await loadSubscriptions() await loadSubscriptions()
console.debug('[context.session] signed in') console.debug('[context.session] signed in')
} else { } else {
@ -172,7 +179,7 @@ export const SessionProvider = (props: {
on( on(
() => props.onStateChangeCallback, () => props.onStateChangeCallback,
() => { () => {
props.onStateChangeCallback(token()) props.onStateChangeCallback(session())
}, },
{ defer: true }, { defer: true },
), ),
@ -200,7 +207,7 @@ export const SessionProvider = (props: {
setIsAuthWithCallback(() => callback) setIsAuthWithCallback(() => callback)
const userdata = await authorizer().getProfile() const userdata = await authorizer().getProfile()
if (userdata) setUser(userdata) if (userdata) mutate({ ...session(), user: userdata })
if (!isAuthenticated()) { if (!isAuthenticated()) {
showModal('auth', modalSource) showModal('auth', modalSource)
@ -209,17 +216,19 @@ export const SessionProvider = (props: {
const signOut = async () => { const signOut = async () => {
await authorizer().logout() await authorizer().logout()
setAuth(null) mutate(null)
setSubscriptions(EMPTY_SUBSCRIPTIONS) setSubscriptions(EMPTY_SUBSCRIPTIONS)
showSnackbar({ body: t("You've successfully logged out") }) showSnackbar({ body: t("You've successfully logged out") })
} }
const confirmEmail = async (input: VerifyEmailInput) => { const confirmEmail = async (input: VerifyEmailInput) => {
console.log(`[context.session] calling authorizer's verify email with ${input}`)
const at: void | AuthToken = await authorizer().verifyEmail(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 = { const actions = {
getToken, getToken,
@ -230,12 +239,11 @@ export const SessionProvider = (props: {
signOut, signOut,
confirmEmail, confirmEmail,
setIsSessionLoaded, setIsSessionLoaded,
setToken, setSession: mutate,
setUser,
authorizer, authorizer,
loadAuthor,
} }
const value: SessionContextType = { const value: SessionContextType = {
user: user(),
config: configuration(), config: configuration(),
session, session,
subscriptions, subscriptions,