166 lines
4.1 KiB
TypeScript
166 lines
4.1 KiB
TypeScript
import { NostrEvent } from "@snort/system";
|
|
import { useState } from "react";
|
|
import { v4 as uuid } from "uuid";
|
|
|
|
import useEventPublisher from "@/Hooks/useEventPublisher";
|
|
import usePreferences from "@/Hooks/usePreferences";
|
|
import { bech32ToHex, unwrap } from "@/Utils";
|
|
import { KieranPubKey } from "@/Utils/Const";
|
|
import NostrBuild from "@/Utils/Upload/NostrBuild";
|
|
import NostrImg from "@/Utils/Upload/NostrImg";
|
|
import VoidCat from "@/Utils/Upload/VoidCat";
|
|
|
|
import { Nip96Uploader } from "./Nip96";
|
|
|
|
export interface UploadResult {
|
|
url?: string;
|
|
error?: string;
|
|
|
|
/**
|
|
* NIP-94 File Header
|
|
*/
|
|
header?: NostrEvent;
|
|
|
|
/**
|
|
* Media metadata
|
|
*/
|
|
metadata?: {
|
|
blurhash?: string;
|
|
width?: number;
|
|
height?: number;
|
|
hash?: string;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* List of supported upload services and their owners on nostr
|
|
*/
|
|
export const UploaderServices = [
|
|
{
|
|
name: "void.cat",
|
|
owner: bech32ToHex(KieranPubKey),
|
|
},
|
|
{
|
|
name: "nostr.build",
|
|
owner: bech32ToHex("npub1nxy4qpqnld6kmpphjykvx2lqwvxmuxluddwjamm4nc29ds3elyzsm5avr7"),
|
|
},
|
|
{
|
|
name: "nostrimg.com",
|
|
owner: bech32ToHex("npub1xv6axulxcx6mce5mfvfzpsy89r4gee3zuknulm45cqqpmyw7680q5pxea6"),
|
|
},
|
|
];
|
|
|
|
export interface Uploader {
|
|
upload: (f: File | Blob, filename: string) => Promise<UploadResult>;
|
|
progress: Array<UploadProgress>;
|
|
}
|
|
|
|
export interface UploadProgress {
|
|
id: string;
|
|
file: File | Blob;
|
|
progress: number;
|
|
stage: UploadStage;
|
|
}
|
|
|
|
export type UploadStage = "starting" | "hashing" | "uploading" | "done" | undefined;
|
|
|
|
export default function useFileUpload(): Uploader {
|
|
const fileUploader = usePreferences(s => s.fileUploader);
|
|
const { publisher } = useEventPublisher();
|
|
const [progress, setProgress] = useState<Array<UploadProgress>>([]);
|
|
const [stage, setStage] = useState<UploadStage>();
|
|
|
|
switch (fileUploader) {
|
|
case "nostr.build": {
|
|
return {
|
|
upload: f => NostrBuild(f, publisher),
|
|
progress: [],
|
|
} as Uploader;
|
|
}
|
|
case "void.cat-NIP96": {
|
|
return new Nip96Uploader("https://void.cat/nostr", unwrap(publisher));
|
|
}
|
|
case "nostrcheck.me": {
|
|
return new Nip96Uploader("https://nostrcheck.me/api/v2/nip96", unwrap(publisher));
|
|
}
|
|
case "nostrimg.com": {
|
|
return {
|
|
upload: NostrImg,
|
|
progress: [],
|
|
} as Uploader;
|
|
}
|
|
default: {
|
|
return {
|
|
upload: async (f, n) => {
|
|
const id = uuid();
|
|
setProgress(s => [
|
|
...s,
|
|
{
|
|
id,
|
|
file: f,
|
|
progress: 0,
|
|
stage: undefined,
|
|
},
|
|
]);
|
|
const px = (n: number) => {
|
|
setProgress(s =>
|
|
s.map(v =>
|
|
v.id === id
|
|
? {
|
|
...v,
|
|
progress: n,
|
|
}
|
|
: v,
|
|
),
|
|
);
|
|
};
|
|
const ret = await VoidCat(f, n, publisher, px, s => setStage(s));
|
|
setProgress(s => s.filter(a => a.id !== id));
|
|
return ret;
|
|
},
|
|
progress,
|
|
stage,
|
|
} 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;
|
|
};
|