diff --git a/front/apps/mobile/app/(app)/search/index.tsx b/front/apps/mobile/app/(app)/search/index.tsx
index ad3064f3..2aa90ae2 100644
--- a/front/apps/mobile/app/(app)/search/index.tsx
+++ b/front/apps/mobile/app/(app)/search/index.tsx
@@ -21,12 +21,9 @@
import { SearchPage } from "@kyoo/ui";
import { Stack, useLocalSearchParams } from "expo-router";
import { useTranslation } from "react-i18next";
-import { createParam } from "solito";
-import { useRouter } from "@kyoo/primitives";
+import { useRouter, useParam } from "@kyoo/primitives";
import { useTheme } from "yoshiki/native";
-const { useParam } = createParam<{ q?: string }>();
-
const Search = () => {
const theme = useTheme();
const { back } = useRouter();
@@ -52,7 +49,7 @@ const Search = () => {
},
}}
/>
-
+
>
);
};
diff --git a/front/apps/web/src/pages/+Layout.tsx b/front/apps/web/src/pages/+Layout.tsx
index 21a864d7..da843cbf 100644
--- a/front/apps/web/src/pages/+Layout.tsx
+++ b/front/apps/web/src/pages/+Layout.tsx
@@ -22,6 +22,7 @@ import "~/polyfill";
// typeof layoutInfo === "function" ? { Layout: layoutInfo, props: {} } : layoutInfo;
// return } randomItems={[]} {...layoutProps} />;
// };
+
const GlobalCssTheme = () => {
const theme = useTheme();
return (
@@ -76,35 +77,36 @@ const GlobalCssTheme = () => {
};
export default function Layout({ children }: { children: ReactNode }) {
// TODO: theme ssr
- const userTheme = useUserTheme(undefined);
+ // const userTheme = useUserTheme(undefined);
useMobileHover();
// TODO: ssr account/error
return (
<>
-
-
-
-
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* */}
{children}
- {/*
-
- */}
-
-
-
-
-
-
+ {/* {/* */}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* */}
>
);
}
+
diff --git a/front/apps/web/src/pages/+config.ts b/front/apps/web/src/pages/+config.ts
index 37249200..9c59c79b 100644
--- a/front/apps/web/src/pages/+config.ts
+++ b/front/apps/web/src/pages/+config.ts
@@ -1,11 +1,10 @@
import type { Config } from "vike/types";
-import logoUrl from "../../public/icon.svg";
import vikeReact from "vike-react/config";
import vikeReactQuery from "vike-react-query/config";
export default {
ssr: true,
title: "Kyoo",
- favicon: logoUrl,
+
extends: [vikeReact, vikeReactQuery],
} satisfies Config;
diff --git a/front/apps/web/src/pages/index/+Page.tsx b/front/apps/web/src/pages/index/+Page.tsx
index a423e40c..42416ea1 100644
--- a/front/apps/web/src/pages/index/+Page.tsx
+++ b/front/apps/web/src/pages/index/+Page.tsx
@@ -1,3 +1,9 @@
-import { HomePage } from "@kyoo/ui";
+// import { HomePage } from "@kyoo/ui";
-export default HomePage;
+// export default HomePage;
+
+export default function Test() {
+ return
+}
diff --git a/front/apps/web/vite.config.ts b/front/apps/web/vite.config.ts
index a051333e..5505458d 100644
--- a/front/apps/web/vite.config.ts
+++ b/front/apps/web/vite.config.ts
@@ -13,5 +13,15 @@ export default {
"~": path.resolve(__dirname, "./src"),
},
},
+ build: {
+ commonjsOptions: {
+ transformMixedEsModules: true,
+ },
+ },
+ optimizeDeps: {
+ esbuildOptions: {
+ mainFields: ["module", "main"],
+ },
+ },
plugins: [react(), vike(), reactNativeWeb()],
} satisfies UserConfig;
diff --git a/front/packages/primitives/src/index.ts b/front/packages/primitives/src/index.ts
index c163e34f..583fc2a4 100644
--- a/front/packages/primitives/src/index.ts
+++ b/front/packages/primitives/src/index.ts
@@ -43,3 +43,4 @@ export * from "./chip";
export * from "./utils";
export * from "./constants";
export * from "./navigation/router";
+export * from "./navigation/params";
diff --git a/front/packages/primitives/src/links.tsx b/front/packages/primitives/src/links.tsx
index b20c6f73..6c8918be 100644
--- a/front/packages/primitives/src/links.tsx
+++ b/front/packages/primitives/src/links.tsx
@@ -23,6 +23,8 @@ import { Platform, Pressable, type TextProps, type View, type PressableProps } f
import { useTheme, useYoshiki } from "yoshiki/native";
import type { UrlObject } from "node:url";
import { alpha } from "./themes";
+import { P } from "./text";
+import { useLink } from "./navigation/link";
export const A = ({
href,
@@ -32,43 +34,16 @@ export const A = ({
...props
}: TextProps & {
href?: string | UrlObject | null;
- target?: string;
+ target?: "_blank";
replace?: boolean;
children: ReactNode;
}) => {
- const { css, theme } = useYoshiki();
+ const link = useLink(href ?? "#", { target, replace });
return (
-
+
{children}
-
+
);
};
@@ -93,20 +68,17 @@ export const PressableFeedback = forwardRef(function Feedb
});
export const Link = ({
- href: link,
+ href,
replace,
target,
children,
...props
-}: { href?: string | UrlObject | null; target?: string; replace?: boolean } & PressableProps) => {
- const href = link && typeof link === "object" ? parseNextPath(link) : link;
- const linkProps = useLink({
- href: href ?? "#",
+}: { href?: string | UrlObject | null; target?: "_blank"; replace?: boolean } & PressableProps) => {
+ const linkProps = useLink(href ?? "#", {
+ target,
replace,
- experimental: { nativeBehavior: "stack-replace", isNestedNavigator: true },
+ isNested: true,
});
- // @ts-ignore Missing hrefAttrs type definition.
- linkProps.hrefAttrs = { ...linkProps.hrefAttrs, target };
return (
{
if (e?.defaultPrevented) return;
- if (Platform.OS !== "web" && href.includes("://")) {
- Linking.openURL(href);
+ const abs = href.includes("://");
+ if (Platform.OS !== "web" && (abs || opts.target)) {
+ Linking.openURL(abs ? href : `https://${href}`);
return;
}
@@ -31,7 +32,9 @@ export const useLink = (
we.shiftKey ||
// ignore everything but left clicks
we.button !== null ||
- we.button !== 0
+ we.button !== 0 ||
+ // let the browser handle target blank
+ opts.target
)
return;
}
diff --git a/front/packages/primitives/src/navigation/params.ts b/front/packages/primitives/src/navigation/params.ts
new file mode 100644
index 00000000..7b82bc93
--- /dev/null
+++ b/front/packages/primitives/src/navigation/params.ts
@@ -0,0 +1,23 @@
+import { useCallback } from "react";
+import { usePageContext } from "vike-react/usePageContext";
+import { useRouter } from "./router";
+
+export const useParam = (name: string) => {
+ const { urlParsed } = usePageContext();
+ const router = useRouter();
+ const val = urlParsed.search[name];
+
+ const setState = useCallback(
+ (newVal: string | null) => {
+ if (newVal) urlParsed.search[name] = newVal;
+ else delete urlParsed.search[name];
+ router.replace({
+ ...urlParsed,
+ search: Object.entries(urlParsed.search).join("&"),
+ });
+ },
+ [router],
+ );
+
+ return [val, setState] as const;
+};
diff --git a/front/packages/primitives/src/navigation/router.ts b/front/packages/primitives/src/navigation/router.ts
index b70e4c69..0eea7890 100644
--- a/front/packages/primitives/src/navigation/router.ts
+++ b/front/packages/primitives/src/navigation/router.ts
@@ -1,18 +1,23 @@
import { navigate } from "vike/client/router";
import { type UrlObject, format } from "node:url";
+import { useMemo } from "react";
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();
- },
- };
+ const ret = useMemo(
+ () => ({
+ 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: true });
+ },
+ back: () => {
+ window.history.back();
+ },
+ }),
+ [],
+ );
+ return ret;
};
diff --git a/front/packages/primitives/src/navigation/router.web.ts b/front/packages/primitives/src/navigation/router.web.ts
index b683eae0..0eea7890 100644
--- a/front/packages/primitives/src/navigation/router.web.ts
+++ b/front/packages/primitives/src/navigation/router.web.ts
@@ -1,18 +1,23 @@
import { navigate } from "vike/client/router";
import { type UrlObject, format } from "node:url";
+import { useMemo } from "react";
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();
- },
- };
+ const ret = useMemo(
+ () => ({
+ 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: true });
+ },
+ back: () => {
+ window.history.back();
+ },
+ }),
+ [],
+ );
+ return ret;
};
diff --git a/front/packages/ui/src/browse/index.tsx b/front/packages/ui/src/browse/index.tsx
index 9b038f69..20a22c7b 100644
--- a/front/packages/ui/src/browse/index.tsx
+++ b/front/packages/ui/src/browse/index.tsx
@@ -25,18 +25,16 @@ import {
type QueryPage,
getDisplayDate,
} from "@kyoo/models";
+import { useParam } from "@kyoo/primitives";
import { type ComponentProps, useState } from "react";
-import { createParam } from "solito";
+import { DefaultLayout } from "../layout";
import type { WithLoading } from "../fetch";
import { InfiniteFetch } from "../fetch-infinite";
-import { DefaultLayout } from "../layout";
import { ItemGrid } from "./grid";
import { BrowseSettings } from "./header";
import { ItemList } from "./list";
import { Layout, SortBy, SortOrd } from "./types";
-const { useParam } = createParam<{ sortBy?: string }>();
-
export const itemMap = (
item: WithLoading,
): WithLoading & ComponentProps> => {
diff --git a/front/packages/ui/src/login/login.tsx b/front/packages/ui/src/login/login.tsx
index cfdbea09..25a3e0b9 100644
--- a/front/packages/ui/src/login/login.tsx
+++ b/front/packages/ui/src/login/login.tsx
@@ -44,7 +44,7 @@ export const LoginPage: QueryPage<{ apiUrl?: string; error?: string }> = ({
const { css } = useYoshiki();
useEffect(() => {
- if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", false);
+ if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", { isNested: false });
}, [apiUrl, router]);
return (
@@ -70,7 +70,7 @@ export const LoginPage: QueryPage<{ apiUrl?: string; error?: string }> = ({
});
setError(error);
if (error) return;
- router.replace("/", false);
+ router.replace("/", { isNested: false });
}}
{...css({
m: ts(1),
diff --git a/front/packages/ui/src/login/oidc.tsx b/front/packages/ui/src/login/oidc.tsx
index 44b31e3e..a935c79b 100644
--- a/front/packages/ui/src/login/oidc.tsx
+++ b/front/packages/ui/src/login/oidc.tsx
@@ -104,12 +104,12 @@ export const OidcCallbackPage: QueryPage<{
hasRun.current = true;
function onError(error: string) {
- router.replace({ pathname: "/login", query: { error, apiUrl } }, false);
+ router.replace({ pathname: "/login", query: { error, apiUrl } }, { isNested: false });
}
async function run() {
const { error: loginError } = await oidcLogin(provider, code, apiUrl);
if (loginError) onError(loginError);
- else router.replace("/", false);
+ else router.replace("/", { isNested: false });
}
if (error) onError(error);
diff --git a/front/packages/ui/src/login/register.tsx b/front/packages/ui/src/login/register.tsx
index efca0d1e..877d70ee 100644
--- a/front/packages/ui/src/login/register.tsx
+++ b/front/packages/ui/src/login/register.tsx
@@ -41,7 +41,7 @@ export const RegisterPage: QueryPage<{ apiUrl?: string }> = ({ apiUrl }) => {
const { css } = useYoshiki();
useEffect(() => {
- if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", false);
+ if (!apiUrl && Platform.OS !== "web") router.replace("/server-url", { isNested: false });
}, [apiUrl, router]);
return (
@@ -79,7 +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("/", false);
+ router.replace("/", { isNested: false });
}}
{...css({
m: ts(1),
diff --git a/front/packages/ui/src/navbar/index.tsx b/front/packages/ui/src/navbar/index.tsx
index ec001dc4..ee210ec7 100644
--- a/front/packages/ui/src/navbar/index.tsx
+++ b/front/packages/ui/src/navbar/index.tsx
@@ -29,6 +29,7 @@ import {
Link,
Menu,
PressableFeedback,
+ useRouter,
tooltip,
ts,
} from "@kyoo/primitives";
@@ -40,11 +41,10 @@ import Search from "@material-symbols/svg-400/rounded/search-fill.svg";
import Settings from "@material-symbols/svg-400/rounded/settings.svg";
import { forwardRef, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
-import { Platform, type TextInput, View, type ViewProps } from "react-native";
-import { useRouter } from "solito/router";
import { type Stylable, useYoshiki } from "yoshiki/native";
import { AdminPage } from "../admin";
import { KyooLongLogo } from "./icon";
+import { Platform, TextInput, View, ViewProps } from "react-native";
export const NavbarTitle = (props: Stylable & { onLayout?: ViewProps["onLayout"] }) => {
const { t } = useTranslation();
@@ -66,7 +66,7 @@ const SearchBar = forwardRef(function SearchBar(props, ref)
useEffect(() => {
if (Platform.OS !== "web" || !hasChanged.current) return;
const action = window.location.pathname.startsWith("/search") ? replace : push;
- if (query) action(`/search?q=${encodeURI(query)}`, undefined, { shallow: true });
+ if (query) action(`/search?q=${encodeURI(query)}`);
else back();
}, [query, push, replace, back]);
diff --git a/front/packages/ui/src/player/index.tsx b/front/packages/ui/src/player/index.tsx
index 01a1fdad..1f30218d 100644
--- a/front/packages/ui/src/player/index.tsx
+++ b/front/packages/ui/src/player/index.tsx
@@ -158,8 +158,8 @@ export const Player = ({
startTime={startTime}
onEnd={() => {
if (!data) return;
- if (data.type === "movie") router.replace(`/movie/${data.slug}`, true);
- else router.replace(next ?? `/show/${data.show!.slug}`, true);
+ if (data.type === "movie") router.replace(`/movie/${data.slug}`, { isNested: true });
+ else router.replace(next ?? `/show/${data.show!.slug}`, { isNested: true });
}}
{...css(StyleSheet.absoluteFillObject)}
/>
diff --git a/front/packages/ui/src/search/index.tsx b/front/packages/ui/src/search/index.tsx
index 8d88b3bb..4939bef8 100644
--- a/front/packages/ui/src/search/index.tsx
+++ b/front/packages/ui/src/search/index.tsx
@@ -19,19 +19,16 @@
*/
import { type LibraryItem, LibraryItemP, type QueryIdentifier, type QueryPage } from "@kyoo/models";
-import { usePageStyle } from "@kyoo/primitives";
import { useState } from "react";
import { useTranslation } from "react-i18next";
-import { createParam } from "solito";
+import { DefaultLayout } from "../layout";
+import { InfiniteFetch } from "../fetch-infinite";
import { itemMap } from "../browse";
import { ItemGrid } from "../browse/grid";
import { BrowseSettings } from "../browse/header";
import { ItemList } from "../browse/list";
+import { useParam, usePageStyle } from "@kyoo/primitives";
import { Layout, SearchSort, SortOrd } from "../browse/types";
-import { InfiniteFetch } from "../fetch-infinite";
-import { DefaultLayout } from "../layout";
-
-const { useParam } = createParam<{ sortBy?: string }>();
const query = (
query?: string,
diff --git a/front/yarn.lock b/front/yarn.lock
index e89ceb05..6695fea2 100644
--- a/front/yarn.lock
+++ b/front/yarn.lock
@@ -3144,7 +3144,6 @@ __metadata:
react-native-blurhash: ^1.1.11
react-native-fast-image: ^8.6.3
react-native-safe-area-context: 4.8.2
- solito: ^4.2.0
typescript: ^5.3.3
peerDependencies:
"@gorhom/portal": "*"
@@ -12079,15 +12078,6 @@ __metadata:
languageName: node
linkType: hard
-"solito@npm:^4.2.0":
- version: 4.2.0
- resolution: "solito@npm:4.2.0"
- dependencies:
- typescript: ^5.0.4
- checksum: 58ea67fce743cc864e7cb9065d06fa287fd99bab1b0a96c5d9bd1e974740bb4a308620622c9b46975d58ba084127a8388dc7696cc6e171ed6b501843093ab64f
- languageName: node
- linkType: hard
-
"source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2":
version: 1.0.2
resolution: "source-map-js@npm:1.0.2"
@@ -12740,7 +12730,7 @@ __metadata:
languageName: node
linkType: hard
-"typescript@npm:5.3.3, typescript@npm:^5.0.4, typescript@npm:^5.3.3":
+"typescript@npm:5.3.3, typescript@npm:^5.3.3":
version: 5.3.3
resolution: "typescript@npm:5.3.3"
bin:
@@ -12750,7 +12740,7 @@ __metadata:
languageName: node
linkType: hard
-"typescript@patch:typescript@5.3.3#~builtin, typescript@patch:typescript@^5.0.4#~builtin, typescript@patch:typescript@^5.3.3#~builtin":
+"typescript@patch:typescript@5.3.3#~builtin, typescript@patch:typescript@^5.3.3#~builtin":
version: 5.3.3
resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=701156"
bin: