Fix prefetch

This commit is contained in:
Zoe Roux 2025-02-07 00:05:24 +01:00
parent 7ee6c2e666
commit 176d76b246
No known key found for this signature in database
17 changed files with 142 additions and 209 deletions

View File

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

View File

@ -1,10 +1,10 @@
import { useYoshiki } from "yoshiki/native";
import { type LibraryItem, LibraryItemP } from "~/models";
import { P } from "~/primitives";
import { Fetch, type QueryIdentifier } from "~/query";
import { Fetch, type QueryIdentifier, prefetch } from "~/query";
export async function loader() {
await prefetchQuery(Header.query());
await prefetch(Header.query());
}
export default function Header() {

View File

@ -4,6 +4,7 @@
"": {
"dependencies": {
"@expo/html-elements": "^0.11.2",
"@material-symbols/svg-400": "^0.28.1",
"@tanstack/react-query": "^5.66.0",
"caniuse-api": "^3.0.0",
"expo": "~52.0.28",
@ -18,6 +19,7 @@
"react-native-reanimated": "~3.16.7",
"react-native-safe-area-context": "5.1.0",
"react-native-screens": "4.6.0",
"react-native-svg": "^15.11.1",
"react-native-web": "^0.19.13",
"yoshiki": "1.2.14",
"zod": "^3.24.1",
@ -470,6 +472,8 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
"@material-symbols/svg-400": ["@material-symbols/svg-400@0.28.1", "", {}, "sha512-lup10XPj7rzxJ4pCQICSG5iILEymcamtv+uveOm9bhgVBZq1KrZOse7VERYfIv5FYqwrP8GheUGkRAURPNc6KQ=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.6", "", { "dependencies": { "@emnapi/core": "^1.3.1", "@emnapi/runtime": "^1.3.1", "@tybys/wasm-util": "^0.9.0" } }, "sha512-z8YVS3XszxFTO73iwvFDNpQIzdMmSDTP/mB3E/ucR37V3Sx57hSExcXyMoNwaucWxnsWf4xfbZv0iZ30jr0M4Q=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@ -830,6 +834,8 @@
"bippy": ["bippy@0.0.25", "", {}, "sha512-+rvlmS7vbv704MjmpMLaSNKezGkc7xux7/DbhTp61RFQZAYwH8V0pbxGYiDWxA9a+7RxNFhHtsSIu9uoB+eK0Q=="],
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
"bplist-creator": ["bplist-creator@0.0.7", "", { "dependencies": { "stream-buffers": "~2.2.0" } }, "sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA=="],
"bplist-parser": ["bplist-parser@0.3.2", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ=="],
@ -956,6 +962,12 @@
"css-in-js-utils": ["css-in-js-utils@3.1.0", "", { "dependencies": { "hyphenate-style-name": "^1.0.3" } }, "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A=="],
"css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="],
"css-tree": ["css-tree@1.1.3", "", { "dependencies": { "mdn-data": "2.0.14", "source-map": "^0.6.1" } }, "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q=="],
"css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="],
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
@ -996,6 +1008,14 @@
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
"dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="],
"dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="],
@ -1012,6 +1032,8 @@
"end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"env-editor": ["env-editor@0.4.2", "", {}, "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA=="],
"eol": ["eol@0.9.1", "", {}, "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg=="],
@ -1382,6 +1404,8 @@
"md5-file": ["md5-file@3.2.3", "", { "dependencies": { "buffer-alloc": "^1.1.0" }, "bin": { "md5-file": "cli.js" } }, "sha512-3Tkp1piAHaworfcCgH0jKbTvj1jWWFgbvh2cXaNCgHwyTCBxxvD1Y04rmfpvdPm1P4oXMOpm6+2H7sr7v9v8Fw=="],
"mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
"memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="],
"merge": ["merge@2.1.1", "", {}, "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w=="],
@ -1484,6 +1508,8 @@
"npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
"nullthrows": ["nullthrows@1.1.1", "", {}, "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="],
"ob1": ["ob1@0.81.0", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-6Cvrkxt1tqaRdWqTAMcVYEiO5i1xcF9y7t06nFdjFqkfPsEloCf8WwhXdwBpNUkVYSQlSGS7cDgVQR86miBfBQ=="],
@ -1656,6 +1682,8 @@
"react-native-screens": ["react-native-screens@4.6.0", "", { "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-PqGtR/moJLiTMSavhfo5spKXNHZrlxffq3g5UUVPmyuu7MmazFlPvYqiAYnR2iB9tkJYgvZO6sbjYAE7619M0A=="],
"react-native-svg": ["react-native-svg@15.11.1", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-Qmwx/yJKt+AHUr4zjxx/Q69qwKtRfr1+uIfFMQoq3WFRhqU76aL9db1DyvPiY632DAsVGba1pHf92OZPkpjrdQ=="],
"react-native-web": ["react-native-web@0.19.13", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@react-native/normalize-colors": "^0.74.1", "fbjs": "^3.0.4", "inline-style-prefixer": "^6.0.1", "memoize-one": "^6.0.0", "nullthrows": "^1.1.1", "postcss-value-parser": "^4.2.0", "styleq": "^0.1.3" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-etv3bN8rJglrRCp/uL4p7l8QvUNUC++QwDbdZ8CB7BvZiMvsxfFIRM1j04vxNldG3uo2puRd6OSWR3ibtmc29A=="],
"react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
@ -2134,6 +2162,8 @@
"cpy/p-map": ["p-map@7.0.3", "", {}, "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA=="],
"css-tree/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"default-gateway/execa": ["execa@1.0.0", "", { "dependencies": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", "signal-exit": "^3.0.0", "strip-eof": "^1.0.0" } }, "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA=="],
"del/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],

