mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Persist mutations
This commit is contained in:
parent
78e30cd516
commit
4a035327ef
@ -18,16 +18,26 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NavbarTitle } from "@kyoo/ui";
|
import { Account, ConnectionErrorContext, useAccount } from "@kyoo/models";
|
||||||
import { Stack } from "expo-router";
|
import { NavbarProfile, NavbarTitle } from "@kyoo/ui";
|
||||||
|
import { Redirect, Stack } from "expo-router";
|
||||||
|
import { useContext, useRef } from "react";
|
||||||
import { useTheme } from "yoshiki/native";
|
import { useTheme } from "yoshiki/native";
|
||||||
|
|
||||||
export default function PublicLayout() {
|
export default function PublicLayout() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const account = useAccount();
|
||||||
|
const { error } = useContext(ConnectionErrorContext);
|
||||||
|
const oldAccount = useRef<Account | null>(null);
|
||||||
|
|
||||||
|
if (account && !error && account != oldAccount.current) return <Redirect href="/" />;
|
||||||
|
oldAccount.current = account;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
headerTitle: () => <NavbarTitle />,
|
headerTitle: () => <NavbarTitle />,
|
||||||
|
headerRight: () => <NavbarProfile />,
|
||||||
contentStyle: {
|
contentStyle: {
|
||||||
backgroundColor: theme.background,
|
backgroundColor: theme.background,
|
||||||
},
|
},
|
||||||
|
@ -22,8 +22,10 @@ import "react-native-reanimated";
|
|||||||
|
|
||||||
import { PortalProvider } from "@gorhom/portal";
|
import { PortalProvider } from "@gorhom/portal";
|
||||||
import { ThemeSelector } from "@kyoo/primitives";
|
import { ThemeSelector } from "@kyoo/primitives";
|
||||||
import { AccountProvider, createQueryClient } from "@kyoo/models";
|
import { DownloadProvider } from "@kyoo/ui";
|
||||||
import { QueryClientProvider } from "@tanstack/react-query";
|
import { AccountProvider, createQueryClient, storage } from "@kyoo/models";
|
||||||
|
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
|
||||||
|
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { Slot } from "expo-router";
|
import { Slot } from "expo-router";
|
||||||
import { getLocales } from "expo-localization";
|
import { getLocales } from "expo-localization";
|
||||||
@ -48,7 +50,29 @@ import "@formatjs/intl-displaynames/locale-data/fr";
|
|||||||
import en from "../../../translations/en.json";
|
import en from "../../../translations/en.json";
|
||||||
import fr from "../../../translations/fr.json";
|
import fr from "../../../translations/fr.json";
|
||||||
import { useTheme } from "yoshiki/native";
|
import { useTheme } from "yoshiki/native";
|
||||||
import { DownloadProvider } from "@kyoo/ui";
|
import NetInfo from "@react-native-community/netinfo";
|
||||||
|
import { onlineManager } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
onlineManager.setEventListener((setOnline) => {
|
||||||
|
return NetInfo.addEventListener((state) => {
|
||||||
|
setOnline(!!state.isConnected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const clientStorage = {
|
||||||
|
setItem: (key, value) => {
|
||||||
|
storage.set(key, value);
|
||||||
|
},
|
||||||
|
getItem: (key) => {
|
||||||
|
const value = storage.getString(key);
|
||||||
|
return value === undefined ? null : value;
|
||||||
|
},
|
||||||
|
removeItem: (key) => {
|
||||||
|
storage.delete(key);
|
||||||
|
},
|
||||||
|
} satisfies Partial<Storage>;
|
||||||
|
|
||||||
|
export const clientPersister = createSyncStoragePersister({ storage: clientStorage });
|
||||||
|
|
||||||
i18next.use(initReactI18next).init({
|
i18next.use(initReactI18next).init({
|
||||||
interpolation: {
|
interpolation: {
|
||||||
@ -92,7 +116,14 @@ export default function Root() {
|
|||||||
|
|
||||||
if (!fontsLoaded) return null;
|
if (!fontsLoaded) return null;
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<PersistQueryClientProvider
|
||||||
|
client={queryClient}
|
||||||
|
persistOptions={{
|
||||||
|
persister: clientPersister,
|
||||||
|
// Only dehydrate mutations, queries are not json serializable anyways.
|
||||||
|
dehydrateOptions: { shouldDehydrateQuery: () => false },
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ThemeSelector
|
<ThemeSelector
|
||||||
theme={theme ?? "light"}
|
theme={theme ?? "light"}
|
||||||
font={{
|
font={{
|
||||||
@ -112,6 +143,6 @@ export default function Root() {
|
|||||||
</AccountProvider>
|
</AccountProvider>
|
||||||
</PortalProvider>
|
</PortalProvider>
|
||||||
</ThemeSelector>
|
</ThemeSelector>
|
||||||
</QueryClientProvider>
|
</PersistQueryClientProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,11 @@
|
|||||||
"@kesha-antonov/react-native-background-downloader": "^2.10.0",
|
"@kesha-antonov/react-native-background-downloader": "^2.10.0",
|
||||||
"@kyoo/ui": "workspace:^",
|
"@kyoo/ui": "workspace:^",
|
||||||
"@material-symbols/svg-400": "^0.14.1",
|
"@material-symbols/svg-400": "^0.14.1",
|
||||||
|
"@react-native-community/netinfo": "^11.2.1",
|
||||||
"@shopify/flash-list": "1.4.3",
|
"@shopify/flash-list": "1.4.3",
|
||||||
|
"@tanstack/query-sync-storage-persister": "^5.14.1",
|
||||||
"@tanstack/react-query": "^5.12.1",
|
"@tanstack/react-query": "^5.12.1",
|
||||||
|
"@tanstack/react-query-persist-client": "^5.14.1",
|
||||||
"array-shuffle": "^3.0.0",
|
"array-shuffle": "^3.0.0",
|
||||||
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
||||||
"expo": "^49.0.21",
|
"expo": "^49.0.21",
|
||||||
|
@ -144,7 +144,7 @@ export const AccountProvider = ({
|
|||||||
|
|
||||||
export const useAccount = () => {
|
export const useAccount = () => {
|
||||||
const acc = useContext(AccountContext);
|
const acc = useContext(AccountContext);
|
||||||
return acc.find((x) => x.selected);
|
return acc.find((x) => x.selected) || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAccounts = () => {
|
export const useAccounts = () => {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./accounts";
|
export * from "./accounts";
|
||||||
|
export { storage } from "./account-internal";
|
||||||
export * from "./resources";
|
export * from "./resources";
|
||||||
export * from "./traits";
|
export * from "./traits";
|
||||||
export * from "./page";
|
export * from "./page";
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
QueryClient,
|
QueryClient,
|
||||||
QueryFunctionContext,
|
QueryFunctionContext,
|
||||||
useInfiniteQuery,
|
useInfiniteQuery,
|
||||||
|
useMutation,
|
||||||
useQuery,
|
useQuery,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -131,6 +132,13 @@ export const queryFn = async <Data,>(
|
|||||||
return parsed.data;
|
return parsed.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MutationParam = {
|
||||||
|
params?: Record<string, number | string>;
|
||||||
|
body?: object;
|
||||||
|
path: string[];
|
||||||
|
method: "POST" | "DELETE";
|
||||||
|
};
|
||||||
|
|
||||||
export const createQueryClient = () =>
|
export const createQueryClient = () =>
|
||||||
new QueryClient({
|
new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
@ -141,6 +149,15 @@ export const createQueryClient = () =>
|
|||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
retry: false,
|
retry: false,
|
||||||
},
|
},
|
||||||
|
mutations: {
|
||||||
|
mutationFn: (({ method, path, body, params }: MutationParam) => {
|
||||||
|
return queryFn({
|
||||||
|
method,
|
||||||
|
path: toQueryKey({ path, params }),
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}) as any,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -165,7 +182,10 @@ export type QueryPage<Props = {}, Items = unknown> = ComponentType<
|
|||||||
randomItems?: Items[];
|
randomItems?: Items[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toQueryKey = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {
|
export const toQueryKey = (query: {
|
||||||
|
path: (string | undefined)[];
|
||||||
|
params?: { [query: string]: boolean | number | string | string[] | undefined };
|
||||||
|
}) => {
|
||||||
if (query.params) {
|
if (query.params) {
|
||||||
return [
|
return [
|
||||||
...query.path,
|
...query.path,
|
||||||
|
@ -25,9 +25,12 @@ import { deleteAsync } from "expo-file-system";
|
|||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
Episode,
|
Episode,
|
||||||
|
EpisodeP,
|
||||||
Movie,
|
Movie,
|
||||||
|
MovieP,
|
||||||
QueryIdentifier,
|
QueryIdentifier,
|
||||||
WatchInfo,
|
WatchInfo,
|
||||||
|
WatchInfoP,
|
||||||
queryFn,
|
queryFn,
|
||||||
toQueryKey,
|
toQueryKey,
|
||||||
} from "@kyoo/models";
|
} from "@kyoo/models";
|
||||||
@ -38,6 +41,7 @@ import { ReactNode, useEffect } from "react";
|
|||||||
import { Platform, ToastAndroid } from "react-native";
|
import { Platform, ToastAndroid } from "react-native";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { Router } from "expo-router/build/types";
|
import { Router } from "expo-router/build/types";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
status: "DOWNLOADING" | "PAUSED" | "DONE" | "FAILED" | "STOPPED";
|
status: "DOWNLOADING" | "PAUSED" | "DONE" | "FAILED" | "STOPPED";
|
||||||
@ -202,8 +206,8 @@ export const DownloadProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
const t = tasks.find((x) => x.id == dl.data.id);
|
const t = tasks.find((x) => x.id == dl.data.id);
|
||||||
if (t) return setupDownloadTask(dl, t, store);
|
if (t) return setupDownloadTask(dl, t, store);
|
||||||
return {
|
return {
|
||||||
data: dl.data,
|
data: z.union([EpisodeP, MovieP]).parse(dl.data),
|
||||||
info: dl.info,
|
info: WatchInfoP.parse(dl.info),
|
||||||
path: dl.path,
|
path: dl.path,
|
||||||
state: atom({
|
state: atom({
|
||||||
status: dl.state.status === "DONE" ? "DONE" : "FAILED",
|
status: dl.state.status === "DONE" ? "DONE" : "FAILED",
|
||||||
|
@ -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 { WatchStatusV, queryFn, useAccount } from "@kyoo/models";
|
import { MutationParam, WatchStatusV, useAccount } from "@kyoo/models";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { useEffect, useCallback } from "react";
|
import { useEffect, useCallback } from "react";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
@ -34,20 +34,23 @@ export const WatchStatusObserver = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const account = useAccount();
|
const account = useAccount();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { mutate } = useMutation({
|
const { mutate: _mutate } = useMutation<unknown, Error, MutationParam>({
|
||||||
mutationFn: (seconds: number) =>
|
mutationKey: [type, slug, "watchStatus"],
|
||||||
queryFn({
|
|
||||||
path: [
|
|
||||||
type,
|
|
||||||
slug,
|
|
||||||
"watchStatus",
|
|
||||||
`?status=${WatchStatusV.Watching}&watchedTime=${Math.round(seconds)}`,
|
|
||||||
],
|
|
||||||
method: "POST",
|
|
||||||
}),
|
|
||||||
onSettled: async () =>
|
onSettled: async () =>
|
||||||
await queryClient.invalidateQueries({ queryKey: [type === "episode" ? "show" : type, slug] }),
|
await queryClient.invalidateQueries({ queryKey: [type === "episode" ? "show" : type, slug] }),
|
||||||
});
|
});
|
||||||
|
const mutate = useCallback(
|
||||||
|
(seconds: number) =>
|
||||||
|
_mutate({
|
||||||
|
method: "POST",
|
||||||
|
path: [type, slug, "watchStatus"],
|
||||||
|
params: {
|
||||||
|
status: WatchStatusV.Watching,
|
||||||
|
watchedTime: Math.round(seconds),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[_mutate, type, slug],
|
||||||
|
);
|
||||||
const readProgress = useAtomCallback(
|
const readProgress = useAtomCallback(
|
||||||
useCallback((get) => {
|
useCallback((get) => {
|
||||||
const currCount = get(progressAtom);
|
const currCount = get(progressAtom);
|
||||||
|
@ -4089,6 +4089,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@react-native-community/netinfo@npm:^11.2.1":
|
||||||
|
version: 11.2.1
|
||||||
|
resolution: "@react-native-community/netinfo@npm:11.2.1"
|
||||||
|
peerDependencies:
|
||||||
|
react-native: ">=0.59"
|
||||||
|
checksum: d54b75c6dc6d7a8cbb054d84ed9326e1715259137e333f26cd801c6b820996cacceb5a1cf3ee94b91866361c040b7a12eb78db3695b076e8f30c3403985f98d5
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@react-native/assets-registry@npm:^0.72.0":
|
"@react-native/assets-registry@npm:^0.72.0":
|
||||||
version: 0.72.0
|
version: 0.72.0
|
||||||
resolution: "@react-native/assets-registry@npm:0.72.0"
|
resolution: "@react-native/assets-registry@npm:0.72.0"
|
||||||
@ -4574,6 +4583,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@tanstack/query-core@npm:5.14.1":
|
||||||
|
version: 5.14.1
|
||||||
|
resolution: "@tanstack/query-core@npm:5.14.1"
|
||||||
|
checksum: 412cea2a9fa675fe64e405412bc2233295229e0b39d677146fd9b671765d033a8edcefb4d2fb5365a47c3cc5b2f15a75062501d37973cb4c9deb64fd501ff9e1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@tanstack/query-devtools@npm:5.12.1":
|
"@tanstack/query-devtools@npm:5.12.1":
|
||||||
version: 5.12.1
|
version: 5.12.1
|
||||||
resolution: "@tanstack/query-devtools@npm:5.12.1"
|
resolution: "@tanstack/query-devtools@npm:5.12.1"
|
||||||
@ -4581,6 +4597,25 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@tanstack/query-persist-client-core@npm:5.14.1":
|
||||||
|
version: 5.14.1
|
||||||
|
resolution: "@tanstack/query-persist-client-core@npm:5.14.1"
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/query-core": 5.14.1
|
||||||
|
checksum: 437f3e8e41c71e88a5770e10912a5a2c9e18dfed063240fb2a6efaa9d2cdfd04bdf1d392f9e793e030cdd8440f0ea81b3a09722ad8adcb683a1642d32ef2e6b4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tanstack/query-sync-storage-persister@npm:^5.14.1":
|
||||||
|
version: 5.14.1
|
||||||
|
resolution: "@tanstack/query-sync-storage-persister@npm:5.14.1"
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/query-core": 5.14.1
|
||||||
|
"@tanstack/query-persist-client-core": 5.14.1
|
||||||
|
checksum: 60006d134dd7da7d2627509daff2c5cd20060f01083287f999292231af354962390d24e8b578c2dcca8ddb2f8e383f46008b5579db34745df3554aff70d410cc
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@tanstack/react-query-devtools@npm:^5.12.2":
|
"@tanstack/react-query-devtools@npm:^5.12.2":
|
||||||
version: 5.12.2
|
version: 5.12.2
|
||||||
resolution: "@tanstack/react-query-devtools@npm:5.12.2"
|
resolution: "@tanstack/react-query-devtools@npm:5.12.2"
|
||||||
@ -4593,6 +4628,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@tanstack/react-query-persist-client@npm:^5.14.1":
|
||||||
|
version: 5.14.1
|
||||||
|
resolution: "@tanstack/react-query-persist-client@npm:5.14.1"
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/query-persist-client-core": 5.14.1
|
||||||
|
peerDependencies:
|
||||||
|
"@tanstack/react-query": ^5.14.1
|
||||||
|
react: ^18.0.0
|
||||||
|
checksum: 8df54287bfcccbf6d636e3559cf416a5e90518fd3a287458ef85f6205c961370afc1e61cedc7a6035a2eb632c08ac93a02e25744b955b36ac37ab93fc536b6dd
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@tanstack/react-query@npm:^5.12.1":
|
"@tanstack/react-query@npm:^5.12.1":
|
||||||
version: 5.12.1
|
version: 5.12.1
|
||||||
resolution: "@tanstack/react-query@npm:5.12.1"
|
resolution: "@tanstack/react-query@npm:5.12.1"
|
||||||
@ -11310,8 +11357,11 @@ __metadata:
|
|||||||
"@kesha-antonov/react-native-background-downloader": ^2.10.0
|
"@kesha-antonov/react-native-background-downloader": ^2.10.0
|
||||||
"@kyoo/ui": "workspace:^"
|
"@kyoo/ui": "workspace:^"
|
||||||
"@material-symbols/svg-400": ^0.14.1
|
"@material-symbols/svg-400": ^0.14.1
|
||||||
|
"@react-native-community/netinfo": ^11.2.1
|
||||||
"@shopify/flash-list": 1.4.3
|
"@shopify/flash-list": 1.4.3
|
||||||
|
"@tanstack/query-sync-storage-persister": ^5.14.1
|
||||||
"@tanstack/react-query": ^5.12.1
|
"@tanstack/react-query": ^5.12.1
|
||||||
|
"@tanstack/react-query-persist-client": ^5.14.1
|
||||||
"@types/react": 18.2.39
|
"@types/react": 18.2.39
|
||||||
array-shuffle: ^3.0.0
|
array-shuffle: ^3.0.0
|
||||||
babel-plugin-transform-inline-environment-variables: ^0.4.4
|
babel-plugin-transform-inline-environment-variables: ^0.4.4
|
||||||
|
@ -208,7 +208,7 @@ pub async fn identify(path: String) -> Option<MediaInfo> {
|
|||||||
extension: Path::new(&path)
|
extension: Path::new(&path)
|
||||||
.extension()
|
.extension()
|
||||||
.map(|x| x.to_os_string().into_string().unwrap())
|
.map(|x| x.to_os_string().into_string().unwrap())
|
||||||
.unwrap_or(String::from(".mkv")),
|
.unwrap_or(String::from("mkv")),
|
||||||
container: general["Format"].as_str().unwrap().to_string(),
|
container: general["Format"].as_str().unwrap().to_string(),
|
||||||
video: {
|
video: {
|
||||||
let v = output["media"]["track"]
|
let v = output["media"]["track"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user