From 315f1aaed1557f750c08adb8fa60921f560e0203 Mon Sep 17 00:00:00 2001 From: Adrian Castro <22133246+castdrian@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:56:06 +0100 Subject: [PATCH] feat: indicator when copying to library + fix percentage --- apps/expo/src/components/DownloadItem.tsx | 42 +++++++++---- .../expo/src/hooks/DownloadManagerContext.tsx | 59 +++++++++---------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/apps/expo/src/components/DownloadItem.tsx b/apps/expo/src/components/DownloadItem.tsx index 4b25e4c..93da300 100644 --- a/apps/expo/src/components/DownloadItem.tsx +++ b/apps/expo/src/components/DownloadItem.tsx @@ -1,6 +1,6 @@ import React from "react"; import { TouchableOpacity } from "react-native-gesture-handler"; -import { Progress, Text, View } from "tamagui"; +import { Progress, Spinner, Text, View } from "tamagui"; export interface DownloadItemProps { id: string; @@ -11,6 +11,7 @@ export interface DownloadItemProps { downloaded: number; isFinished: boolean; onLongPress: (id: string) => void; + statusText?: string; } const formatBytes = (bytes: number, decimals = 2) => { @@ -31,11 +32,37 @@ export const DownloadItem: React.FC = ({ downloaded, isFinished, onLongPress, + statusText, }) => { const percentage = progress * 100; const formattedFileSize = formatBytes(fileSize); const formattedDownloaded = formatBytes(downloaded); + const renderStatus = () => { + if (statusText) { + return ( + + + + {statusText} + + + ); + } else if (isFinished) { + return ( + + Finished + + ); + } else { + return ( + + {speed.toFixed(2)} MB/s + + ); + } + }; + return ( onLongPress(id)} activeOpacity={0.7}> @@ -59,17 +86,10 @@ export const DownloadItem: React.FC = ({ justifyContent="space-between" > - {percentage}% - {formattedDownloaded} of {formattedFileSize} + {percentage.toFixed()}% - {formattedDownloaded} of{" "} + {formattedFileSize} - {isFinished ? ( - - Finished - - ) : ( - - {speed.toFixed(2)} MB/s - - )} + {renderStatus()} diff --git a/apps/expo/src/hooks/DownloadManagerContext.tsx b/apps/expo/src/hooks/DownloadManagerContext.tsx index 23d8a77..b33457a 100644 --- a/apps/expo/src/hooks/DownloadManagerContext.tsx +++ b/apps/expo/src/hooks/DownloadManagerContext.tsx @@ -15,6 +15,7 @@ export interface DownloadItem { url: string; type: "mp4" | "hls"; isFinished: boolean; + statusText?: string; } interface DownloadManagerContextType { @@ -73,13 +74,21 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({ setDownloads((currentDownloads) => [newDownload, ...currentDownloads]); if (type === "mp4") { - await downloadMP4(url); + await downloadMP4(url, newDownload.id); } else if (type === "hls") { // HLS stuff later } }; - const downloadMP4 = async (url: string) => { + const updateDownloadItem = (id: string, updates: Partial) => { + setDownloads((currentDownloads) => + currentDownloads.map((download) => + download.id === id ? { ...download, ...updates } : download, + ), + ); + }; + + const downloadMP4 = async (url: string, downloadId: string) => { let lastBytesWritten = 0; let lastTimestamp = Date.now(); @@ -92,22 +101,15 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({ const bytesWritten = downloadProgress.totalBytesWritten; const newBytes = bytesWritten - lastBytesWritten; const speed = newBytes / timeElapsed / 1024; - const progress = bytesWritten / downloadProgress.totalBytesExpectedToWrite; - setDownloads((currentDownloads) => - currentDownloads.map((item) => - item.url === url - ? { - ...item, - progress, - speed, - fileSize: downloadProgress.totalBytesExpectedToWrite, - } - : item, - ), - ); + updateDownloadItem(downloadId, { + progress, + speed, + fileSize: downloadProgress.totalBytesExpectedToWrite, + downloaded: bytesWritten, + }); lastBytesWritten = bytesWritten; lastTimestamp = currentTime; @@ -132,29 +134,20 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({ const result = await downloadResumable.downloadAsync(); if (result) { console.log("Finished downloading to ", result.uri); - await saveFileToMediaLibraryAndDeleteOriginal(result.uri); - - setDownloads((currentDownloads) => - currentDownloads.map((item) => - item.url === url - ? { - ...item, - progress: 1, - speed: 0, - downloaded: item.fileSize, - isFinished: true, - } - : item, - ), - ); + await saveFileToMediaLibraryAndDeleteOriginal(result.uri, downloadId); } } catch (e) { console.error(e); } }; - const saveFileToMediaLibraryAndDeleteOriginal = async (fileUri: string) => { + const saveFileToMediaLibraryAndDeleteOriginal = async ( + fileUri: string, + downloadId: string, + ) => { try { + updateDownloadItem(downloadId, { statusText: "Importing" }); + const { status } = await MediaLibrary.requestPermissionsAsync(); if (status !== MediaLibrary.PermissionStatus.GRANTED) { throw new Error("MediaLibrary permission not granted"); @@ -163,6 +156,10 @@ export const DownloadManagerProvider: React.FC<{ children: ReactNode }> = ({ await MediaLibrary.saveToLibraryAsync(fileUri); await FileSystem.deleteAsync(fileUri); + updateDownloadItem(downloadId, { + statusText: undefined, + isFinished: true, + }); console.log("File saved to media library and original deleted"); } catch (error) { console.error("Error saving file to media library:", error);