From f14ddb4bcee589d6fafc27bb0f1b23e11ed05227 Mon Sep 17 00:00:00 2001 From: Benson Cho <100653148+bcho892@users.noreply.github.com> Date: Sun, 31 Mar 2024 21:29:06 +1300 Subject: [PATCH] store current user in sweet state (#203) * add current user to data store * fetch data about current user if logged in * add files * add claims to the app state * refactor * shoreten implementation * update userclaims type --- client/src/firebase.ts | 20 ++++++++++++++++++++ client/src/models/User.ts | 8 ++++++++ client/src/store/store.ts | 38 ++++++++++++++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 client/src/models/User.ts diff --git a/client/src/firebase.ts b/client/src/firebase.ts index 09956ccdf..f8a87ab8f 100644 --- a/client/src/firebase.ts +++ b/client/src/firebase.ts @@ -2,6 +2,9 @@ import { initializeApp, type FirebaseOptions } from "@firebase/app" import { getAuth, connectAuthEmulator } from "@firebase/auth" import { getFirestore, connectFirestoreEmulator } from "@firebase/firestore" +import { UserClaims } from "models/User" +import fetchClient from "services/OpenApiFetchClient" +import { StoreInstance } from "store/store" const firebaseConfig: FirebaseOptions = { apiKey: import.meta.env.VITE_FIREBASE_API_KEY, @@ -22,4 +25,21 @@ if (import.meta.env.VITE_NODE_ENV !== "production") { connectAuthEmulator(auth, "http://localhost:9099") } +auth.onIdTokenChanged(async (user) => { + if (user === null) { + // suggests a log out + StoreInstance.actions.resetCurrentUserState() + return + } + + try { + const { claims } = await user.getIdTokenResult() + const { data: userData } = await fetchClient.GET("/users/self") + + StoreInstance.actions.setCurrentUser(user, userData, claims as UserClaims) + } catch (error) { + console.error(error) + } +}) + export { auth, db } diff --git a/client/src/models/User.ts b/client/src/models/User.ts new file mode 100644 index 000000000..9aae2f3c6 --- /dev/null +++ b/client/src/models/User.ts @@ -0,0 +1,8 @@ +import { components } from "./__generated__/schema" + +export type UserAdditionalInfo = components["schemas"]["UserAdditionalInfo"] + +export type UserClaims = { + admin?: boolean + member?: boolean +} diff --git a/client/src/store/store.ts b/client/src/store/store.ts index d38c8718a..aa5bf00c4 100644 --- a/client/src/store/store.ts +++ b/client/src/store/store.ts @@ -1,20 +1,48 @@ +import { User } from "firebase/auth" +import { UserAdditionalInfo, UserClaims } from "models/User" import { + defaultRegistry, createStore, Action, createContainer, createHook } from "react-sweet-state" -type State = { current: number } +type State = { + currentUser: User | null // firebase type + currentUserData?: UserAdditionalInfo + currentUserClaims?: UserClaims +} + +const defaultUserState = { + currentUser: null, + currentUserClaims: undefined, + currentUserData: undefined +} const initialState: State = { - current: 1000 + ...defaultUserState } const actions = { - loadInfo: + setCurrentUser: + ( + user: User | null, + userData: UserAdditionalInfo | undefined, + userClaims: UserClaims | undefined + ): Action => + ({ setState }) => { + setState({ + currentUser: user, + currentUserData: userData, + currentUserClaims: userClaims + }) + }, + resetCurrentUserState: (): Action => - async ({ setState }) => {} + ({ setState }) => { + setState({ ...defaultUserState }) + } } type Actions = typeof actions @@ -27,4 +55,6 @@ const Store = createStore({ containedBy: AppDataContainer }) +export const StoreInstance = defaultRegistry.getStore(Store) + export const useAppData = createHook(Store)