diff --git a/front/src/components/items/context-menus.tsx b/front/src/components/items/context-menus.tsx
index 955da0e8..69ac3da6 100644
--- a/front/src/components/items/context-menus.tsx
+++ b/front/src/components/items/context-menus.tsx
@@ -15,14 +15,14 @@ import { watchListIcon } from "./watchlist-info";
// import { useDownloader } from "../../packages/ui/src/downloadses/ui/src/downloads";
export const EpisodesContext = ({
- type = "episode",
+ kind = "episode",
slug,
showSlug,
status,
force,
...props
}: {
- type?: "serie" | "movie" | "episode";
+ kind?: "serie" | "movie" | "episode";
showSlug?: string | null;
slug: string;
status: WatchStatusV | null;
@@ -34,17 +34,17 @@ export const EpisodesContext = ({
const { t } = useTranslation();
const mutation = useMutation({
- path: [type, slug, "watchStatus"],
+ path: [kind, slug, "watchStatus"],
compute: (newStatus: WatchStatusV | null) => ({
method: newStatus ? "POST" : "DELETE",
params: newStatus ? { status: newStatus } : undefined,
}),
- invalidate: [type, slug],
+ invalidate: [kind, slug],
});
const metadataRefreshMutation = useMutation({
method: "POST",
- path: [type, slug, "refresh"],
+ path: [kind, slug, "refresh"],
invalidate: null,
});
@@ -54,7 +54,10 @@ export const EpisodesContext = ({
Trigger={IconButton}
icon={MoreVert}
{...tooltip(t("misc.more"))}
- {...(css([Platform.OS !== "web" && !force && { display: "none" }], props) as any)}
+ {...(css(
+ [Platform.OS !== "web" && !force && { display: "none" }],
+ props,
+ ) as any)}
>
{showSlug && (
(
}`)}
+ label={t(
+ `show.watchlistMark.${x.toLowerCase() as Lowercase}`,
+ )}
onSelect={() => mutation.mutate(x)}
selected={x === status}
/>
@@ -83,7 +88,7 @@ export const EpisodesContext = ({
/>
)}
- {type !== "serie" && (
+ {kind !== "serie" && (
<>
{/*
>
)}
@@ -113,20 +118,20 @@ export const EpisodesContext = ({
};
export const ItemContext = ({
- type,
+ kind,
slug,
status,
force,
...props
}: {
- type: "movie" | "serie";
+ kind: "movie" | "serie";
slug: string;
status: WatchStatusV | null;
force?: boolean;
} & Partial>>) => {
return (
{
const { css } = useYoshiki("episodebox");
@@ -48,7 +54,7 @@ export const ItemGrid = ({
href,
slug,
name,
- type,
+ kind,
subtitle,
poster,
watchStatus,
@@ -63,7 +69,7 @@ export const ItemGrid = ({
poster: KImage | null;
watchStatus: WatchStatusV | null;
watchPercent: number | null;
- type: "movie" | "serie" | "collection";
+ kind: "movie" | "serie" | "collection";
unseenEpisodesCount: number | null;
} & Stylable<"text">) => {
const [moreOpened, setMoreOpened] = useState(false);
@@ -111,11 +117,16 @@ export const ItemGrid = ({
layout={{ width: percent(100) }}
{...(css("poster") as { style: ImageStyle })}
>
-
- {type === "movie" && watchPercent && }
- {type !== "collection" && (
+
+ {kind === "movie" && watchPercent && (
+
+ )}
+ {kind !== "collection" && (
theme.dark.background,
},
"more",
- Platform.OS === "web" && moreOpened && { display: important("flex") },
+ Platform.OS === "web" &&
+ moreOpened && { display: important("flex") },
])}
/>
)}
-
+
{name}
{subtitle && (
diff --git a/front/src/components/items/item-list.tsx b/front/src/components/items/item-list.tsx
index e8012011..7794fb81 100644
--- a/front/src/components/items/item-list.tsx
+++ b/front/src/components/items/item-list.tsx
@@ -1,26 +1,26 @@
import { useState } from "react";
import { Platform, View } from "react-native";
import { percent, px, rem, useYoshiki } from "yoshiki/native";
-import type { KyooImage, WatchStatusV } from "~/models";
+import type { KImage, WatchStatusV } from "~/models";
import {
GradientImageBackground,
Heading,
+ important,
Link,
P,
Poster,
PosterBackground,
Skeleton,
- imageBorderRadius,
- important,
ts,
} from "~/primitives";
import type { Layout } from "~/query";
+import { ItemContext } from "./context-menus";
import { ItemWatchStatus } from "./item-helpers";
export const ItemList = ({
href,
slug,
- type,
+ kind,
name,
subtitle,
thumbnail,
@@ -31,11 +31,11 @@ export const ItemList = ({
}: {
href: string;
slug: string;
- type: "movie" | "show" | "collection";
+ kind: "movie" | "serie" | "collection";
name: string;
subtitle: string | null;
- poster: KyooImage | null;
- thumbnail: KyooImage | null;
+ poster: KImage | null;
+ thumbnail: KImage | null;
watchStatus: WatchStatusV | null;
unseenEpisodesCount: number | null;
}) => {
@@ -43,98 +43,110 @@ export const ItemList = ({
const [moreOpened, setMoreOpened] = useState(false);
return (
- setMoreOpened(true)}
- {...css(
- {
- alignItems: "center",
- justifyContent: "space-evenly",
- flexDirection: "row",
- height: ItemList.layout.size,
- borderRadius: px(imageBorderRadius),
- overflow: "hidden",
- marginX: ItemList.layout.gap,
- child: {
- more: {
- opacity: 0,
- },
- },
- fover: {
- title: {
- textDecorationLine: "underline",
- },
- more: {
- opacity: 100,
- },
+ {...css({
+ child: {
+ more: {
+ opacity: 0,
},
},
- props,
- )}
+ fover: {
+ title: {
+ textDecorationLine: "underline",
+ },
+ more: {
+ opacity: 100,
+ },
+ },
+ })}
>
-
-
- {name}
-
- {type !== "collection" && (
- setMoreOpened(v)}
- {...css([
- {
- // I dont know why marginLeft gets overwritten by the margin: px(2) so we important
- marginLeft: important(ts(2)),
- bg: (theme) => theme.darkOverlay,
- },
- "more",
- Platform.OS === "web" && moreOpened && { opacity: important(100) },
- ])}
- />
- )}
-
- {subtitle && (
-
- {subtitle}
-
- )}
-
-
-
-
-
+
+ {name}
+
+ {kind !== "collection" && (
+ setMoreOpened(v)}
+ {...css([
+ {
+ // I dont know why marginLeft gets overwritten by the margin: px(2) so we important
+ marginLeft: important(ts(2)),
+ bg: (theme) => theme.darkOverlay,
+ },
+ "more",
+ Platform.OS === "web" &&
+ moreOpened && { opacity: important(100) },
+ ])}
+ />
+ )}
+
+ {subtitle && (
+
+ {subtitle}
+
+ )}
+
+
+
+
+
+
);
};
@@ -149,7 +161,7 @@ ItemList.Loader = (props: object) => {
justifyContent: "space-evenly",
flexDirection: "row",
height: ItemList.layout.size,
- borderRadius: px(imageBorderRadius),
+ borderRadius: px(10),
overflow: "hidden",
bg: (theme) => theme.dark.background,
marginX: ItemList.layout.gap,
diff --git a/front/src/primitives/image-background.tsx b/front/src/primitives/image-background.tsx
index 727d790e..9fda0352 100644
--- a/front/src/primitives/image-background.tsx
+++ b/front/src/primitives/image-background.tsx
@@ -2,6 +2,7 @@ import { ImageBackground as EImageBackground } from "expo-image";
import { LinearGradient, type LinearGradientProps } from "expo-linear-gradient";
import type { ComponentProps, ReactNode } from "react";
import type { ImageStyle } from "react-native";
+import { Platform } from "react-native";
import { useYoshiki } from "yoshiki/native";
import type { KImage } from "~/models";
import { useToken } from "~/providers/account-context";
@@ -31,11 +32,13 @@ export const ImageBackground = ({
);
};
diff --git a/front/src/primitives/image.tsx b/front/src/primitives/image.tsx
index 7663dc91..6650f9dc 100644
--- a/front/src/primitives/image.tsx
+++ b/front/src/primitives/image.tsx
@@ -1,6 +1,6 @@
import { Image as EImage } from "expo-image";
import type { ComponentProps } from "react";
-import type { ImageStyle, ViewStyle } from "react-native";
+import { type ImageStyle, Platform, type ViewStyle } from "react-native";
import { useYoshiki } from "yoshiki/native";
import type { YoshikiStyle } from "yoshiki/src/type";
import type { KImage } from "~/models";
@@ -41,11 +41,13 @@ export const Image = ({
}> = Partial & U[keyof U];
export type Breakpoint = T | AtLeastOne>;
@@ -7,9 +7,9 @@ export type Breakpoint = T | AtLeastOne>;
// copied from yoshiki.
const useBreakpoint = () => {
const { width } = useWindowDimensions();
- const idx = Object.values(breakpoints).findIndex((x) => width <= x);
+ const idx = Object.values(breakpoints).findLastIndex((x) => x <= width);
if (idx === -1) return 0;
- return idx - 1;
+ return idx;
};
const getBreakpointValue = (value: Breakpoint, breakpoint: number): T => {
diff --git a/front/src/primitives/utils/spacing.tsx b/front/src/primitives/utils/spacing.tsx
index 2cb39cdc..e30d1356 100644
--- a/front/src/primitives/utils/spacing.tsx
+++ b/front/src/primitives/utils/spacing.tsx
@@ -1,12 +1,11 @@
import { Platform } from "react-native";
-import { px } from "yoshiki/native";
export const important = (value: T): T => {
return `${value} !important` as T;
};
export const ts = (spacing: number) => {
- return px(spacing * 8);
+ return spacing * 8;
};
export const focusReset: object =
diff --git a/front/src/query/fetch-infinite.tsx b/front/src/query/fetch-infinite.tsx
index 0b521997..48fb804b 100644
--- a/front/src/query/fetch-infinite.tsx
+++ b/front/src/query/fetch-infinite.tsx
@@ -80,12 +80,10 @@ export const InfiniteFetch = ({
onEndReachedThreshold={0.5}
onRefresh={layout.layout !== "horizontal" ? refetch : undefined}
refreshing={isRefetching}
-
ListHeaderComponent={Header}
ItemSeparatorComponent={divider === true ? HR : (divider as any) || undefined}
ListEmptyComponent={Empty}
-
- contentContainerStyle={{ gap, margin: gap }}
+ contentContainerStyle={{ gap, marginHorizontal: gap }}
{...props}
/>
);
diff --git a/front/src/ui/browse/header.tsx b/front/src/ui/browse/header.tsx
index 2fd93f82..e2ce0bae 100644
--- a/front/src/ui/browse/header.tsx
+++ b/front/src/ui/browse/header.tsx
@@ -11,10 +11,22 @@ import ViewList from "@material-symbols/svg-400/rounded/view_list.svg";
import { useTranslation } from "react-i18next";
import { type PressableProps, View } from "react-native";
import { useYoshiki } from "yoshiki/native";
-import { HR, Icon, IconButton, Menu, P, PressableFeedback, tooltip, ts } from "~/primitives";
-import { type SortBy, type SortOrd, availableSorts } from "./types";
+import {
+ HR,
+ Icon,
+ IconButton,
+ Menu,
+ P,
+ PressableFeedback,
+ tooltip,
+ ts,
+} from "~/primitives";
+import { availableSorts, type SortBy, type SortOrd } from "./types";
-const SortTrigger = ({ sortBy, ...props }: { sortBy: SortBy } & PressableProps) => {
+const SortTrigger = ({
+ sortBy,
+ ...props
+}: { sortBy: SortBy } & PressableProps) => {
const { css } = useYoshiki();
const { t } = useTranslation();
@@ -48,8 +60,17 @@ const MediaTypeTrigger = ({
{...css({ flexDirection: "row", alignItems: "center" }, props as any)}
{...tooltip(t("browse.mediatype-tt"))}
>
-
- {t(mediaType !== "all" ? `browse.mediatypekey.${mediaType}` : "browse.mediatypelabel")}
+
+
+ {t(
+ mediaType !== "all"
+ ? `browse.mediatypekey.${mediaType}`
+ : "browse.mediatypelabel",
+ )}
+
);
};
@@ -75,8 +96,9 @@ export const BrowseSettings = ({
const { t } = useTranslation();
// TODO: have a proper filter frontend
- const mediaType = /kind eq (\w+)/.exec(filter)?.groups?.[0] ?? "all";
- const setMediaType = (kind: string) => setFilter(kind !== "all " ? `kind eq ${kind}` : "");
+ const mediaType = /kind eq (\w+)/.exec(filter)?.[1] ?? "all";
+ const setMediaType = (kind: string) =>
+ setFilter(kind !== "all" ? `kind eq ${kind}` : "");
return (
setSort(x, sortBy === x && sortOrd === "asc" ? "desc" : "asc")}
+ onSelect={() =>
+ setSort(x, sortBy === x && sortOrd === "asc" ? "desc" : "asc")
+ }
/>
))}
@@ -115,8 +139,13 @@ export const BrowseSettings = ({
{...css({ padding: ts(0.5), marginY: "auto" })}
/>
-
-