View File

@ -14,6 +14,7 @@
},
"dependencies": {
"@expo/html-elements": "^0.11.2",
"@material-symbols/svg-400": "^0.28.1",
"@tanstack/react-query": "^5.66.0",
"caniuse-api": "^3.0.0",
"expo": "~52.0.28",
@ -28,6 +29,7 @@
"react-native-reanimated": "~3.16.7",
"react-native-safe-area-context": "5.1.0",
"react-native-screens": "4.6.0",
"react-native-svg": "^15.11.1",
"react-native-web": "^0.19.13",
"yoshiki": "1.2.14",
"zod": "^3.24.1"

View File

@ -20,25 +20,25 @@
export { Header, Main, Nav, Footer, UL } from "@expo/html-elements";
export * from "./text";
export * from "./themes";
export * from "./theme";
export * from "./icons";
export * from "./links";
export * from "./avatar";
export * from "./image";
export * from "./skeleton";
export * from "./tooltip";
export * from "./container";
export * from "./divider";
export * from "./progress";
export * from "./slider";
export * from "./snackbar";
export * from "./alert";
export * from "./menu";
export * from "./popup";
export * from "./select";
export * from "./input";
// export * from "./avatar";
// export * from "./image";
// export * from "./skeleton";
// export * from "./tooltip";
// export * from "./container";
// export * from "./divider";
// export * from "./progress";
// export * from "./slider";
// export * from "./snackbar";
// export * from "./alert";
// export * from "./menu";
// export * from "./popup";
// export * from "./select";
// export * from "./input";
export * from "./button";
export * from "./chip";
// export * from "./chip";
export * from "./utils";
export * from "./constants";

View File

@ -28,10 +28,10 @@ import {
type TextProps,
type View,
} from "react-native";
import { TextLink, useLink } from "solito/link";
import { parseNextPath } from "solito/router";
// import { TextLink, useLink } from "solito/link";
// import { parseNextPath } from "solito/router";
import { useTheme, useYoshiki } from "yoshiki/native";
import { alpha } from "./themes";
import { alpha } from "./theme";
export const A = ({
href,

View File

@ -1 +1,2 @@
export * from "./theme";
export * from "./catppuccin";

View File

@ -1,19 +0,0 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@ -1,22 +0,0 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
export * from "./theme";
export * from "./catppuccin";

View File

@ -1,20 +0,0 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@ -1,61 +0,0 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { useWindowDimensions } from "react-native";
import { type Breakpoints as YoshikiBreakpoint, breakpoints, isBreakpoints } from "yoshiki/native";
type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
export type Breakpoint<T> = T | AtLeastOne<YoshikiBreakpoint<T>>;
// copied from yoshiki.
const useBreakpoint = () => {
const { width } = useWindowDimensions();
const idx = Object.values(breakpoints).findIndex((x) => width <= x);
if (idx === -1) return 0;
return idx - 1;
};
const getBreakpointValue = <T>(value: Breakpoint<T>, breakpoint: number): T => {
if (!isBreakpoints(value)) return value;
const bpKeys = Object.keys(breakpoints) as Array<keyof YoshikiBreakpoint<T>>;
for (let i = breakpoint; i >= 0; i--) {
if (bpKeys[i] in value) {
const val = value[bpKeys[i]];
if (val) return val;
}
}
// This should never be reached.
return undefined!;
};
export const useBreakpointValue = <T>(value: Breakpoint<T>): T => {
const breakpoint = useBreakpoint();
return getBreakpointValue(value, breakpoint);
};
export const useBreakpointMap = <T extends Record<string, unknown>>(
value: T,
): { [key in keyof T]: T[key] extends Breakpoint<infer V> ? V : T } => {
const breakpoint = useBreakpoint();
// @ts-ignore
return Object.fromEntries(
Object.entries(value).map(([key, val]) => [key, getBreakpointValue(val, breakpoint)]),
);
};

View File

@ -18,7 +18,7 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import NextHead from "next/head";
// import NextHead from "next/head";
export const Head = ({
title,

View File

@ -18,7 +18,6 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
export * from "./breakpoints";
export * from "./nojs";
export * from "./head";
export * from "./spacing";

View File

@ -8,9 +8,9 @@ import { removeAccounts, updateAccount } from "./account-store";
import { useSetError } from "./error-provider";
import { useStoreValue } from "./settings";
const AccountContext = createContext<{
export const AccountContext = createContext<{
apiUrl: string;
authToken: Token | undefined;
authToken: Token | null;
selectedAccount?: Account;
accounts: (Account & { select: () => void; remove: () => void })[];
}>(undefined!);
@ -23,7 +23,7 @@ export const AccountProvider = ({ children }: { children: ReactNode }) => {
const acc = accounts.find((x) => x.selected);
return {
apiUrl: Platform.OS === "web" ? "/api" : acc?.apiUrl!,
authToken: acc?.token,
authToken: acc?.token ?? null,
selectedAccount: acc,
accounts: accounts.map((account) => ({
...account,

View File

@ -3,8 +3,8 @@ import { type ReactNode, useState } from "react";
// import { useUserTheme } from "@kyoo/models";
import { ThemeSelector } from "~/primitives/theme";
import { createQueryClient } from "~/query";
import { ErrorConsumer, ErrorProvider } from "./error-provider";
import { AccountProvider } from "./account-provider";
import { ErrorConsumer, ErrorProvider } from "./error-provider";
const QueryProvider = ({ children }: { children: ReactNode }) => {
const [queryClient] = useState(() => createQueryClient());

View File

@ -1,7 +1,9 @@
import { QueryClient, useInfiniteQuery, useQuery } from "@tanstack/react-query";
import { QueryClient, dehydrate, useInfiniteQuery, useQuery } from "@tanstack/react-query";
import { setServerData } from "one";
import { type ComponentType, type ReactElement, useContext } from "react";
import type { z } from "zod";
import { type KyooError, type Page, Paged } from "~/models";
import { AccountContext } from "~/providers/account-provider";
const cleanSlash = (str: string | null, keepFirst = false) => {
if (!str) return null;
@ -30,7 +32,7 @@ export const queryFn = async <Parser extends z.ZodTypeAny>(context: {
? context.formData
: undefined,
headers: {
...(context.authToken ? { Authorization: context.authToken } : {}),
...(context.authToken ? { Authorization: `Bearer ${context.authToken}` } : {}),
...("body" in context ? { "Content-Type": "application/json" } : {}),
},
signal: context.signal,
@ -157,7 +159,7 @@ export const toQueryKey = (query: {
};
export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
const { apiUrl, authToken } = useContext(QueryContext);
const { apiUrl, authToken } = useContext(AccountContext);
const key = toQueryKey({ apiUrl, path: query.path, params: query.params });
return useQuery<Data, KyooError>({
@ -167,7 +169,7 @@ export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
url: key.join("/").replace("/?", "?"),
parser: query.parser,
signal: ctx.signal,
authToken,
authToken: authToken?.access_token ?? null,
...query.options,
}),
placeholderData: query.placeholderData as any,
@ -176,7 +178,7 @@ export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
};
export const useInfiniteFetch = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {
const { apiUrl, authToken } = useContext(QueryContext);
const { apiUrl, authToken } = useContext(AccountContext);
const key = toQueryKey({ apiUrl, path: query.path, params: query.params });
const ret = useInfiniteQuery<Page<Data>, KyooError>({
@ -186,7 +188,7 @@ export const useInfiniteFetch = <Data, Ret>(query: QueryIdentifier<Data, Ret>) =
url: (ctx.pageParam as string) ?? key.join("/").replace("/?", "?"),
parser: Paged(query.parser),
signal: ctx.signal,
authToken,
authToken: authToken?.access_token ?? null,
...query.options,
}),
getNextPageParam: (page: Page<Data>) => page?.next || undefined,
@ -204,22 +206,41 @@ export const useInfiniteFetch = <Data, Ret>(query: QueryIdentifier<Data, Ret>) =
};
};
export const fetchQuery = async (queries: QueryIdentifier[], authToken?: string | null) => {
export const prefetch = async (...queries: QueryIdentifier[]) => {
const client = createQueryClient();
const authToken = undefined;
await Promise.all(
queries.map((query) => {
const key = toQueryKey({ apiUrl: "/api", path: query.path, params: query.params });
if (query.infinite) {
return client.prefetchInfiniteQuery({
queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(ctx, Paged(query.parser), authToken),
queryKey: key,
queryFn: (ctx) =>
queryFn({
url: key.join("/").replace("/?", "?"),
parser: Paged(query.parser),
signal: ctx.signal,
authToken: authToken?.access_token ?? null,
...query.options,
}),
initialPageParam: undefined,
});
}
return client.prefetchQuery({
queryKey: toQueryKey(query),
queryFn: (ctx) => queryFn(ctx, query.parser, authToken),
queryKey: key,
queryFn: (ctx) =>
queryFn({
url: key.join("/").replace("/?", "?"),
parser: query.parser,
signal: ctx.signal,
authToken: authToken?.access_token ?? null,
...query.options,
}),
});
}),
);
setServerData("queryState", dehydrate(client));
return client;
};

View File

@ -1,48 +1,48 @@
import { Main } from "@expo/html-elements";
import { type QueryPage, SetupStep } from "@kyoo/models";
import { Button, Icon, Link, P, ts } from "@kyoo/primitives";
import Register from "@material-symbols/svg-400/rounded/app_registration.svg";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useRouter } from "solito/router";
import { useYoshiki } from "yoshiki/native";
import { Navbar, NavbarProfile } from "../../../packages/ui/src/navbar";
import { KyooLongLogo } from "../../../packages/ui/src/navbar/icon";
export const SetupPage: QueryPage<{ step: SetupStep }> = ({ step }) => {
const { css } = useYoshiki();
const { t } = useTranslation();
const router = useRouter();
const isValid = Object.values(SetupStep).includes(step) && step !== SetupStep.Done;
useEffect(() => {
if (!isValid) router.replace("/");
}, [isValid, router]);
if (!isValid) return <P>Loading...</P>;
return (
<Main {...css({ flexGrow: 1, flexShrink: 1, justifyContent: "center", alignItems: "center" })}>
<P>{t(`errors.setup.${step}`)}</P>
{step === SetupStep.MissingAdminAccount && (
<Button
as={Link}
href={"/register"}
text={t("login.register")}
licon={<Icon icon={Register} {...css({ marginRight: ts(2) })} />}
/>
)}
</Main>
);
};
SetupPage.getLayout = ({ page }) => {
const { css } = useYoshiki();
return (
<>
<Navbar left={<KyooLongLogo {...css({ marginX: ts(2) })} />} right={<NavbarProfile />} />
{page}
</>
);
};
// import { Main } from "@expo/html-elements";
// import { type QueryPage, SetupStep } from "@kyoo/models";
// import { Button, Icon, Link, P, ts } from "@kyoo/primitives";
// import Register from "@material-symbols/svg-400/rounded/app_registration.svg";
// import { useEffect } from "react";
// import { useTranslation } from "react-i18next";
// import { useRouter } from "solito/router";
// import { useYoshiki } from "yoshiki/native";
// import { Navbar, NavbarProfile } from "../../../packages/ui/src/navbar";
// import { KyooLongLogo } from "../../../packages/ui/src/navbar/icon";
//
// export const SetupPage: QueryPage<{ step: SetupStep }> = ({ step }) => {
// const { css } = useYoshiki();
// const { t } = useTranslation();
// const router = useRouter();
// const isValid = Object.values(SetupStep).includes(step) && step !== SetupStep.Done;
//
// useEffect(() => {
// if (!isValid) router.replace("/");
// }, [isValid, router]);
//
// if (!isValid) return <P>Loading...</P>;
//
// return (
// <Main {...css({ flexGrow: 1, flexShrink: 1, justifyContent: "center", alignItems: "center" })}>
// <P>{t(`errors.setup.${step}`)}</P>
// {step === SetupStep.MissingAdminAccount && (
// <Button
// as={Link}
// href={"/register"}
// text={t("login.register")}
// licon={<Icon icon={Register} {...css({ marginRight: ts(2) })} />}
// />
// )}
// </Main>
// );
// };
//
// SetupPage.getLayout = ({ page }) => {
// const { css } = useYoshiki();
//
// return (
// <>
// <Navbar left={<KyooLongLogo {...css({ marginX: ts(2) })} />} right={<NavbarProfile />} />
// {page}
// </>
// );
// };