2023-06-14 17:19:30 +00:00
|
|
|
import { Accessor, JSX, Resource, createEffect } from 'solid-js'
|
2022-12-06 16:03:55 +00:00
|
|
|
import { createContext, createMemo, createResource, createSignal, onMount, useContext } from 'solid-js'
|
2023-03-29 08:51:27 +00:00
|
|
|
import type { AuthResult, User } from '../graphql/types.gen'
|
2022-11-13 19:35:57 +00:00
|
|
|
import { apiClient } from '../utils/apiClient'
|
|
|
|
import { resetToken, setToken } from '../graphql/privateGraphQLClient'
|
2023-02-10 11:11:24 +00:00
|
|
|
import { useSnackbar } from './snackbar'
|
2023-02-17 09:21:02 +00:00
|
|
|
import { useLocalize } from './localize'
|
2023-06-14 17:19:30 +00:00
|
|
|
import { showModal } from '../stores/ui'
|
|
|
|
import type { AuthModalSource } from '../components/Nav/AuthModal/types'
|
2022-11-13 19:35:57 +00:00
|
|
|
|
2022-11-14 10:02:08 +00:00
|
|
|
type SessionContextType = {
|
2022-12-01 18:45:35 +00:00
|
|
|
session: Resource<AuthResult>
|
2022-12-06 16:03:55 +00:00
|
|
|
isSessionLoaded: Accessor<boolean>
|
2023-03-29 08:51:27 +00:00
|
|
|
user: Accessor<User>
|
2022-11-13 19:35:57 +00:00
|
|
|
isAuthenticated: Accessor<boolean>
|
|
|
|
actions: {
|
2022-12-01 18:45:35 +00:00
|
|
|
loadSession: () => AuthResult | Promise<AuthResult>
|
2023-06-14 17:19:30 +00:00
|
|
|
requireAuthentication: (
|
|
|
|
callback: (() => Promise<void>) | (() => void),
|
|
|
|
modalSource: AuthModalSource
|
|
|
|
) => void
|
2022-11-13 19:35:57 +00:00
|
|
|
signIn: ({ email, password }: { email: string; password: string }) => Promise<void>
|
|
|
|
signOut: () => Promise<void>
|
|
|
|
confirmEmail: (token: string) => Promise<void>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-14 10:02:08 +00:00
|
|
|
const SessionContext = createContext<SessionContextType>()
|
2022-11-13 19:35:57 +00:00
|
|
|
|
2022-11-14 10:02:08 +00:00
|
|
|
export function useSession() {
|
|
|
|
return useContext(SessionContext)
|
2022-11-13 19:35:57 +00:00
|
|
|
}
|
|
|
|
|
2022-11-14 10:02:08 +00:00
|
|
|
export const SessionProvider = (props: { children: JSX.Element }) => {
|
2022-12-06 16:03:55 +00:00
|
|
|
const [isSessionLoaded, setIsSessionLoaded] = createSignal(false)
|
2023-02-17 09:21:02 +00:00
|
|
|
const { t } = useLocalize()
|
2023-02-10 11:11:24 +00:00
|
|
|
const {
|
|
|
|
actions: { showSnackbar }
|
|
|
|
} = useSnackbar()
|
|
|
|
|
2022-12-06 16:03:55 +00:00
|
|
|
const getSession = async (): Promise<AuthResult> => {
|
|
|
|
try {
|
|
|
|
const authResult = await apiClient.getSession()
|
|
|
|
if (!authResult) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
setToken(authResult.token)
|
|
|
|
return authResult
|
|
|
|
} catch (error) {
|
|
|
|
console.error('getSession error:', error)
|
|
|
|
resetToken()
|
|
|
|
return null
|
2022-12-07 11:06:26 +00:00
|
|
|
} finally {
|
|
|
|
setIsSessionLoaded(true)
|
2022-12-06 16:03:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const [session, { refetch: loadSession, mutate }] = createResource<AuthResult>(getSession, {
|
|
|
|
ssrLoadFrom: 'initial',
|
|
|
|
initialValue: null
|
|
|
|
})
|
2022-11-13 19:35:57 +00:00
|
|
|
|
2023-03-29 08:51:27 +00:00
|
|
|
const user = createMemo(() => session()?.user)
|
2022-11-25 11:03:51 +00:00
|
|
|
|
2022-11-13 19:35:57 +00:00
|
|
|
const isAuthenticated = createMemo(() => Boolean(session()?.user?.slug))
|
|
|
|
|
|
|
|
const signIn = async ({ email, password }: { email: string; password: string }) => {
|
|
|
|
const authResult = await apiClient.authLogin({ email, password })
|
|
|
|
mutate(authResult)
|
|
|
|
setToken(authResult.token)
|
|
|
|
console.debug('signed in')
|
|
|
|
}
|
|
|
|
|
2023-06-14 17:19:30 +00:00
|
|
|
const [isAuthWithCallback, setIsAuthWithCallback] = createSignal(null)
|
|
|
|
|
|
|
|
const requireAuthentication = (callback: () => void, modalSource: AuthModalSource) => {
|
|
|
|
setIsAuthWithCallback(() => callback)
|
|
|
|
|
|
|
|
if (!isAuthenticated()) {
|
|
|
|
showModal('auth', modalSource)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
createEffect(async () => {
|
|
|
|
if (isAuthWithCallback()) {
|
|
|
|
const sessionProof = await session()
|
|
|
|
|
|
|
|
if (sessionProof) {
|
|
|
|
await isAuthWithCallback()()
|
|
|
|
|
|
|
|
setIsAuthWithCallback(null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-11-13 19:35:57 +00:00
|
|
|
const signOut = async () => {
|
|
|
|
// TODO: call backend to revoke token
|
|
|
|
mutate(null)
|
|
|
|
resetToken()
|
2023-02-10 11:11:24 +00:00
|
|
|
showSnackbar({ body: t("You've successfully logged out") })
|
2022-11-13 19:35:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const confirmEmail = async (token: string) => {
|
|
|
|
const authResult = await apiClient.confirmEmail({ token })
|
|
|
|
mutate(authResult)
|
|
|
|
setToken(authResult.token)
|
|
|
|
}
|
|
|
|
|
|
|
|
const actions = {
|
2022-12-01 18:45:35 +00:00
|
|
|
loadSession,
|
2023-06-14 17:19:30 +00:00
|
|
|
requireAuthentication,
|
2022-11-13 19:35:57 +00:00
|
|
|
signIn,
|
|
|
|
signOut,
|
|
|
|
confirmEmail
|
|
|
|
}
|
|
|
|
|
2023-03-29 08:51:27 +00:00
|
|
|
const value: SessionContextType = { session, isSessionLoaded, user, isAuthenticated, actions }
|
2022-11-13 19:35:57 +00:00
|
|
|
|
|
|
|
onMount(() => {
|
2022-12-01 18:45:35 +00:00
|
|
|
loadSession()
|
2022-11-13 19:35:57 +00:00
|
|
|
})
|
|
|
|
|
2022-11-14 10:02:08 +00:00
|
|
|
return <SessionContext.Provider value={value}>{props.children}</SessionContext.Provider>
|
2022-11-13 19:35:57 +00:00
|
|
|
}
|