feat: indicator when copying to library + fix percentage

This commit is contained in:
Adrian Castro
2024-03-20 20:56:06 +01:00
parent 66344d552b
commit 315f1aaed1
2 changed files with 59 additions and 42 deletions

View File

@@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { TouchableOpacity } from "react-native-gesture-handler"; import { TouchableOpacity } from "react-native-gesture-handler";
import { Progress, Text, View } from "tamagui"; import { Progress, Spinner, Text, View } from "tamagui";
export interface DownloadItemProps { export interface DownloadItemProps {
id: string; id: string;
@@ -11,6 +11,7 @@ export interface DownloadItemProps {
downloaded: number; downloaded: number;
isFinished: boolean; isFinished: boolean;
onLongPress: (id: string) => void; onLongPress: (id: string) => void;
statusText?: string;
} }
const formatBytes = (bytes: number, decimals = 2) => { const formatBytes = (bytes: number, decimals = 2) => {
@@ -31,11 +32,37 @@ export const DownloadItem: React.FC<DownloadItemProps> = ({
downloaded, downloaded,
isFinished, isFinished,
onLongPress, onLongPress,
statusText,
}) => { }) => {
const percentage = progress * 100; const percentage = progress * 100;
const formattedFileSize = formatBytes(fileSize); const formattedFileSize = formatBytes(fileSize);
const formattedDownloaded = formatBytes(downloaded); const formattedDownloaded = formatBytes(downloaded);
const renderStatus = () => {
if (statusText) {
return (
<View style={{ flexDirection: "row", alignItems: "center" }}>
<Spinner size="small" color="$loadingIndicator" />
<Text fontSize={12} color="gray" style={{ marginLeft: 8 }}>
{statusText}
</Text>
</View>
);
} else if (isFinished) {
return (
<Text fontSize={12} color="gray">
Finished
</Text>
);
} else {
return (
<Text fontSize={12} color="gray">
{speed.toFixed(2)} MB/s
</Text>
);
}
};
return ( return (
<TouchableOpacity onLongPress={() => onLongPress(id)} activeOpacity={0.7}> <TouchableOpacity onLongPress={() => onLongPress(id)} activeOpacity={0.7}>
<View marginBottom={16} borderRadius={8} borderColor="white" padding={16}> <View marginBottom={16} borderRadius={8} borderColor="white" padding={16}>
@@ -59,17 +86,10 @@ export const DownloadItem: React.FC<DownloadItemProps> = ({
justifyContent="space-between" justifyContent="space-between"
> >
<Text fontSize={12} color="gray"> <Text fontSize={12} color="gray">
{percentage}% - {formattedDownloaded} of {formattedFileSize} {percentage.toFixed()}% - {formattedDownloaded} of{" "}
{formattedFileSize}
</Text> </Text>
{isFinished ? ( {renderStatus()}
<Text fontSize={12} color="gray">
Finished
</Text>
) : (
<Text fontSize={12} color="gray">
{speed.toFixed(2)} MB/s
</Text>
)}
</View> </View>
</View> </View>
</TouchableOpacity> </TouchableOpacity>

View File

@@ -15,6 +15,7 @@ export interface DownloadItem {
url: string; url: string;
type: "mp4" | "hls"; type: "mp4" | "hls";
isFinished: boolean; isFinished: boolean;
statusText?: string;
} }
interface DownloadManagerContextType { interface DownloadManagerContextType {
@@ -73,13 +74,21 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({
setDownloads((currentDownloads) => [newDownload, ...currentDownloads]); setDownloads((currentDownloads) => [newDownload, ...currentDownloads]);
if (type === "mp4") { if (type === "mp4") {
await downloadMP4(url); await downloadMP4(url, newDownload.id);
} else if (type === "hls") { } else if (type === "hls") {
// HLS stuff later // HLS stuff later
} }
}; };
const downloadMP4 = async (url: string) => { const updateDownloadItem = (id: string, updates: Partial<DownloadItem>) => {
setDownloads((currentDownloads) =>
currentDownloads.map((download) =>
download.id === id ? { ...download, ...updates } : download,
),
);
};
const downloadMP4 = async (url: string, downloadId: string) => {
let lastBytesWritten = 0; let lastBytesWritten = 0;
let lastTimestamp = Date.now(); let lastTimestamp = Date.now();
@@ -92,22 +101,15 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({
const bytesWritten = downloadProgress.totalBytesWritten; const bytesWritten = downloadProgress.totalBytesWritten;
const newBytes = bytesWritten - lastBytesWritten; const newBytes = bytesWritten - lastBytesWritten;
const speed = newBytes / timeElapsed / 1024; const speed = newBytes / timeElapsed / 1024;
const progress = const progress =
bytesWritten / downloadProgress.totalBytesExpectedToWrite; bytesWritten / downloadProgress.totalBytesExpectedToWrite;
setDownloads((currentDownloads) => updateDownloadItem(downloadId, {
currentDownloads.map((item) =>
item.url === url
? {
...item,
progress, progress,
speed, speed,
fileSize: downloadProgress.totalBytesExpectedToWrite, fileSize: downloadProgress.totalBytesExpectedToWrite,
} downloaded: bytesWritten,
: item, });
),
);
lastBytesWritten = bytesWritten; lastBytesWritten = bytesWritten;
lastTimestamp = currentTime; lastTimestamp = currentTime;
@@ -132,29 +134,20 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({
const result = await downloadResumable.downloadAsync(); const result = await downloadResumable.downloadAsync();
if (result) { if (result) {
console.log("Finished downloading to ", result.uri); console.log("Finished downloading to ", result.uri);
await saveFileToMediaLibraryAndDeleteOriginal(result.uri); await saveFileToMediaLibraryAndDeleteOriginal(result.uri, downloadId);
setDownloads((currentDownloads) =>
currentDownloads.map((item) =>
item.url === url
? {
...item,
progress: 1,
speed: 0,
downloaded: item.fileSize,
isFinished: true,
}
: item,
),
);
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
}; };
const saveFileToMediaLibraryAndDeleteOriginal = async (fileUri: string) => { const saveFileToMediaLibraryAndDeleteOriginal = async (
fileUri: string,
downloadId: string,
) => {
try { try {
updateDownloadItem(downloadId, { statusText: "Importing" });
const { status } = await MediaLibrary.requestPermissionsAsync(); const { status } = await MediaLibrary.requestPermissionsAsync();
if (status !== MediaLibrary.PermissionStatus.GRANTED) { if (status !== MediaLibrary.PermissionStatus.GRANTED) {
throw new Error("MediaLibrary permission not granted"); throw new Error("MediaLibrary permission not granted");
@@ -163,6 +156,10 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({
await MediaLibrary.saveToLibraryAsync(fileUri); await MediaLibrary.saveToLibraryAsync(fileUri);
await FileSystem.deleteAsync(fileUri); await FileSystem.deleteAsync(fileUri);
updateDownloadItem(downloadId, {
statusText: undefined,
isFinished: true,
});
console.log("File saved to media library and original deleted"); console.log("File saved to media library and original deleted");
} catch (error) { } catch (error) {
console.error("Error saving file to media library:", error); console.error("Error saving file to media library:", error);