From 78610d17a5506cec79a83124b1d95251207a8d39 Mon Sep 17 00:00:00 2001 From: florian <> Date: Sun, 2 Jun 2024 15:43:24 +0200 Subject: [PATCH] feat: Added upload progress --- src/pages/Transfer.tsx | 81 +++++++++++++++++++++++++++------- src/utils/useFileMetaEvents.ts | 5 +-- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/pages/Transfer.tsx b/src/pages/Transfer.tsx index 6b90512..c524a2a 100644 --- a/src/pages/Transfer.tsx +++ b/src/pages/Transfer.tsx @@ -8,13 +8,14 @@ import { import { ServerList } from '../components/ServerList/ServerList'; import { useServerInfo } from '../utils/useServerInfo'; import { useMemo, useState } from 'react'; -import { BlobDescriptor, BlossomClient } from 'blossom-client-sdk'; +import { BlobDescriptor, BlossomClient, SignedEvent } from 'blossom-client-sdk'; import { useNDK } from '../utils/ndk'; import { useQueryClient } from '@tanstack/react-query'; import { formatFileSize } from '../utils/utils'; import BlobList from '../components/BlobList/BlobList'; import './Transfer.css'; import { useNavigate, useParams } from 'react-router-dom'; +import axios, { AxiosProgressEvent } from 'axios'; import ProgressBar from '../components/ProgressBar/ProgressBar'; type TransferStatus = { @@ -23,6 +24,8 @@ type TransferStatus = { status: 'pending' | 'done' | 'error'; message?: string; size: number; + transferred?: number; + rate?: number; }; }; @@ -54,25 +57,71 @@ export const Transfer = () => { } return []; }, [serverInfo, transferSource, transferTarget]); - // https://github.com/sindresorhus/p-limit - // + + const uploadBlob = async ( + server: string, + file: File, + auth?: SignedEvent, + onUploadProgress?: (progressEvent: AxiosProgressEvent) => void + ) => { + const headers = { + Accept: 'application/json', + 'Content-Type': file.type, + }; + + const res = await axios.put(`${server}/upload`, file, { + headers: auth ? { ...headers, authorization: BlossomClient.encodeAuthorizationHeader(auth) } : headers, + onUploadProgress, + }); + + return res.data; + }; + const performTransfer = async (sourceServer: string, targetServer: string, blobs: BlobDescriptor[]) => { setTransferLog({}); setStarted(true); for (const b of blobs) { try { - // BlossomClient.getGetAuth() - setTransferLog(ts => ({ ...ts, [b.sha256]: { sha256: b.sha256, status: 'pending', size: b.size } })); + setTransferLog(ts => ({ + ...ts, + [b.sha256]: { sha256: b.sha256, status: 'pending', size: b.size, transferred: 0, rate: 0 }, + })); + + const data = await BlossomClient.getBlob(serverInfo[sourceServer].url, b.sha256).catch(e => { + if (e.response?.status === 404) { + setTransferLog(ts => ({ + ...ts, + [b.sha256]: { sha256: b.sha256, status: 'error', message: 'Blob not found (404)', size: b.size }, + })); + return null; + } + throw e; + }); + + if (!data) continue; - const data = await BlossomClient.getBlob(serverInfo[sourceServer].url, b.sha256); const file = new File([data], b.sha256, { type: b.type, lastModified: b.created }); const uploadAuth = await BlossomClient.getUploadAuth(file, signEventTemplate, 'Upload Blob'); - await BlossomClient.uploadBlob(serverInfo[targetServer].url, file, uploadAuth); - setTransferLog(ts => ({ ...ts, [b.sha256]: { sha256: b.sha256, status: 'done', size: b.size } })); + + await uploadBlob(serverInfo[targetServer].url, file, uploadAuth, progressEvent => { + setTransferLog(ts => ({ + ...ts, + [b.sha256]: { + ...ts[b.sha256], + transferred: progressEvent.loaded, + rate: progressEvent.rate || 0, + }, + })); + }); + + setTransferLog(ts => ({ + ...ts, + [b.sha256]: { sha256: b.sha256, status: 'done', size: b.size }, + })); } catch (e) { setTransferLog(ts => ({ ...ts, - [b.sha256]: { sha256: b.sha256, status: 'error', message: (e as Error).message, size: blobs.length }, + [b.sha256]: { sha256: b.sha256, status: 'error', message: (e as Error).message, size: b.size }, })); console.warn(e); } @@ -101,6 +150,8 @@ export const Transfer = () => { return { ...stats, fullSize: transferJobs?.reduce((acc, b) => acc + b.size, 0) || 0 }; }, [transferLog, transferJobs]); + const transferErrors = useMemo(() => Object.values(transferLog).filter(b => b.status == 'error'), [transferLog]); + return transferSource ? ( <> { } /> {
} -
- {Object.values(transferLog) - .filter(b => b.status == 'error') - .map(t => ( -
+ {transferErrors.length > 0 && ( +
+ {transferErrors.map(t => ( +
@@ -162,7 +212,8 @@ export const Transfer = () => { {t.message}
))} -
+
+ )}
{!started && } diff --git a/src/utils/useFileMetaEvents.ts b/src/utils/useFileMetaEvents.ts index 4c4c360..707461a 100644 --- a/src/utils/useFileMetaEvents.ts +++ b/src/utils/useFileMetaEvents.ts @@ -3,7 +3,7 @@ import useEvents from '../utils/useEvents'; import groupBy from 'lodash/groupBy'; import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'; import { useNDK } from '../utils/ndk'; -import { mapValues, uniq } from 'lodash'; +import { mapValues } from 'lodash'; export const KIND_FILE_META = 1063; export const KIND_BLOSSOM_DRIVE = 30563; @@ -12,7 +12,6 @@ export const KIND_VIDEO_HORIZONTAL = 34235; export const KIND_VIDEO_VERTICAL = 34236; export const KIND_AUDIO = 31337; - const blossomUrlRegex = /https?:\/\/(?:www\.)?[^\s/]+\/([a-fA-F0-9]{64})(?:\.[a-zA-Z0-9]+)?/g; function extractHashesFromContent(text: string) { @@ -58,7 +57,7 @@ const useFileMetaEventsByHash = () => { const groupedByX = groupBy(allXTags, item => item.x); return mapValues(groupedByX, v => v.map(e => e.ev)); }, [fileMetaSub]); - console.log(fileMetaEventsByHash); + console.log(fileMetaEventsByHash); return fileMetaEventsByHash; };