94 lines
2.5 KiB
TypeScript
94 lines
2.5 KiB
TypeScript
import { createEffect, createEvent, createStore, sample } from 'effector'
|
|
import { apiClient } from '@/shared/api/client'
|
|
import { createErrorStore } from '@/shared/lib/create-error-store'
|
|
import { persistToken } from '@/shared/lib/persist-token'
|
|
|
|
export type AuthUser = {
|
|
id: string
|
|
email: string
|
|
displayName?: string | null
|
|
firstName?: string | null
|
|
lastName?: string | null
|
|
gender?: string | null
|
|
avatar?: string | null
|
|
phone?: string | null
|
|
isAdmin?: boolean
|
|
}
|
|
|
|
export const tokenSet = createEvent<string | null>()
|
|
export const logout = createEvent()
|
|
|
|
// ----- Token persistence -----
|
|
|
|
const persistTokenFx = createEffect<string | null, void>({
|
|
handler: (token) => persistToken(token),
|
|
})
|
|
|
|
export const $token = createStore<string | null>(null)
|
|
.on(tokenSet, (_, t) => t)
|
|
.reset(logout)
|
|
|
|
sample({
|
|
clock: $token,
|
|
target: persistTokenFx,
|
|
})
|
|
|
|
// ----- User -----
|
|
|
|
export const $user = createStore<AuthUser | null>(null).reset(logout)
|
|
|
|
export const meFx = createEffect(async (token: string) => {
|
|
const { data } = await apiClient.get<{ user: AuthUser | null }>('me', {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
})
|
|
return data.user
|
|
})
|
|
|
|
sample({
|
|
clock: tokenSet,
|
|
filter: (t): t is string => Boolean(t),
|
|
target: meFx,
|
|
})
|
|
|
|
sample({
|
|
clock: meFx.doneData,
|
|
target: $user,
|
|
})
|
|
|
|
// ----- Email change -----
|
|
|
|
export const requestEmailChangeCodeFx = createEffect(async (newEmail: string) => {
|
|
await apiClient.post('me/change-email/request-code', { newEmail })
|
|
})
|
|
|
|
export const verifyEmailChangeFx = createEffect(async (params: { newEmail: string; code: string }) => {
|
|
const { data } = await apiClient.post<{ user: AuthUser }>('me/change-email/verify', params)
|
|
return data.user
|
|
})
|
|
|
|
// ----- Profile update -----
|
|
|
|
export type UpdateProfileParams = { displayName: string | null; phone?: string | null }
|
|
|
|
export const updateProfileFx = createEffect(async (params: UpdateProfileParams) => {
|
|
const { data } = await apiClient.patch<{ user: AuthUser }>('me/profile', params)
|
|
return data.user
|
|
})
|
|
|
|
// ----- Error stores -----
|
|
|
|
export const $requestEmailChangeCodeError = createErrorStore(requestEmailChangeCodeFx).$error
|
|
export const $verifyEmailChangeError = createErrorStore(verifyEmailChangeFx).$error
|
|
export const $updateProfileError = createErrorStore(updateProfileFx).$error
|
|
|
|
// ----- Re-exports -----
|
|
|
|
export { readStoredToken } from '@/shared/lib/persist-token'
|
|
|
|
// ----- Sync user from profile/email changes -----
|
|
|
|
sample({
|
|
clock: [verifyEmailChangeFx.doneData, updateProfileFx.doneData],
|
|
target: $user,
|
|
})
|