2022-11-04 01:08:03 +09:00

104 lines
2.8 KiB
TypeScript

/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { useState } from "react";
import {
View,
Image as Img,
ImageSourcePropType,
} from "react-native";
import type { Property } from "csstype";
type ImageOptions = {
radius?: string;
fallback?: string | ImageSourcePropType;
};
type ImageProps = {
src?: string | ImageSourcePropType | null;
alt?: string;
} & ImageOptions;
type ImagePropsWithLoading =
| (ImageProps & { loading?: boolean })
| (Partial<ImageProps> & { loading: true });
type Width = Property.Width<(string & {}) | 0>;
type Height = Property.Height<(string & {}) | 0>;
export const Image = ({
src,
alt,
radius,
fallback,
loading = false,
aspectRatio = undefined,
width = undefined,
height = undefined,
...others
}: ImagePropsWithLoading &
(
| { aspectRatio?: string; width: Width; height: Height }
| { aspectRatio: string; width?: Width; height?: Height }
)) => {
const [showLoading, setLoading] = useState<boolean>(loading);
const [source, setSource] = useState(src);
/* const imgRef = useRef<Img>(null); */
// This allow the loading bool to be false with SSR but still be on client-side
/* useLayoutEffect(() => { */
/* if (!imgRef.current?.complete && src) setLoading(true); */
/* if (!src && !loading) setLoading(false); */
/* }, [src, loading]); */
return (
<View
css={{
aspectRatio,
width,
height,
/* backgroundColor: "grey.300", */
borderRadius: radius,
overflow: "hidden",
"& > *": { width: "100%", height: "100%" },
}}
{...others}
>
{/* {showLoading && <Skeleton variant="rectangular" height="100%" />} */}
{!loading && source && (
<Img
source={typeof source === "string" ? { uri: source } : source}
accessibilityLabel={alt}
onLoad={() => setLoading(false)}
onError={() => {
if (fallback) setSource(fallback);
else setLoading(false);
}}
css={{ objectFit: "cover", display: showLoading ? "hidden" : undefined }}
/>
)}
</View>
);
};
export const Poster = (props: ImagePropsWithLoading & { width?: Width; height?: Height }) => (
<Image aspectRatio="2 / 3" {...props} />
);