diff --git a/front/apps/mobile/app/(app)/(tabs)/_layout.tsx b/front/apps/mobile/app/(app)/(tabs)/_layout.tsx index 641fdf67..814bb0aa 100644 --- a/front/apps/mobile/app/(app)/(tabs)/_layout.tsx +++ b/front/apps/mobile/app/(app)/(tabs)/_layout.tsx @@ -22,6 +22,7 @@ import { Icon } from "@kyoo/primitives"; import { Tabs } from "expo-router"; import Home from "@material-symbols/svg-400/rounded/home-fill.svg"; import Browse from "@material-symbols/svg-400/rounded/browse-fill.svg"; +import Downloading from "@material-symbols/svg-400/rounded/downloading-fill.svg"; export default function TabsLayout() { return ( @@ -44,6 +45,13 @@ export default function TabsLayout() { tabBarIcon: ({ color, size }) => , }} /> + , + }} + /> ); } diff --git a/front/apps/mobile/app/(app)/(tabs)/downloads.tsx b/front/apps/mobile/app/(app)/(tabs)/downloads.tsx new file mode 100644 index 00000000..dfb44d79 --- /dev/null +++ b/front/apps/mobile/app/(app)/(tabs)/downloads.tsx @@ -0,0 +1,23 @@ +/* + * 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 { DownloadPage } from "@kyoo/ui"; + +export default DownloadPage; diff --git a/front/apps/mobile/app/_layout.tsx b/front/apps/mobile/app/_layout.tsx index 29a177d9..bb140bc5 100644 --- a/front/apps/mobile/app/_layout.tsx +++ b/front/apps/mobile/app/_layout.tsx @@ -48,6 +48,7 @@ import "@formatjs/intl-displaynames/locale-data/fr"; import en from "../../../translations/en.json"; import fr from "../../../translations/fr.json"; import { useTheme } from "yoshiki/native"; +import { DownloadProvider } from "@kyoo/ui"; i18next.use(initReactI18next).init({ interpolation: { @@ -103,9 +104,11 @@ export default function Root() { > - - - + + + + + diff --git a/front/packages/primitives/src/text.tsx b/front/packages/primitives/src/text.tsx index ee339f82..405e795b 100644 --- a/front/packages/primitives/src/text.tsx +++ b/front/packages/primitives/src/text.tsx @@ -52,6 +52,7 @@ const styleText = ( { marginVertical: rem(0.5), color: type === "header" ? theme.heading : theme.paragraph, + flexShrink: 1, fontSize: rem(1), fontFamily: theme.font.normal, }, diff --git a/front/packages/ui/src/components/context-menus.tsx b/front/packages/ui/src/components/context-menus.tsx index 8eaab777..1aef55e5 100644 --- a/front/packages/ui/src/components/context-menus.tsx +++ b/front/packages/ui/src/components/context-menus.tsx @@ -27,7 +27,7 @@ import Download from "@material-symbols/svg-400/rounded/download.svg"; import { WatchStatusV, queryFn, useAccount } from "@kyoo/models"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { watchListIcon } from "./watchlist-info"; -import { downloadFile } from "../downloads/download"; +import { downloadFile } from "../downloads/state"; export const EpisodesContext = ({ type = "episode", diff --git a/front/packages/ui/src/downloads/index.tsx b/front/packages/ui/src/downloads/index.tsx new file mode 100644 index 00000000..39485cc1 --- /dev/null +++ b/front/packages/ui/src/downloads/index.tsx @@ -0,0 +1,22 @@ +/* + * 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 . + */ + +export { DownloadPage } from "./page"; +export { DownloadProvider } from "./state"; diff --git a/front/packages/ui/src/downloads/page.tsx b/front/packages/ui/src/downloads/page.tsx new file mode 100644 index 00000000..8cd2a6cc --- /dev/null +++ b/front/packages/ui/src/downloads/page.tsx @@ -0,0 +1,39 @@ +/* + * 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 { useAtomValue } from "jotai"; +import { downloadAtom } from "./state"; +import { FlashList } from "@shopify/flash-list"; +import { View } from "react-native"; + +export const DownloadPage = () => { + const downloads = useAtomValue(downloadAtom); + + return ( + { + {item.data.name}; + }} + keyExtractor={(x) => x.data.id} + numColumns={1} + /> + ); +}; diff --git a/front/packages/ui/src/downloads/download.tsx b/front/packages/ui/src/downloads/state.tsx similarity index 91% rename from front/packages/ui/src/downloads/download.tsx rename to front/packages/ui/src/downloads/state.tsx index 1b24e2ad..f7641769 100644 --- a/front/packages/ui/src/downloads/download.tsx +++ b/front/packages/ui/src/downloads/state.tsx @@ -18,13 +18,8 @@ * along with Kyoo. If not, see . */ -import { - download, - completeHandler, - directories, - DownloadTask, - checkForExistingDownloads, - ensureDownloadsAreRunning, +import RNBackgroundDownloader, { + type DownloadTask, } from "@kesha-antonov/react-native-background-downloader"; import { deleteAsync } from "expo-file-system"; import { @@ -39,7 +34,7 @@ import { import { Player } from "../player"; import { atom, useSetAtom, PrimitiveAtom, useStore } from "jotai"; import { getCurrentAccount, storage } from "@kyoo/models/src/account-internal"; -import { useEffect } from "react"; +import { ReactNode, useEffect } from "react"; type State = { status: "DOWNLOADING" | "PAUSED" | "DONE" | "FAILED" | "STOPPED"; @@ -53,7 +48,7 @@ type State = { play: () => void; }; -const downloadAtom = atom< +export const downloadAtom = atom< { data: Episode | Movie; info: WatchInfo; @@ -116,7 +111,7 @@ const setupDownloadTask = ( update((x) => ({ ...x, percent: 100, status: "DONE" })); // apparently this is needed for ios /shrug i'm totaly gona forget this // if i ever implement ios so keeping this here - completeHandler(task.id); + RNBackgroundDownloader.completeHandler(task.id); }) .error((error) => update((x) => ({ ...x, status: "FAILED", error }))); @@ -158,8 +153,8 @@ export const useDownloader = () => { ]); // TODO: support custom paths - const path = `${directories.documents}/${slug}-${data.id}.${info.extension}`; - const task = download({ + const path = `${RNBackgroundDownloader.directories.documents}/${slug}-${data.id}.${info.extension}`; + const task = RNBackgroundDownloader.download({ id: data.id, // TODO: support variant qualities url: `${account.apiUrl}/api/video/${type}/${slug}/direct`, @@ -175,14 +170,14 @@ export const useDownloader = () => { }; }; -export const DownloadProvider = () => { +export const DownloadProvider = ({ children }: { children: ReactNode }) => { const store = useStore(); useEffect(() => { async function run() { if (store.get(downloadAtom).length) return; - const tasks = await checkForExistingDownloads(); + const tasks = await RNBackgroundDownloader.checkForExistingDownloads(); const dls: { data: Episode | Movie; info: WatchInfo; path: string; state: State }[] = JSON.parse(storage.getString("downloads") ?? "[]"); const downloads = dls.map((dl) => { @@ -214,8 +209,10 @@ export const DownloadProvider = () => { for (const t of tasks) { if (!downloads.find((x) => x.data.id === t.id)) t.stop(); } - ensureDownloadsAreRunning(); + RNBackgroundDownloader.ensureDownloadsAreRunning(); } run(); }, [store]); + + return children; }; diff --git a/front/packages/ui/src/downloads/download.web.tsx b/front/packages/ui/src/downloads/state.web.tsx similarity index 100% rename from front/packages/ui/src/downloads/download.web.tsx rename to front/packages/ui/src/downloads/state.web.tsx diff --git a/front/packages/ui/src/index.ts b/front/packages/ui/src/index.ts index 91a77a6e..f4053f5f 100644 --- a/front/packages/ui/src/index.ts +++ b/front/packages/ui/src/index.ts @@ -26,3 +26,4 @@ export { CollectionPage } from "./collection"; export { Player } from "./player"; export { SearchPage } from "./search"; export { LoginPage, RegisterPage } from "./login"; +export { DownloadPage, DownloadProvider } from "./downloads";