wip: Rewrite router

This commit is contained in:
Zoe Roux 2024-05-10 21:14:22 +02:00
parent d953e9e769
commit cb65325c56
No known key found for this signature in database
16 changed files with 116 additions and 73 deletions

View File

@ -22,7 +22,7 @@ import { SearchPage } from "@kyoo/ui";
import { Stack, useLocalSearchParams } from "expo-router";
import { useTranslation } from "react-i18next";
import { createParam } from "solito";
import { useRouter } from "solito/router";
import { useRouter } from "@kyoo/primitives";
import { useTheme } from "yoshiki/native";
const { useParam } = createParam<{ q?: string }>();

View File

@ -54,8 +54,7 @@
},
"dependencies": {
"@expo/html-elements": "^0.9.1",
"@tanstack/react-query": "^5.17.19",
"solito": "^4.2.0"
"@tanstack/react-query": "^5.17.19"
},
"optionalDependencies": {
"@radix-ui/react-select": "^2.0.0",

View File

@ -42,3 +42,4 @@ export * from "./chip";
export * from "./utils";
export * from "./constants";
export * from "./navigation/router";

View File

@ -18,19 +18,10 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import type { UrlObject } from "node:url";
import { type ReactNode, forwardRef } from "react";
import {
Linking,
Platform,
Pressable,
type PressableProps,
type TextProps,
type View,
} from "react-native";
import { TextLink, useLink } from "solito/link";
import { parseNextPath } from "solito/router";
import { forwardRef, type ReactNode } from "react";
import { Platform, Pressable, type TextProps, type View, type PressableProps } from "react-native";
import { useTheme, useYoshiki } from "yoshiki/native";
import type { UrlObject } from "node:url";
import { alpha } from "./themes";
export const A = ({
@ -122,9 +113,7 @@ export const Link = ({
{...props}
onPress={(e?: any) => {
props?.onPress?.(e);
if (e?.defaultPrevented) return;
if (Platform.OS !== "web" && href?.includes("://")) Linking.openURL(href);
else linkProps.onPress(e);
linkProps.onPress(e);
}}
>
{children}

View File

@ -39,6 +39,7 @@ import { useRouter } from "solito/router";
import { percent, px, sm, useYoshiki, vh, xl } from "yoshiki/native";
import { Icon, IconButton } from "./icons";
import { PressableFeedback } from "./links";
import { useRouter } from "./navigation/router";
import { P } from "./text";
import { ContrastArea, SwitchVariant } from "./themes";
import { ts } from "./utils";

View File

@ -0,0 +1,44 @@
import { type UrlObject, format } from "node:url";
import type { MouseEvent } from "react";
import { Platform, type PressableProps, Linking } from "react-native";
import { useRouter } from "./router";
export const useLink = (
route: string | UrlObject,
opts: { replace?: boolean; target?: "_blank"; isNested?: boolean } = {},
) => {
const router = useRouter();
const href = typeof route === "object" ? format(route) : route;
return {
accessibilityRole: "link",
href,
onPress: (e) => {
if (e?.defaultPrevented) return;
if (Platform.OS !== "web" && href.includes("://")) {
Linking.openURL(href);
return;
}
if (Platform.OS === "web" && e) {
// Web event
const we = e as unknown as MouseEvent;
if (
// ignore clicks with modifier keys
we.metaKey ||
we.altKey ||
we.ctrlKey ||
we.shiftKey ||
// ignore everything but left clicks
we.button !== null ||
we.button !== 0
)
return;
}
e.preventDefault();
if (opts.replace === true) router.replace(href, { isNested: opts.isNested });
else router.push(href);
},
} satisfies PressableProps & { href?: string };
};

View File

@ -0,0 +1,18 @@
import { navigate } from "vike/client/router";
import { type UrlObject, format } from "node:url";
export const useRouter = () => {
return {
push: (route: string | UrlObject) => {
if (typeof route === "object") route = format(route);
navigate(route);
},
replace: (route: string | UrlObject, opts: {isNested?: boolean} = {}) => {
if (typeof route === "object") route = format(route);
navigate(route, { overwriteLastHistoryEntry: opts.isNested });
},
back: () => {
window.history.back();
},
};
};

View File

@ -0,0 +1,18 @@
import { navigate } from "vike/client/router";
import { type UrlObject, format } from "node:url";
export const useRouter = () => {
return {
push: (route: string | UrlObject) => {
if (typeof route === "object") route = format(route);
navigate(route);
},
replace: (route: string | UrlObject, isNested = true) => {
if (typeof route === "object") route = format(route);
navigate(route, { overwriteLastHistoryEntry: true });
},
back: () => {
window.history.back();
},
};
};

View File

@ -44,10 +44,7 @@ export const LoginPage: QueryPage<{ apiUrl?: string; error?: string }> = ({
const { css } = useYoshiki();
useEffect(() => {
if (!apiUrl && Platform.OS !== "web")
router.replace("/server-url", undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", false);
}, [apiUrl, router]);
return (
@ -73,9 +70,7 @@ export const LoginPage: QueryPage<{ apiUrl?: string; error?: string }> = ({
});
setError(error);
if (error) return;
router.replace("/", undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
router.replace("/", false);
}}
{...css({
m: ts(1),

View File

@ -26,12 +26,11 @@ import {
oidcLogin,
useFetch,
} from "@kyoo/models";
import { Button, HR, Link, P, Skeleton, ts } from "@kyoo/primitives";
import { useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { ImageBackground, View } from "react-native";
import { useRouter } from "solito/router";
import { useRouter, Button, HR, Link, P, Skeleton, ts } from "@kyoo/primitives";
import { View, ImageBackground } from "react-native";
import { percent, rem, useYoshiki } from "yoshiki/native";
import { useTranslation } from "react-i18next";
import { useEffect, useRef } from "react";
import { ErrorView } from "../errors";
export const OidcLogin = ({ apiUrl }: { apiUrl?: string }) => {
@ -105,18 +104,12 @@ export const OidcCallbackPage: QueryPage<{
hasRun.current = true;
function onError(error: string) {
router.replace({ pathname: "/login", query: { error, apiUrl } }, undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
router.replace({ pathname: "/login", query: { error, apiUrl } }, false);
}
async function run() {
const { error: loginError } = await oidcLogin(provider, code, apiUrl);
if (loginError) onError(loginError);
else {
router.replace("/", undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
}
else router.replace("/", false);
}
if (error) onError(error);

View File

@ -18,13 +18,11 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { type QueryPage, login } from "@kyoo/models";
import { A, Button, H1, Input, P, ts } from "@kyoo/primitives";
import { login, type QueryPage } from "@kyoo/models";
import { Button, P, Input, ts, H1, A, useRouter } from "@kyoo/primitives";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Trans } from "react-i18next";
import { Platform } from "react-native";
import { useRouter } from "solito/router";
import { percent, px, useYoshiki } from "yoshiki/native";
import { DefaultLayout } from "../layout";
import { FormPage } from "./form";
@ -43,10 +41,7 @@ export const RegisterPage: QueryPage<{ apiUrl?: string }> = ({ apiUrl }) => {
const { css } = useYoshiki();
useEffect(() => {
if (!apiUrl && Platform.OS !== "web")
router.replace("/server-url", undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", false);
}, [apiUrl, router]);
return (
@ -84,9 +79,7 @@ export const RegisterPage: QueryPage<{ apiUrl?: string }> = ({ apiUrl }) => {
const { error } = await login("register", { email, username, password, apiUrl });
setError(error);
if (error) return;
router.replace("/", undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
router.replace("/", false);
}}
{...css({
m: ts(1),

View File

@ -25,11 +25,10 @@ import {
ServerInfoP,
useFetch,
} from "@kyoo/models";
import { Button, H1, HR, Input, Link, P, ts } from "@kyoo/primitives";
import { useRouter, Button, P, Link, Input, ts, H1, HR } from "@kyoo/primitives";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { ImageBackground, Platform, View } from "react-native";
import { useRouter } from "solito/router";
import { type Theme, percent, useYoshiki } from "yoshiki/native";
import { DefaultLayout } from "../layout";

View File

@ -18,7 +18,6 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import type { Audio, Chapter, KyooImage, Subtitle } from "@kyoo/models";
import {
CircularProgress,
ContrastArea,
@ -35,15 +34,16 @@ import {
tooltip,
ts,
useIsTouch,
useRouter,
} from "@kyoo/primitives";
import ArrowBack from "@material-symbols/svg-400/rounded/arrow_back-fill.svg";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { atom } from "jotai";
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import type { Chapter, KyooImage, Subtitle, Audio } from "@kyoo/models";
import { useAtomValue, useSetAtom, useAtom } from "jotai";
import { type ImageStyle, Platform, Pressable, View, type ViewProps } from "react-native";
import { useRouter } from "solito/router";
import { useTranslation } from "react-i18next";
import { percent, rem, useYoshiki } from "yoshiki/native";
import ArrowBack from "@material-symbols/svg-400/rounded/arrow_back-fill.svg";
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
import { atom } from "jotai";
import {
bufferedAtom,
durationAtom,

View File

@ -28,12 +28,11 @@ import {
WatchInfoP,
useFetch,
} from "@kyoo/models";
import { Head } from "@kyoo/primitives";
import { useSetAtom } from "jotai";
import { type ComponentProps, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Head, useRouter } from "@kyoo/primitives";
import { useState, useEffect, type ComponentProps } from "react";
import { Platform, StyleSheet, View } from "react-native";
import { useRouter } from "solito/router";
import { useTranslation } from "react-i18next";
import { useSetAtom } from "jotai";
import { useYoshiki } from "yoshiki/native";
import { episodeDisplayNumber } from "../details/episode";
import { ErrorView } from "../errors";
@ -159,14 +158,8 @@ export const Player = ({
startTime={startTime}
onEnd={() => {
if (!data) return;
if (data.type === "movie")
router.replace(`/movie/${data.slug}`, undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true },
});
else
router.replace(next ?? `/show/${data.show!.slug}`, undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true },
});
if (data.type === "movie") router.replace(`/movie/${data.slug}`, true);
else router.replace(next ?? `/show/${data.show!.slug}`, true);
}}
{...css(StyleSheet.absoluteFillObject)}
/>

View File

@ -20,9 +20,9 @@
import type { Subtitle } from "@kyoo/models";
import { atom, useSetAtom } from "jotai";
import { useRouter } from "@kyoo/primitives";
import { useEffect } from "react";
import { Platform } from "react-native";
import { useRouter } from "solito/router";
import {
durationAtom,
fullscreenAtom,

View File

@ -19,8 +19,8 @@
*/
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { useRouter } from "@kyoo/primitives";
import { useEffect } from "react";
import { useRouter } from "solito/router";
import { reducerAtom } from "./keyboard";
import { durationAtom, playAtom, progressAtom } from "./state";