Fixup ssr fetch

This commit is contained in:
Zoe Roux 2025-02-07 18:51:50 +01:00
parent 13cc334785
commit cd519998a7
No known key found for this signature in database
8 changed files with 45 additions and 38 deletions

View File

@ -1,6 +1,5 @@
import { HydrationBoundary } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { Slot, getServerData } from "one"; import { Slot } from "one";
import { useServerHeadInsertion } from "one"; import { useServerHeadInsertion } from "one";
import { StyleRegistryProvider, createStyleRegistry, useTheme } from "yoshiki/web"; import { StyleRegistryProvider, createStyleRegistry, useTheme } from "yoshiki/web";
import { Providers } from "~/providers"; import { Providers } from "~/providers";
@ -63,8 +62,6 @@ export default function Layout() {
const registry = createStyleRegistry(); const registry = createStyleRegistry();
useServerHeadInsertion(() => registry.flushToComponent()); useServerHeadInsertion(() => registry.flushToComponent());
const queryState = getServerData("queryState");
// TODO: change this lang attr // TODO: change this lang attr
return ( return (
<html lang="en-US"> <html lang="en-US">
@ -82,12 +79,10 @@ export default function Layout() {
<body className="hoverEnabled"> <body className="hoverEnabled">
<StyleRegistryProvider registry={registry}> <StyleRegistryProvider registry={registry}>
<HydrationBoundary state={queryState}> <Providers>
<Providers> <Slot />
<Slot /> <ReactQueryDevtools initialIsOpen={false} />
<ReactQueryDevtools initialIsOpen={false} /> </Providers>
</Providers>
</HydrationBoundary>
</StyleRegistryProvider> </StyleRegistryProvider>
</body> </body>
</html> </html>

7
front/app/_middleware.ts Normal file
View File

@ -0,0 +1,7 @@
import { createMiddleware, setServerData } from "one";
export default createMiddleware(({ request, next }) => {
console.log(request);
setServerData("cookies", request.headers.get("Cookies") ?? "");
return next();
});

View File

@ -8,12 +8,14 @@ import { removeAccounts, updateAccount } from "./account-store";
import { useSetError } from "./error-provider"; import { useSetError } from "./error-provider";
import { useStoreValue } from "./settings"; import { useStoreValue } from "./settings";
export const ssrApiUrl = process.env.KYOO_URL ?? "http://back/api";
export const AccountContext = createContext<{ export const AccountContext = createContext<{
apiUrl: string; apiUrl: string;
authToken: Token | null; authToken: Token | null;
selectedAccount?: Account; selectedAccount: Account | null;
accounts: (Account & { select: () => void; remove: () => void })[]; accounts: (Account & { select: () => void; remove: () => void })[];
}>(undefined!); }>({ apiUrl: ssrApiUrl, authToken: null, selectedAccount: null, accounts: [] });
export const AccountProvider = ({ children }: { children: ReactNode }) => { export const AccountProvider = ({ children }: { children: ReactNode }) => {
const [setError, clearError] = useSetError("account"); const [setError, clearError] = useSetError("account");

View File

@ -39,7 +39,8 @@ export const ErrorConsumer = ({ children, scope }: { children: ReactNode; scope:
}; };
export const useSetError = (key: string) => { export const useSetError = (key: string) => {
const { error, setError } = useContext(ErrorContext); const { error, setError } = useContext(ErrorContext);
const set = (obj: Omit<Error, "key">) => setError({ key, ...obj }); const set = ({ key: nKey, ...obj }: Error & { key?: Error["key"] }) =>
setError({ key: nKey ?? key, ...obj });
const clearError = () => { const clearError = () => {
if (error?.key === key) setError(null); if (error?.key === key) setError(null);
}; };

View File

@ -1,4 +1,5 @@
import { QueryClientProvider } from "@tanstack/react-query"; import { HydrationBoundary, QueryClientProvider } from "@tanstack/react-query";
import { getServerData } from "one";
import { type ReactNode, useState } from "react"; import { type ReactNode, useState } from "react";
// import { useUserTheme } from "@kyoo/models"; // import { useUserTheme } from "@kyoo/models";
import { ThemeSelector } from "~/primitives/theme"; import { ThemeSelector } from "~/primitives/theme";
@ -8,7 +9,11 @@ import { ErrorConsumer, ErrorProvider } from "./error-provider";
const QueryProvider = ({ children }: { children: ReactNode }) => { const QueryProvider = ({ children }: { children: ReactNode }) => {
const [queryClient] = useState(() => createQueryClient()); const [queryClient] = useState(() => createQueryClient());
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; return (
<QueryClientProvider client={queryClient}>
<HydrationBoundary state={getServerData("queryState")}>{children}</HydrationBoundary>
</QueryClientProvider>
);
}; };
const ThemeProvider = ({ children }: { children: ReactNode }) => { const ThemeProvider = ({ children }: { children: ReactNode }) => {

View File

@ -1,3 +1,5 @@
import { getServerData } from "one";
import { Platform } from "react-native";
import { MMKV, useMMKVString } from "react-native-mmkv"; import { MMKV, useMMKVString } from "react-native-mmkv";
import type { ZodTypeAny, z } from "zod"; import type { ZodTypeAny, z } from "zod";
@ -22,12 +24,9 @@ export const setCookie = (key: string, val?: unknown) => {
document.cookie = `${key}=${value};${expires};path=/;samesite=strict`; document.cookie = `${key}=${value};${expires};path=/;samesite=strict`;
}; };
export const readCookie = <T extends ZodTypeAny>( export const readCookie = <T extends ZodTypeAny>(key: string, parser: T) => {
cookies: string | undefined, const cookies = getServerData("cookies");
key: string, console.log("cookies", cookies);
parser?: T,
) => {
if (!cookies) return undefined;
const decodedCookie = decodeURIComponent(cookies); const decodedCookie = decodeURIComponent(cookies);
const ca = decodedCookie.split(";"); const ca = decodedCookie.split(";");
@ -35,10 +34,13 @@ export const readCookie = <T extends ZodTypeAny>(
const ret = ca.find((x) => x.trimStart().startsWith(name)); const ret = ca.find((x) => x.trimStart().startsWith(name));
if (ret === undefined) return undefined; if (ret === undefined) return undefined;
const str = fromBase64(ret.substring(name.length)); const str = fromBase64(ret.substring(name.length));
return parser ? (parser.parse(JSON.parse(str)) as z.infer<T>) : str; return parser.parse(JSON.parse(str)) as z.infer<T>;
}; };
export const useStoreValue = <T extends ZodTypeAny>(key: string, parser: T) => { export const useStoreValue = <T extends ZodTypeAny>(key: string, parser: T) => {
if (Platform.OS === "web" && typeof window === "undefined") {
return readCookie(key, parser);
}
const [val] = useMMKVString(key); const [val] = useMMKVString(key);
if (val === undefined) return val; if (val === undefined) return val;
return parser.parse(JSON.parse(val)) as z.infer<T>; return parser.parse(JSON.parse(val)) as z.infer<T>;
@ -49,6 +51,9 @@ export const storeValue = (key: string, value: unknown) => {
}; };
export const readValue = <T extends ZodTypeAny>(key: string, parser: T) => { export const readValue = <T extends ZodTypeAny>(key: string, parser: T) => {
if (Platform.OS === "web" && typeof window === "undefined") {
return readCookie(key, parser);
}
const val = storage.getString(key); const val = storage.getString(key);
if (val === undefined) return val; if (val === undefined) return val;
return parser.parse(JSON.parse(val)) as z.infer<T>; return parser.parse(JSON.parse(val)) as z.infer<T>;

View File

@ -13,7 +13,7 @@ export const Fetch = <Data,>({
Loader: () => ReactElement; Loader: () => ReactElement;
}): JSX.Element | null => { }): JSX.Element | null => {
const { data, isPaused, error } = useFetch(query); const { data, isPaused, error } = useFetch(query);
const setError = useSetError(); const [setError] = useSetError("fetch");
if (error) { if (error) {
if (error.status === 401 || error.status === 403) { if (error.status === 401 || error.status === 403) {

View File

@ -1,9 +1,9 @@
import { QueryClient, dehydrate, useInfiniteQuery, useQuery } from "@tanstack/react-query"; import { QueryClient, dehydrate, useInfiniteQuery, useQuery } from "@tanstack/react-query";
import { setServerData } from "one"; import { setServerData } from "one";
import { type ComponentType, type ReactElement, useContext } from "react"; import { useContext } from "react";
import type { z } from "zod"; import type { z } from "zod";
import { type KyooError, type Page, Paged } from "~/models"; import { type KyooError, type Page, Paged } from "~/models";
import { AccountContext } from "~/providers/account-provider"; import { AccountContext, ssrApiUrl } from "~/providers/account-provider";
const cleanSlash = (str: string | null, keepFirst = false) => { const cleanSlash = (str: string | null, keepFirst = false) => {
if (!str) return null; if (!str) return null;
@ -129,18 +129,6 @@ export type QueryIdentifier<T = unknown, Ret = T> = {
}; };
}; };
export type QueryPage<Props = {}, Items = unknown> = ComponentType<
Props & { randomItems: Items[] }
> & {
getFetchUrls?: (route: { [key: string]: string }, randomItems: Items[]) => QueryIdentifier<any>[];
getLayout?:
| QueryPage<{ page: ReactElement }>
| { Layout: QueryPage<{ page: ReactElement }>; props: object };
requiredPermissions?: string[];
randomItems?: Items[];
isPublic?: boolean;
};
export const toQueryKey = (query: { export const toQueryKey = (query: {
apiUrl: string; apiUrl: string;
path: (string | undefined)[]; path: (string | undefined)[];
@ -212,7 +200,11 @@ export const prefetch = async (...queries: QueryIdentifier[]) => {
await Promise.all( await Promise.all(
queries.map((query) => { queries.map((query) => {
const key = toQueryKey({ apiUrl: "/api", path: query.path, params: query.params }); const key = toQueryKey({
apiUrl: ssrApiUrl,
path: query.path,
params: query.params,
});
if (query.infinite) { if (query.infinite) {
return client.prefetchInfiniteQuery({ return client.prefetchInfiniteQuery({