mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Use expo image on mobile
This commit is contained in:
parent
18f7bda090
commit
f0ae541eb2
@ -33,6 +33,7 @@
|
|||||||
"expo-dev-client": "~5.0.0-preview.4",
|
"expo-dev-client": "~5.0.0-preview.4",
|
||||||
"expo-file-system": "~18.0.0",
|
"expo-file-system": "~18.0.0",
|
||||||
"expo-font": "~13.0.0",
|
"expo-font": "~13.0.0",
|
||||||
|
"expo-image": "^1.13.0",
|
||||||
"expo-image-picker": "~16.0.0",
|
"expo-image-picker": "~16.0.0",
|
||||||
"expo-linear-gradient": "~14.0.1",
|
"expo-linear-gradient": "~14.0.1",
|
||||||
"expo-linking": "~7.0.2",
|
"expo-linking": "~7.0.2",
|
||||||
@ -49,8 +50,6 @@
|
|||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-i18next": "^15.1.0",
|
"react-i18next": "^15.1.0",
|
||||||
"react-native": "0.76.1",
|
"react-native": "0.76.1",
|
||||||
"react-native-blurhash": "^2.0.3",
|
|
||||||
"react-native-fast-image": "^8.6.3",
|
|
||||||
"react-native-mmkv": "^3.1.0",
|
"react-native-mmkv": "^3.1.0",
|
||||||
"react-native-reanimated": "~3.16.1",
|
"react-native-reanimated": "~3.16.1",
|
||||||
"react-native-safe-area-context": "4.12.0",
|
"react-native-safe-area-context": "4.12.0",
|
||||||
|
BIN
front/bun.lockb
BIN
front/bun.lockb
Binary file not shown.
@ -17,8 +17,6 @@
|
|||||||
"moti": "*",
|
"moti": "*",
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*",
|
"react-native": "*",
|
||||||
"react-native-blurhash": "*",
|
|
||||||
"react-native-fast-image": "*",
|
|
||||||
"react-native-reanimated": "*",
|
"react-native-reanimated": "*",
|
||||||
"react-native-safe-area-context": "*",
|
"react-native-safe-area-context": "*",
|
||||||
"react-native-svg": "*",
|
"react-native-svg": "*",
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { KyooImage } from "@kyoo/models";
|
import type { KyooImage } from "@kyoo/models";
|
||||||
import type { ReactElement } from "react";
|
|
||||||
import type { ImageStyle } from "react-native";
|
import type { ImageStyle } from "react-native";
|
||||||
import type { YoshikiStyle } from "yoshiki/src/type";
|
import type { YoshikiStyle } from "yoshiki/src/type";
|
||||||
|
|
||||||
@ -33,7 +32,6 @@ export type Props = {
|
|||||||
src?: KyooImage | null;
|
src?: KyooImage | null;
|
||||||
quality: "low" | "medium" | "high";
|
quality: "low" | "medium" | "high";
|
||||||
alt?: string;
|
alt?: string;
|
||||||
Err?: ReactElement | null;
|
|
||||||
forcedLoading?: boolean;
|
forcedLoading?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,12 +18,10 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getCurrentToken } from "@kyoo/models";
|
import { type ReactElement } from "react";
|
||||||
import { type ReactElement, useState } from "react";
|
import { type ImageStyle, View, type ViewStyle } from "react-native";
|
||||||
import { type FlexStyle, type ImageStyle, View, type ViewStyle } from "react-native";
|
import { Image as ExpoImage } from "expo-image";
|
||||||
import { Blurhash } from "react-native-blurhash";
|
import { useYoshiki } from "yoshiki/native";
|
||||||
import FastImage from "react-native-fast-image";
|
|
||||||
import { percent, useYoshiki } from "yoshiki/native";
|
|
||||||
import { Skeleton } from "../skeleton";
|
import { Skeleton } from "../skeleton";
|
||||||
import type { ImageLayout, Props } from "./base-image";
|
import type { ImageLayout, Props } from "./base-image";
|
||||||
|
|
||||||
@ -33,64 +31,24 @@ export const Image = ({
|
|||||||
alt,
|
alt,
|
||||||
forcedLoading = false,
|
forcedLoading = false,
|
||||||
layout,
|
layout,
|
||||||
Err,
|
|
||||||
...props
|
...props
|
||||||
}: Props & { style?: ImageStyle } & { layout: ImageLayout }) => {
|
}: Props & { style?: ImageStyle } & { layout: ImageLayout }) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
const [state, setState] = useState<"loading" | "errored" | "finished">(
|
|
||||||
src ? "loading" : "errored",
|
|
||||||
);
|
|
||||||
|
|
||||||
// This could be done with a key but this makes the API easier to use.
|
|
||||||
// This unsures that the state is resetted when the source change (useful for recycler lists.)
|
|
||||||
const [oldSource, setOldSource] = useState(src);
|
|
||||||
if (oldSource !== src) {
|
|
||||||
setState("loading");
|
|
||||||
setOldSource(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle;
|
const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle;
|
||||||
|
|
||||||
if (forcedLoading) return <Skeleton variant="custom" {...css([layout, border], props)} />;
|
if (forcedLoading) return <Skeleton variant="custom" {...css([layout, border], props)} />;
|
||||||
if (!src || state === "errored") {
|
if (!src) return <View {...css([{ bg: (theme) => theme.overlay0 }, layout, border], props)} />;
|
||||||
return Err !== undefined ? (
|
|
||||||
Err
|
|
||||||
) : (
|
|
||||||
<View {...css([{ bg: (theme) => theme.overlay0 }, layout, border], props)} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
quality ??= "high";
|
|
||||||
const token = getCurrentToken();
|
|
||||||
return (
|
return (
|
||||||
<View {...css([layout, border], props)}>
|
<ExpoImage
|
||||||
{state !== "finished" && (
|
alt={alt}
|
||||||
<Blurhash
|
contentFit="cover"
|
||||||
blurhash={src.blurhash}
|
placeholderContentFit="cover"
|
||||||
resizeMode="cover"
|
placeholder={{ blurhash: src.blurhash }}
|
||||||
{...css({ width: percent(100), height: percent(100) })}
|
source={src[quality ?? "high"]}
|
||||||
|
recyclingKey={src.high}
|
||||||
|
{...css([layout, border])}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<FastImage
|
|
||||||
source={{
|
|
||||||
uri: src[quality],
|
|
||||||
headers: token
|
|
||||||
? {
|
|
||||||
Authorization: token,
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
priority: FastImage.priority[quality === "medium" ? "normal" : quality],
|
|
||||||
}}
|
|
||||||
accessibilityLabel={alt}
|
|
||||||
onLoad={() => setState("finished")}
|
|
||||||
onError={() => setState("errored")}
|
|
||||||
resizeMode={FastImage.resizeMode.cover}
|
|
||||||
{...(css({
|
|
||||||
width: percent(100),
|
|
||||||
height: percent(100),
|
|
||||||
}) as { style: FlexStyle })}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,14 +18,13 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import NextImage from "next/image";
|
|
||||||
import { type ReactElement, useState } from "react";
|
import { type ReactElement, useState } from "react";
|
||||||
import { type ImageStyle, View, type ViewStyle } from "react-native";
|
import { type ImageStyle, View, type ViewStyle } from "react-native";
|
||||||
import { useYoshiki } from "yoshiki/native";
|
import { useYoshiki } from "yoshiki/native";
|
||||||
import { imageBorderRadius } from "../constants";
|
import { imageBorderRadius } from "../constants";
|
||||||
import { Skeleton } from "../skeleton";
|
import { Skeleton } from "../skeleton";
|
||||||
import type { ImageLayout, Props } from "./base-image";
|
import type { ImageLayout, Props } from "./base-image";
|
||||||
import { BlurhashContainer, useRenderType } from "./blurhash.web";
|
import { BlurhashContainer } from "./blurhash.web";
|
||||||
|
|
||||||
export const Image = ({
|
export const Image = ({
|
||||||
src,
|
src,
|
||||||
@ -33,7 +32,6 @@ export const Image = ({
|
|||||||
alt,
|
alt,
|
||||||
forcedLoading = false,
|
forcedLoading = false,
|
||||||
layout,
|
layout,
|
||||||
Err,
|
|
||||||
...props
|
...props
|
||||||
}: Props & { style?: ImageStyle } & { layout: ImageLayout }) => {
|
}: Props & { style?: ImageStyle } & { layout: ImageLayout }) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
@ -45,11 +43,7 @@ export const Image = ({
|
|||||||
|
|
||||||
if (forcedLoading) return <Skeleton variant="custom" {...css([layout, border], props)} />;
|
if (forcedLoading) return <Skeleton variant="custom" {...css([layout, border], props)} />;
|
||||||
if (!src || state === "errored") {
|
if (!src || state === "errored") {
|
||||||
return Err !== undefined ? (
|
return <View {...css([{ bg: (theme) => theme.overlay0 }, layout, border], props)} />;
|
||||||
Err
|
|
||||||
) : (
|
|
||||||
<View {...css([{ bg: (theme) => theme.overlay0 }, layout, border], props)} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -18,18 +18,14 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Image, View } from "react-native";
|
import { Image as ExpoImage } from "expo-image";
|
||||||
|
|
||||||
export const Sprite = ({
|
export const Sprite = ({
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
width,
|
style,
|
||||||
height,
|
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
rows,
|
|
||||||
columns,
|
|
||||||
style,
|
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
src: string;
|
src: string;
|
||||||
@ -43,15 +39,17 @@ export const Sprite = ({
|
|||||||
style?: object;
|
style?: object;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<View style={{ width, height, overflow: "hidden", flexGrow: 0, flexShrink: 0 }}>
|
<ExpoImage
|
||||||
<Image
|
source={src}
|
||||||
source={{ uri: src }}
|
|
||||||
alt={alt}
|
alt={alt}
|
||||||
width={width * columns}
|
contentFit="none"
|
||||||
height={height * rows}
|
contentPosition={{left: -x, top: -y}}
|
||||||
style={{ transform: [{ translateX: -x }, { translateY: -y }] }}
|
style={{
|
||||||
|
flexGrow: 0,
|
||||||
|
flexShrink: 0,
|
||||||
|
...style
|
||||||
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user