From 7ee6c2e666bfc78c2b5a33618621cc2cf16de3fe Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Thu, 6 Feb 2025 22:42:40 +0100 Subject: [PATCH] Finish account provider cleanup --- front/src/providers/account-provider.tsx | 82 +++++++----------------- front/src/providers/error-provider.tsx | 10 ++- 2 files changed, 29 insertions(+), 63 deletions(-) diff --git a/front/src/providers/account-provider.tsx b/front/src/providers/account-provider.tsx index 44f033d4..f170b349 100644 --- a/front/src/providers/account-provider.tsx +++ b/front/src/providers/account-provider.tsx @@ -1,7 +1,8 @@ -import { type ReactNode, createContext, useEffect, useMemo } from "react"; +import { useQueryClient } from "@tanstack/react-query"; +import { type ReactNode, createContext, useEffect, useMemo, useRef } from "react"; import { Platform } from "react-native"; import { z } from "zod"; -import { type Account, AccountP, type Token, type User, UserP } from "~/models"; +import { type Account, AccountP, type Token, UserP } from "~/models"; import { useFetch } from "~/query"; import { removeAccounts, updateAccount } from "./account-store"; import { useSetError } from "./error-provider"; @@ -9,37 +10,19 @@ import { useStoreValue } from "./settings"; const AccountContext = createContext<{ apiUrl: string; - authToken: Token; + authToken: Token | undefined; selectedAccount?: Account; accounts: (Account & { select: () => void; remove: () => void })[]; }>(undefined!); -export const AccountProvider = ({ - children, - ssrAccount, -}: { - children: ReactNode; - ssrAccount?: Account; -}) => { - if (Platform.OS === "web" && typeof window === "undefined") { - const accounts = ssrAccount - ? [{ ...ssrAccount, selected: true, select: () => {}, remove: () => {} }] - : []; - - return ( - - {children} - - ); - } - - const setError = useSetError(); +export const AccountProvider = ({ children }: { children: ReactNode }) => { + const [setError, clearError] = useSetError("account"); const accounts = useStoreValue("accounts", z.array(AccountP)) ?? []; const ret = useMemo(() => { const acc = accounts.find((x) => x.selected); return { - apiUrl: Platform.OS === "web" ? "/api" : acc?.apiUrl, + apiUrl: Platform.OS === "web" ? "/api" : acc?.apiUrl!, authToken: acc?.token, selectedAccount: acc, accounts: accounts.map((account) => ({ @@ -53,8 +36,6 @@ export const AccountProvider = ({ // update user's data from kyoo on startup, it could have changed. const { isSuccess: userIsSuccess, - isError: userIsError, - isLoading: userIsLoading, isPlaceholderData: userIsPlaceholder, data: user, error: userError, @@ -71,8 +52,8 @@ export const AccountProvider = ({ // Use a ref here because we don't want the effect to trigger when the selected // value has changed, only when the fetch result changed // If we trigger the effect when the selected value change, we enter an infinite render loop - const selectedRef = useRef(selected); - selectedRef.current = selected; + const selectedRef = useRef(ret.selectedAccount); + selectedRef.current = ret.selectedAccount; useEffect(() => { if (!selectedRef.current || !userIsSuccess || userIsPlaceholder) return; // The id is different when user is stale data, we need to wait for the use effect to invalidate the query. @@ -81,41 +62,22 @@ export const AccountProvider = ({ updateAccount(nUser.id, nUser); }, [user, userIsSuccess, userIsPlaceholder]); + useEffect(() => { + if (!userError) return clearError(); + setError({ + error: userError, + retry: () => { + queryClient.resetQueries(); + }, + }); + }, [userError]); + const queryClient = useQueryClient(); - const oldSelected = useRef<{ id: string; token: string } | null>( - selected ? { id: selected.id, token: selected.token.access_token } : null, - ); - - const [permissionError, setPermissionError] = useState(null); - + const selectedId = ret.selectedAccount?.id; useEffect(() => { // if the user change account (or connect/disconnect), reset query cache. - if ( - // biome-ignore lint/suspicious/noDoubleEquals: id can be an id, null or undefined - selected?.id != oldSelected.current?.id || - (userIsError && selected?.token.access_token !== oldSelected.current?.token) - ) { - setPermissionError(null); - queryClient.resetQueries(); - } - oldSelected.current = selected ? { id: selected.id, token: selected.token.access_token } : null; - - // update cookies for ssr (needs to contains token, theme, language...) - if (Platform.OS === "web") { - setCookie("account", selected); - // cookie used for images and videos since we can't add Authorization headers in img or video tags. - setCookie("X-Bearer", selected?.token.access_token); - } - }, [selected, queryClient, userIsError]); - // { - // queryClient.invalidateQueries({ queryKey: ["auth", "me"] }); - // }, - // setError: setPermissionError, - // }} + queryClient.resetQueries(); + }, [selectedId, queryClient]); return {children}; }; diff --git a/front/src/providers/error-provider.tsx b/front/src/providers/error-provider.tsx index 04df98ac..1afe794e 100644 --- a/front/src/providers/error-provider.tsx +++ b/front/src/providers/error-provider.tsx @@ -37,7 +37,11 @@ export const ErrorConsumer = ({ children, scope }: { children: ReactNode; scope: const Handler = handler.view; return ; }; -export const useSetError = () => { - const { setError } = useContext(ErrorContext); - return setError; +export const useSetError = (key: string) => { + const { error, setError } = useContext(ErrorContext); + const set = (obj: Omit) => setError({ key, ...obj }); + const clearError = () => { + if (error?.key === key) setError(null); + }; + return [set, clearError] as const; };