Add a downloads tab

This commit is contained in:
Zoe Roux 2023-12-18 12:47:55 +01:00
parent f1cc396bfc
commit 71d29a303f
10 changed files with 113 additions and 19 deletions

View File

@ -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 }) => <Icon icon={Browse} color={color} size={size} />,
}}
/>
<Tabs.Screen
name="downloads"
options={{
tabBarLabel: "Downloads",
tabBarIcon: ({ color, size }) => <Icon icon={Downloading} color={color} size={size} />,
}}
/>
</Tabs>
);
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
import { DownloadPage } from "@kyoo/ui";
export default DownloadPage;

View File

@ -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() {
>
<PortalProvider>
<AccountProvider>
<NavigationThemeProvider>
<Slot />
</NavigationThemeProvider>
<DownloadProvider>
<NavigationThemeProvider>
<Slot />
</NavigationThemeProvider>
</DownloadProvider>
</AccountProvider>
</PortalProvider>
</ThemeSelector>

View File

@ -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,
},

View File

@ -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",

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
export { DownloadPage } from "./page";
export { DownloadProvider } from "./state";

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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 (
<FlashList
data={downloads}
renderItem={({ item }) => {
<View>{item.data.name}</View>;
}}
keyExtractor={(x) => x.data.id}
numColumns={1}
/>
);
};

View File

@ -18,13 +18,8 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
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;
};

View File

@ -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";