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 { HydrationBoundary } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { Slot } from "one"; import { Slot, getServerData } 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,6 +63,8 @@ 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">

View File

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

View File

@ -4,6 +4,7 @@
"": { "": {
"dependencies": { "dependencies": {
"@expo/html-elements": "^0.11.2", "@expo/html-elements": "^0.11.2",
"@material-symbols/svg-400": "^0.28.1",
"@tanstack/react-query": "^5.66.0", "@tanstack/react-query": "^5.66.0",
"caniuse-api": "^3.0.0", "caniuse-api": "^3.0.0",
"expo": "~52.0.28", "expo": "~52.0.28",
@ -18,6 +19,7 @@
"react-native-reanimated": "~3.16.7", "react-native-reanimated": "~3.16.7",
"react-native-safe-area-context": "5.1.0", "react-native-safe-area-context": "5.1.0",
"react-native-screens": "4.6.0", "react-native-screens": "4.6.0",
"react-native-svg": "^15.11.1",
"react-native-web": "^0.19.13", "react-native-web": "^0.19.13",
"yoshiki": "1.2.14", "yoshiki": "1.2.14",
"zod": "^3.24.1", "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=="], "@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=="], "@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=="], "@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=="], "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-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=="], "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-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=="], "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
@ -996,6 +1008,14 @@
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], "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": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="],
"dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="], "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=="], "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=="], "env-editor": ["env-editor@0.4.2", "", {}, "sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA=="],
"eol": ["eol@0.9.1", "", {}, "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg=="], "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=="], "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=="], "memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="],
"merge": ["merge@2.1.1", "", {}, "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w=="], "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=="], "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=="], "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=="], "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-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-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=="], "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=="], "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=="], "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=="], "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": { "dependencies": {
"@expo/html-elements": "^0.11.2", "@expo/html-elements": "^0.11.2",
"@material-symbols/svg-400": "^0.28.1",
"@tanstack/react-query": "^5.66.0", "@tanstack/react-query": "^5.66.0",
"caniuse-api": "^3.0.0", "caniuse-api": "^3.0.0",
"expo": "~52.0.28", "expo": "~52.0.28",
@ -28,6 +29,7 @@
"react-native-reanimated": "~3.16.7", "react-native-reanimated": "~3.16.7",
"react-native-safe-area-context": "5.1.0", "react-native-safe-area-context": "5.1.0",
"react-native-screens": "4.6.0", "react-native-screens": "4.6.0",
"react-native-svg": "^15.11.1",
"react-native-web": "^0.19.13", "react-native-web": "^0.19.13",
"yoshiki": "1.2.14", "yoshiki": "1.2.14",
"zod": "^3.24.1" "zod": "^3.24.1"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,8 +3,8 @@ 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";
import { createQueryClient } from "~/query"; import { createQueryClient } from "~/query";
import { ErrorConsumer, ErrorProvider } from "./error-provider";
import { AccountProvider } from "./account-provider"; import { AccountProvider } from "./account-provider";
import { ErrorConsumer, ErrorProvider } from "./error-provider";
const QueryProvider = ({ children }: { children: ReactNode }) => { const QueryProvider = ({ children }: { children: ReactNode }) => {
const [queryClient] = useState(() => createQueryClient()); 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 ComponentType, type ReactElement, 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";
const cleanSlash = (str: string | null, keepFirst = false) => { const cleanSlash = (str: string | null, keepFirst = false) => {
if (!str) return null; if (!str) return null;
@ -30,7 +32,7 @@ export const queryFn = async <Parser extends z.ZodTypeAny>(context: {
? context.formData ? context.formData
: undefined, : undefined,
headers: { headers: {
...(context.authToken ? { Authorization: context.authToken } : {}), ...(context.authToken ? { Authorization: `Bearer ${context.authToken}` } : {}),
...("body" in context ? { "Content-Type": "application/json" } : {}), ...("body" in context ? { "Content-Type": "application/json" } : {}),
}, },
signal: context.signal, signal: context.signal,
@ -157,7 +159,7 @@ export const toQueryKey = (query: {
}; };
export const useFetch = <Data,>(query: QueryIdentifier<Data>) => { 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 }); const key = toQueryKey({ apiUrl, path: query.path, params: query.params });
return useQuery<Data, KyooError>({ return useQuery<Data, KyooError>({
@ -167,7 +169,7 @@ export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {
url: key.join("/").replace("/?", "?"), url: key.join("/").replace("/?", "?"),
parser: query.parser, parser: query.parser,
signal: ctx.signal, signal: ctx.signal,
authToken, authToken: authToken?.access_token ?? null,
...query.options, ...query.options,
}), }),
placeholderData: query.placeholderData as any, 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>) => { 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 key = toQueryKey({ apiUrl, path: query.path, params: query.params });
const ret = useInfiniteQuery<Page<Data>, KyooError>({ 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("/?", "?"), url: (ctx.pageParam as string) ?? key.join("/").replace("/?", "?"),
parser: Paged(query.parser), parser: Paged(query.parser),
signal: ctx.signal, signal: ctx.signal,
authToken, authToken: authToken?.access_token ?? null,
...query.options, ...query.options,
}), }),
getNextPageParam: (page: Page<Data>) => page?.next || undefined, 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 client = createQueryClient();
const authToken = undefined;
await Promise.all( await Promise.all(
queries.map((query) => { queries.map((query) => {
const key = toQueryKey({ apiUrl: "/api", path: query.path, params: query.params });
if (query.infinite) { if (query.infinite) {
return client.prefetchInfiniteQuery({ return client.prefetchInfiniteQuery({
queryKey: toQueryKey(query), queryKey: key,
queryFn: (ctx) => queryFn(ctx, Paged(query.parser), authToken), queryFn: (ctx) =>
queryFn({
url: key.join("/").replace("/?", "?"),
parser: Paged(query.parser),
signal: ctx.signal,
authToken: authToken?.access_token ?? null,
...query.options,
}),
initialPageParam: undefined, initialPageParam: undefined,
}); });
} }
return client.prefetchQuery({ return client.prefetchQuery({
queryKey: toQueryKey(query), queryKey: key,
queryFn: (ctx) => queryFn(ctx, query.parser, authToken), 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; return client;
}; };

View File

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