diff --git a/index.html b/index.html index abab362..856944f 100644 --- a/index.html +++ b/index.html @@ -66,13 +66,7 @@ const file = input.files[0]; console.debug(file); - const r_nip96 = document.querySelector("#method-nip96").checked; - const r_blossom = document.querySelector("#method-blossom").checked; - if (r_nip96) { - await uploadFilesNip96(file) - } else if (r_blossom) { - await uploadBlossom(file); - } + await uploadBlossom(file); } catch (ex) { if (ex instanceof Error) { alert(ex.message); @@ -147,16 +141,7 @@ Welcome to route96
-
- - -
+
You must have a nostr extension for this to work
@@ -164,7 +149,7 @@
diff --git a/ui_src/src/upload/index.ts b/ui_src/src/upload/index.ts index 538fe52..45d2585 100644 --- a/ui_src/src/upload/index.ts +++ b/ui_src/src/upload/index.ts @@ -3,6 +3,7 @@ export async function openFile(): Promise { const elm = document.createElement("input"); let lock = false; elm.type = "file"; + elm.multiple = true; // Allow multiple file selection const handleInput = (e: Event) => { lock = true; const elm = e.target as HTMLInputElement; @@ -28,3 +29,35 @@ export async function openFile(): Promise { ); }); } + +export async function openFiles(): Promise { + return new Promise((resolve) => { + const elm = document.createElement("input"); + let lock = false; + elm.type = "file"; + elm.multiple = true; + const handleInput = (e: Event) => { + lock = true; + const elm = e.target as HTMLInputElement; + if ((elm.files?.length ?? 0) > 0) { + resolve(elm.files!); + } else { + resolve(undefined); + } + }; + + elm.onchange = (e) => handleInput(e); + elm.click(); + window.addEventListener( + "focus", + () => { + setTimeout(() => { + if (!lock) { + resolve(undefined); + } + }, 300); + }, + { once: true }, + ); + }); +} diff --git a/ui_src/src/views/upload.tsx b/ui_src/src/views/upload.tsx index eaadc8c..7bff4ca 100644 --- a/ui_src/src/views/upload.tsx +++ b/ui_src/src/views/upload.tsx @@ -3,8 +3,8 @@ import Button from "../components/button"; import FileList from "./files"; import PaymentFlow from "../components/payment"; import ProgressBar from "../components/progress-bar"; -import { openFile } from "../upload"; -import { Blossom } from "../upload/blossom"; +import { openFiles } from "../upload"; +import { Blossom, BlobDescriptor } from "../upload/blossom"; import useLogin from "../hooks/login"; import usePublisher from "../hooks/publisher"; import { Nip96, Nip96FileList } from "../upload/nip96"; @@ -13,12 +13,10 @@ import { FormatBytes } from "../const"; import { UploadProgress } from "../upload/progress"; export default function Upload() { - const [type, setType] = useState<"blossom" | "nip96">("blossom"); const [noCompress, setNoCompress] = useState(false); - const [toUpload, setToUpload] = useState(); const [self, setSelf] = useState(); const [error, setError] = useState(); - const [results, setResults] = useState>([]); + const [results, setResults] = useState>([]); const [listedFiles, setListedFiles] = useState(); const [listedPage, setListedPage] = useState(0); const [showPaymentFlow, setShowPaymentFlow] = useState(false); @@ -30,9 +28,15 @@ export default function Upload() { const url = import.meta.env.VITE_API_URL || `${location.protocol}//${location.host}`; - async function doUpload() { + + // Check if file should have compression enabled by default + const shouldCompress = (file: File) => { + return file.type.startsWith('video/') || file.type.startsWith('image/'); + }; + + async function doUpload(file: File) { if (!pub) return; - if (!toUpload) return; + if (!file) return; if (isUploading) return; // Prevent multiple uploads try { @@ -44,19 +48,13 @@ export default function Upload() { setUploadProgress(progress); }; - if (type === "blossom") { - const uploader = new Blossom(url, pub); - const result = noCompress - ? await uploader.upload(toUpload, onProgress) - : await uploader.media(toUpload, onProgress); - setResults((s) => [...s, result]); - } - if (type === "nip96") { - const uploader = new Nip96(url, pub); - await uploader.loadInfo(); - const result = await uploader.upload(toUpload, onProgress); - setResults((s) => [...s, result]); - } + const uploader = new Blossom(url, pub); + // Use compression by default for video and image files, unless explicitly disabled + const useCompression = shouldCompress(file) && !noCompress; + const result = useCompression + ? await uploader.media(file, onProgress) + : await uploader.upload(file, onProgress); + setResults((s) => [...s, result]); } catch (e) { if (e instanceof Error) { setError(e.message || "Upload failed - no error details provided"); @@ -71,6 +69,27 @@ export default function Upload() { } } + async function handleFileSelection() { + if (isUploading) return; + + try { + const files = await openFiles(); + if (!files || files.length === 0) return; + + // Start uploading each file immediately + for (let i = 0; i < files.length; i++) { + const file = files[i]; + await doUpload(file); + } + } catch (e) { + if (e instanceof Error) { + setError(e.message || "File selection failed"); + } else { + setError("File selection failed"); + } + } + } + const listUploads = useCallback( async (n: number) => { if (!pub) return; @@ -147,39 +166,9 @@ export default function Upload() { )}
-

Upload Settings

+

Upload Files

-
- -
- - -
-
-
- {toUpload && ( -
- -
- )} - {/* Upload Progress */} {isUploading && uploadProgress && ( )}
-
@@ -232,22 +204,31 @@ export default function Upload() { {self && (
-

Storage Quota

+

Storage Usage

+ {/* File Count */} +
+ Files: + + {self.file_count.toLocaleString()} + +
+ + {/* Total Usage */} +
+ Total Size: + + {FormatBytes(self.total_size)} + +
+ + {/* Only show quota information if available */} {self.total_available_quota && self.total_available_quota > 0 && ( <> - {/* File Count */} -
- Files: - - {self.file_count.toLocaleString()} - -
- {/* Progress Bar */}
- Used: + Quota Used: {FormatBytes(self.total_size)} of{" "} {FormatBytes(self.total_available_quota)} @@ -295,16 +276,8 @@ export default function Upload() {
- {/* Quota Breakdown */} + {/* Quota Breakdown - excluding free quota */}
- {self.free_quota && self.free_quota > 0 && ( -
- Free Quota: - - {FormatBytes(self.free_quota)} - -
- )} {(self.quota ?? 0) > 0 && (
Paid Quota: @@ -342,16 +315,6 @@ export default function Upload() {
)} - - {(!self.total_available_quota || - self.total_available_quota === 0) && ( -
-

No quota information available

-

- Contact administrator for storage access -

-
- )}
-
-
- - {result.nip94?.find((tag: any[]) => tag[0] === "thumb") && ( + {result.url && (
-

- Thumbnail URL -

+

File URL

- - { - result.nip94.find( - (tag: any[]) => tag[0] === "thumb", - )?.[1] - } + + {result.url}