Finish account provider cleanup

This commit is contained in:
Zoe Roux 2025-02-06 22:42:40 +01:00
parent ea7cb8b4d1
commit 7ee6c2e666
No known key found for this signature in database
2 changed files with 29 additions and 63 deletions

View File

@ -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 { Platform } from "react-native";
import { z } from "zod"; 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 { useFetch } from "~/query";
import { removeAccounts, updateAccount } from "./account-store"; import { removeAccounts, updateAccount } from "./account-store";
import { useSetError } from "./error-provider"; import { useSetError } from "./error-provider";
@ -9,37 +10,19 @@ import { useStoreValue } from "./settings";
const AccountContext = createContext<{ const AccountContext = createContext<{
apiUrl: string; apiUrl: string;
authToken: Token; authToken: Token | undefined;
selectedAccount?: Account; selectedAccount?: Account;
accounts: (Account & { select: () => void; remove: () => void })[]; accounts: (Account & { select: () => void; remove: () => void })[];
}>(undefined!); }>(undefined!);
export const AccountProvider = ({ export const AccountProvider = ({ children }: { children: ReactNode }) => {
children, const [setError, clearError] = useSetError("account");
ssrAccount,
}: {
children: ReactNode;
ssrAccount?: Account;
}) => {
if (Platform.OS === "web" && typeof window === "undefined") {
const accounts = ssrAccount
? [{ ...ssrAccount, selected: true, select: () => {}, remove: () => {} }]
: [];
return (
<AccountContext.Provider value={{ ...ssrAccount, accounts }}>
{children}
</AccountContext.Provider>
);
}
const setError = useSetError();
const accounts = useStoreValue("accounts", z.array(AccountP)) ?? []; const accounts = useStoreValue("accounts", z.array(AccountP)) ?? [];
const ret = useMemo(() => { const ret = useMemo(() => {
const acc = accounts.find((x) => x.selected); const acc = accounts.find((x) => x.selected);
return { return {
apiUrl: Platform.OS === "web" ? "/api" : acc?.apiUrl, apiUrl: Platform.OS === "web" ? "/api" : acc?.apiUrl!,
authToken: acc?.token, authToken: acc?.token,
selectedAccount: acc, selectedAccount: acc,
accounts: accounts.map((account) => ({ accounts: accounts.map((account) => ({
@ -53,8 +36,6 @@ export const AccountProvider = ({
// update user's data from kyoo on startup, it could have changed. // update user's data from kyoo on startup, it could have changed.
const { const {
isSuccess: userIsSuccess, isSuccess: userIsSuccess,
isError: userIsError,
isLoading: userIsLoading,
isPlaceholderData: userIsPlaceholder, isPlaceholderData: userIsPlaceholder,
data: user, data: user,
error: userError, 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 // 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 // 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 // If we trigger the effect when the selected value change, we enter an infinite render loop
const selectedRef = useRef(selected); const selectedRef = useRef(ret.selectedAccount);
selectedRef.current = selected; selectedRef.current = ret.selectedAccount;
useEffect(() => { useEffect(() => {
if (!selectedRef.current || !userIsSuccess || userIsPlaceholder) return; 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. // 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); updateAccount(nUser.id, nUser);
}, [user, userIsSuccess, userIsPlaceholder]); }, [user, userIsSuccess, userIsPlaceholder]);
useEffect(() => {
if (!userError) return clearError();
setError({
error: userError,
retry: () => {
queryClient.resetQueries();
},
});
}, [userError]);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const oldSelected = useRef<{ id: string; token: string } | null>( const selectedId = ret.selectedAccount?.id;
selected ? { id: selected.id, token: selected.token.access_token } : null,
);
const [permissionError, setPermissionError] = useState<KyooErrors | null>(null);
useEffect(() => { useEffect(() => {
// if the user change account (or connect/disconnect), reset query cache. // 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(); queryClient.resetQueries();
} }, [selectedId, queryClient]);
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]);
// <ConnectionErrorContext.Provider
// value={{
// error: (selected ? (userError) : null) ?? permissionError,
// loading: userIsLoading,
// retry: () => {
// queryClient.invalidateQueries({ queryKey: ["auth", "me"] });
// },
// setError: setPermissionError,
// }}
return <AccountContext.Provider value={ret}>{children}</AccountContext.Provider>; return <AccountContext.Provider value={ret}>{children}</AccountContext.Provider>;
}; };

View File

@ -37,7 +37,11 @@ export const ErrorConsumer = ({ children, scope }: { children: ReactNode; scope:
const Handler = handler.view; const Handler = handler.view;
return <Handler {...(error as any)} />; return <Handler {...(error as any)} />;
}; };
export const useSetError = () => { export const useSetError = (key: string) => {
const { setError } = useContext(ErrorContext); const { error, setError } = useContext(ErrorContext);
return setError; const set = (obj: Omit<Error, "key">) => setError({ key, ...obj });
const clearError = () => {
if (error?.key === key) setError(null);
};
return [set, clearError] as const;
}; };