feat: file upload progress / imeta

This commit is contained in:
2023-10-16 11:07:13 +01:00
parent a29d82bd56
commit 6448996529
13 changed files with 257 additions and 60 deletions

View File

@ -1,5 +1,7 @@
import { useState } from "react";
import useLogin from "Hooks/useLogin";
import { NostrEvent } from "@snort/system";
import { v4 as uuid } from "uuid";
import NostrBuild from "Upload/NostrBuild";
import VoidCat from "Upload/VoidCat";
@ -16,6 +18,15 @@ export interface UploadResult {
* NIP-94 File Header
*/
header?: NostrEvent;
/**
* Media metadata
*/
metadata?: {
blurhash?: string;
width?: number;
height?: number;
};
}
/**
@ -38,27 +49,102 @@ export const UploaderServices = [
export interface Uploader {
upload: (f: File | Blob, filename: string) => Promise<UploadResult>;
progress: Array<UploadProgress>;
}
export interface UploadProgress {
id: string;
file: File | Blob;
progress: number;
}
export default function useFileUpload(): Uploader {
const fileUploader = useLogin().preferences.fileUploader;
const { publisher } = useEventPublisher();
const [progress, setProgress] = useState<Array<UploadProgress>>([]);
switch (fileUploader) {
case "nostr.build": {
return {
upload: f => NostrBuild(f, publisher),
progress: [],
} as Uploader;
}
case "nostrimg.com": {
return {
upload: NostrImg,
progress: [],
} as Uploader;
}
default: {
return {
upload: (f, n) => VoidCat(f, n, publisher),
upload: async (f, n) => {
const id = uuid();
setProgress(s => [
...s,
{
id,
file: f,
progress: 0,
},
]);
const px = (n: number) => {
setProgress(s =>
s.map(v =>
v.id === id
? {
...v,
progress: n,
}
: v,
),
);
};
const ret = await VoidCat(f, n, publisher, px);
setProgress(s => s.filter(a => a.id !== id));
return ret;
},
progress,
} as Uploader;
}
}
}
export const ProgressStream = (file: File | Blob, progress: (n: number) => void) => {
let offset = 0;
const DefaultChunkSize = 1024 * 32;
const readChunk = async (offset: number, size: number) => {
if (offset > file.size) {
return new Uint8Array(0);
}
const end = Math.min(offset + size, file.size);
const blob = file.slice(offset, end, file.type);
const data = await blob.arrayBuffer();
return new Uint8Array(data);
};
const rsBase = new ReadableStream(
{
start: async () => {},
pull: async controller => {
const chunk = await readChunk(offset, controller.desiredSize ?? DefaultChunkSize);
if (chunk.byteLength === 0) {
controller.close();
return;
}
progress((offset + chunk.byteLength) / file.size);
offset += chunk.byteLength;
controller.enqueue(chunk);
},
cancel: reason => {
console.log(reason);
},
type: "bytes",
},
{
highWaterMark: DefaultChunkSize,
},
);
return rsBase;
};