mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Fix browse layout switch
This commit is contained in:
parent
0ad9c86756
commit
552926d2cb
@ -187,7 +187,7 @@ ItemGrid.Loader = (props: object) => {
|
||||
};
|
||||
|
||||
ItemGrid.layout = {
|
||||
size: px(150),
|
||||
size: 150,
|
||||
numColumns: { xs: 3, sm: 4, md: 5, lg: 6, xl: 8 },
|
||||
gap: { xs: ts(1), sm: ts(2), md: ts(4) },
|
||||
layout: "grid",
|
||||
|
@ -3,6 +3,7 @@ import { Platform, View } from "react-native";
|
||||
import { percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||
import type { KImage, WatchStatusV } from "~/models";
|
||||
import {
|
||||
ContrastArea,
|
||||
GradientImageBackground,
|
||||
Heading,
|
||||
important,
|
||||
@ -39,114 +40,117 @@ export const ItemList = ({
|
||||
watchStatus: WatchStatusV | null;
|
||||
unseenEpisodesCount: number | null;
|
||||
}) => {
|
||||
const { css } = useYoshiki("line");
|
||||
const [moreOpened, setMoreOpened] = useState(false);
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={moreOpened ? undefined : href}
|
||||
onLongPress={() => setMoreOpened(true)}
|
||||
{...css({
|
||||
child: {
|
||||
more: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
fover: {
|
||||
title: {
|
||||
textDecorationLine: "underline",
|
||||
},
|
||||
more: {
|
||||
opacity: 100,
|
||||
},
|
||||
},
|
||||
})}
|
||||
>
|
||||
<GradientImageBackground
|
||||
src={thumbnail}
|
||||
alt={name}
|
||||
quality="medium"
|
||||
layout={{ width: percent(100), height: ItemList.layout.size }}
|
||||
{...(css(
|
||||
{
|
||||
alignItems: "center",
|
||||
justifyContent: "space-evenly",
|
||||
flexDirection: "row",
|
||||
borderRadius: px(10),
|
||||
overflow: "hidden",
|
||||
},
|
||||
props,
|
||||
) as any)}
|
||||
>
|
||||
<View
|
||||
<ContrastArea>
|
||||
{({ css }) => (
|
||||
<Link
|
||||
href={moreOpened ? undefined : href}
|
||||
onLongPress={() => setMoreOpened(true)}
|
||||
{...css({
|
||||
width: { xs: "50%", lg: "30%" },
|
||||
child: {
|
||||
more: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
fover: {
|
||||
title: {
|
||||
textDecorationLine: "underline",
|
||||
},
|
||||
more: {
|
||||
opacity: 100,
|
||||
},
|
||||
},
|
||||
})}
|
||||
{...props}
|
||||
>
|
||||
<View
|
||||
{...css({
|
||||
<GradientImageBackground
|
||||
src={thumbnail}
|
||||
alt={name}
|
||||
quality="medium"
|
||||
layout={{ width: percent(100), height: ItemList.layout.size }}
|
||||
gradientStyle={{
|
||||
alignItems: "center",
|
||||
justifyContent: "space-evenly",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
})}
|
||||
}}
|
||||
{...(css({
|
||||
borderRadius: px(10),
|
||||
overflow: "hidden",
|
||||
}) as any)}
|
||||
>
|
||||
<Heading
|
||||
{...css([
|
||||
"title",
|
||||
{
|
||||
textAlign: "center",
|
||||
fontSize: rem(2),
|
||||
letterSpacing: rem(0.002),
|
||||
fontWeight: "900",
|
||||
textTransform: "uppercase",
|
||||
},
|
||||
])}
|
||||
>
|
||||
{name}
|
||||
</Heading>
|
||||
{kind !== "collection" && (
|
||||
<ItemContext
|
||||
kind={kind}
|
||||
slug={slug}
|
||||
status={watchStatus}
|
||||
isOpen={moreOpened}
|
||||
setOpen={(v) => 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) },
|
||||
])}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
{subtitle && (
|
||||
<P
|
||||
<View
|
||||
{...css({
|
||||
textAlign: "center",
|
||||
marginRight: ts(4),
|
||||
width: { xs: "50%", lg: "30%" },
|
||||
})}
|
||||
>
|
||||
{subtitle}
|
||||
</P>
|
||||
)}
|
||||
</View>
|
||||
<PosterBackground
|
||||
src={poster}
|
||||
alt=""
|
||||
quality="low"
|
||||
layout={{ height: percent(80) }}
|
||||
>
|
||||
<ItemWatchStatus
|
||||
watchStatus={watchStatus}
|
||||
unseenEpisodesCount={unseenEpisodesCount}
|
||||
/>
|
||||
</PosterBackground>
|
||||
</GradientImageBackground>
|
||||
</Link>
|
||||
<View
|
||||
{...css({
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
})}
|
||||
>
|
||||
<Heading
|
||||
{...css([
|
||||
"title",
|
||||
{
|
||||
textAlign: "center",
|
||||
fontSize: rem(2),
|
||||
letterSpacing: rem(0.002),
|
||||
fontWeight: "900",
|
||||
textTransform: "uppercase",
|
||||
},
|
||||
])}
|
||||
>
|
||||
{name}
|
||||
</Heading>
|
||||
{kind !== "collection" && (
|
||||
<ItemContext
|
||||
kind={kind}
|
||||
slug={slug}
|
||||
status={watchStatus}
|
||||
isOpen={moreOpened}
|
||||
setOpen={(v) => 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) },
|
||||
])}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
{subtitle && (
|
||||
<P
|
||||
{...css({
|
||||
textAlign: "center",
|
||||
marginRight: ts(4),
|
||||
})}
|
||||
>
|
||||
{subtitle}
|
||||
</P>
|
||||
)}
|
||||
</View>
|
||||
<PosterBackground
|
||||
src={poster}
|
||||
alt=""
|
||||
quality="low"
|
||||
layout={{ height: percent(80) }}
|
||||
>
|
||||
<ItemWatchStatus
|
||||
watchStatus={watchStatus}
|
||||
unseenEpisodesCount={unseenEpisodesCount}
|
||||
/>
|
||||
</PosterBackground>
|
||||
</GradientImageBackground>
|
||||
</Link>
|
||||
)}
|
||||
</ContrastArea>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,9 @@ export const PosterBackground = ({
|
||||
...props
|
||||
}: Omit<ComponentProps<typeof ImageBackground>, "layout"> & {
|
||||
style?: ImageStyle;
|
||||
layout: YoshikiEnhanced<{ width: ImageStyle["width"] } | { height: ImageStyle["height"] }>;
|
||||
layout: YoshikiEnhanced<
|
||||
{ width: ImageStyle["width"] } | { height: ImageStyle["height"] }
|
||||
>;
|
||||
}) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
@ -67,10 +69,12 @@ export const PosterBackground = ({
|
||||
|
||||
export const GradientImageBackground = ({
|
||||
gradient,
|
||||
gradientStyle,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<typeof ImageBackground> & {
|
||||
gradient?: Partial<LinearGradientProps>;
|
||||
gradientStyle?: Parameters<ReturnType<typeof useYoshiki>["css"]>[0];
|
||||
}) => {
|
||||
const { css, theme } = useYoshiki();
|
||||
|
||||
@ -81,13 +85,16 @@ export const GradientImageBackground = ({
|
||||
end={{ x: 0, y: 1 }}
|
||||
colors={["transparent", theme.darkOverlay]}
|
||||
{...css(
|
||||
{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
[
|
||||
{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
},
|
||||
gradientStyle,
|
||||
],
|
||||
typeof gradient === "object" ? gradient : undefined,
|
||||
)}
|
||||
>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useRouter } from "expo-router";
|
||||
import { type ReactNode, forwardRef } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import {
|
||||
Linking,
|
||||
Platform,
|
||||
@ -7,7 +7,6 @@ import {
|
||||
type PressableProps,
|
||||
Text,
|
||||
type TextProps,
|
||||
type View,
|
||||
} from "react-native";
|
||||
import { useTheme, useYoshiki } from "yoshiki/native";
|
||||
import { alpha } from "./theme";
|
||||
@ -26,7 +25,9 @@ function useLinkTo({
|
||||
onPress: (e) => {
|
||||
if (e?.defaultPrevented) return;
|
||||
if (href.startsWith("http")) {
|
||||
Platform.OS === "web" ? window.open(href, "_blank") : Linking.openURL(href);
|
||||
Platform.OS === "web"
|
||||
? window.open(href, "_blank")
|
||||
: Linking.openURL(href);
|
||||
} else {
|
||||
replace ? router.replace(href) : router.push(href);
|
||||
}
|
||||
@ -65,25 +66,26 @@ export const A = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const PressableFeedback = forwardRef<View, PressableProps>(function Feedback(
|
||||
{ children, ...props },
|
||||
ref,
|
||||
) {
|
||||
export const PressableFeedback = ({ children, ...props }: PressableProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
ref={ref}
|
||||
// TODO: Enable ripple on tv. Waiting for https://github.com/react-native-tvos/react-native-tvos/issues/440
|
||||
{...(Platform.isTV
|
||||
? {}
|
||||
: { android_ripple: { foreground: true, color: alpha(theme.contrast, 0.5) as any } })}
|
||||
: {
|
||||
android_ripple: {
|
||||
foreground: true,
|
||||
color: alpha(theme.contrast, 0.5) as any,
|
||||
},
|
||||
})}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Pressable>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const Link = ({
|
||||
href,
|
||||
|
@ -48,11 +48,19 @@ export const InfiniteFetch = <Data, Props, _, Kind extends number | string>({
|
||||
const { numColumns, size, gap } = useBreakpointMap(layout);
|
||||
const [setOffline, clearOffline] = useSetError("offline");
|
||||
const oldItems = useRef<Data[] | undefined>(undefined);
|
||||
let { items, isPaused, error, fetchNextPage, isFetching, refetch, isRefetching } =
|
||||
useInfiniteFetch(query);
|
||||
let {
|
||||
items,
|
||||
isPaused,
|
||||
error,
|
||||
fetchNextPage,
|
||||
isFetching,
|
||||
refetch,
|
||||
isRefetching,
|
||||
} = useInfiniteFetch(query);
|
||||
if (incremental && items) oldItems.current = items;
|
||||
|
||||
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
|
||||
if (!query.infinite)
|
||||
console.warn("A non infinite query was passed to an InfiniteFetch.");
|
||||
|
||||
if (isPaused) setOffline();
|
||||
else clearOffline();
|
||||
@ -60,10 +68,13 @@ export const InfiniteFetch = <Data, Props, _, Kind extends number | string>({
|
||||
if (error) return <ErrorView error={error} />;
|
||||
|
||||
if (incremental) items ??= oldItems.current;
|
||||
const count = items ? numColumns - (items.length % numColumns) : placeholderCount;
|
||||
console.log(numColumns, count);
|
||||
const count = items
|
||||
? numColumns - (items.length % numColumns)
|
||||
: placeholderCount;
|
||||
const placeholders = [...Array(count === 0 ? numColumns : count)].fill(null);
|
||||
const data = isFetching || !items ? [...(items || []), ...placeholders] : items;
|
||||
const data =
|
||||
isFetching || !items ? [...(items || []), ...placeholders] : items;
|
||||
|
||||
return (
|
||||
<LegendList
|
||||
data={data}
|
||||
@ -71,9 +82,8 @@ export const InfiniteFetch = <Data, Props, _, Kind extends number | string>({
|
||||
renderItem={({ item, index }) =>
|
||||
item ? <Render index={index} item={item} /> : <Loader index={index} />
|
||||
}
|
||||
// keyExtractor={(item: any, index) => (item ? item.id : index)}
|
||||
// estimatedItemSize={size}
|
||||
|
||||
keyExtractor={(item: any, index) => (item ? item.id : index)}
|
||||
estimatedItemSize={size}
|
||||
horizontal={layout.layout === "horizontal"}
|
||||
numColumns={layout.layout === "horizontal" ? 1 : numColumns}
|
||||
onEndReached={fetchMore ? () => fetchNextPage() : undefined}
|
||||
@ -81,7 +91,9 @@ export const InfiniteFetch = <Data, Props, _, Kind extends number | string>({
|
||||
onRefresh={layout.layout !== "horizontal" ? refetch : undefined}
|
||||
refreshing={isRefetching}
|
||||
ListHeaderComponent={Header}
|
||||
ItemSeparatorComponent={divider === true ? HR : (divider as any) || undefined}
|
||||
ItemSeparatorComponent={
|
||||
divider === true ? HR : (divider as any) || undefined
|
||||
}
|
||||
ListEmptyComponent={Empty}
|
||||
contentContainerStyle={{ gap, marginHorizontal: gap }}
|
||||
{...props}
|
||||
|
@ -149,7 +149,9 @@ export const BrowseSettings = ({
|
||||
{Object.keys(MediaTypeIcons).map((x) => (
|
||||
<Menu.Item
|
||||
key={x}
|
||||
label={t(`browse.mediatypekey.${x}`)}
|
||||
label={t(
|
||||
`browse.mediatypekey.${x as keyof typeof MediaTypeIcons}`,
|
||||
)}
|
||||
selected={mediaType === x}
|
||||
icon={MediaTypeIcons[x as keyof typeof MediaTypeIcons]}
|
||||
onSelect={() => setMediaType(x)}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useState } from "react";
|
||||
import { ItemGrid, ItemList, itemMap } from "~/components/items";
|
||||
import { Show } from "~/models";
|
||||
import { InfiniteFetch, type QueryIdentifier } from "~/query";
|
||||
@ -17,6 +16,7 @@ export const BrowsePage = () => {
|
||||
|
||||
return (
|
||||
<InfiniteFetch
|
||||
key={layout}
|
||||
query={BrowsePage.query(filter, sortBy, sortOrd)}
|
||||
layout={LayoutComponent.layout}
|
||||
Header={
|
||||
|
Loading…
x
Reference in New Issue
Block a user