/* * 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 . */ import type { KyooImage } from "@kyoo/models"; import { Alert, H6, IconButton, ImageBackground, Menu, P, PressableFeedback, SubP, focusReset, ts, usePageStyle, } from "@kyoo/primitives"; import DownloadForOffline from "@material-symbols/svg-400/rounded/download_for_offline.svg"; import Downloading from "@material-symbols/svg-400/rounded/downloading.svg"; import ErrorIcon from "@material-symbols/svg-400/rounded/error.svg"; import NotStarted from "@material-symbols/svg-400/rounded/not_started.svg"; import { FlashList } from "@shopify/flash-list"; import { useRouter } from "expo-router"; import { type Atom, useAtomValue } from "jotai"; import { useTranslation } from "react-i18next"; import { View } from "react-native"; import { percent, useYoshiki } from "yoshiki/native"; import { EpisodeLine, displayRuntime, episodeDisplayNumber } from "../details/episode"; import { EmptyView } from "../fetch"; import { type State, downloadAtom } from "./state"; const DownloadedItem = ({ name, statusAtom, runtime, kind, image, ...props }: { name: string; statusAtom: Atom; runtime: number | null; kind: "episode" | "movie"; image: KyooImage | null; }) => { const { css } = useYoshiki(); const { t } = useTranslation(); const router = useRouter(); const { error, status, progress, pause, resume, remove, play, retry } = useAtomValue(statusAtom); return ( play?.(router)} {...css( { alignItems: "center", flexDirection: "row", fover: { self: focusReset, title: { textDecorationLine: "underline", }, }, }, props, )} > {/* {(watchedPercent || watchedStatus === WatchStatusV.Completed) && ( */} {/* <> */} {/* theme.overlay0, */} {/* width: percent(100), */} {/* height: ts(0.5), */} {/* position: "absolute", */} {/* bottom: 0, */} {/* })} */} {/* /> */} {/* theme.accent, */} {/* width: percent(watchedPercent ?? 100), */} {/* height: ts(0.5), */} {/* position: "absolute", */} {/* bottom: 0, */} {/* })} */} {/* /> */} {/* */} {/* )} */} {/* biome-ignore lint/a11y/useValidAriaValues: use h6 for style only */}
{name ?? t("show.episodeNoMetadata")}
{status === "FAILED" &&

{t("downloads.error", { error: error ?? "Unknow error" })}

} {runtime && status === "DONE" && {displayRuntime(runtime)}} {progress !== 100 && progress !== null && ( {Math.round(progress)}% theme.user.overlay0, })} > theme.user.accent, width: percent(progress), height: percent(100), })} /> )} {status === "FAILED" && ( retry?.()} /> )} {status === "DOWNLOADING" && ( pause?.()} /> )} {status === "PAUSED" && ( resume?.()} /> )} { Alert.alert( t("downloads.delete"), t("downloads.deleteMessage"), [ { text: t("misc.cancel"), style: "cancel" }, { text: t("misc.delete"), onPress: remove, style: "destructive" }, ], { icon: "error", }, ); }} />
); }; const downloadIcon = (status: State["status"]) => { switch (status) { case "DONE": return DownloadForOffline; case "DOWNLOADING": return Downloading; case "FAILED": return ErrorIcon; case "PENDING": case "PAUSED": case "STOPPED": return NotStarted; } }; export const DownloadPage = () => { const pageStyle = usePageStyle(); const downloads = useAtomValue(downloadAtom); const { t } = useTranslation(); if (downloads.length === 0) return ; return ( item.data.kind} renderItem={({ item }) => ( )} estimatedItemSize={EpisodeLine.layout.size} keyExtractor={(x) => x.data.id} numColumns={1} contentContainerStyle={pageStyle} /> ); };