@@ -334,7 +302,7 @@ export default function Upload() {
-
+
-
-
- {progress()}%
-
+
+
+
+ {progress()}%
+
+
{status() === "encrypting"
? "encrypting\u2026"
@@ -383,7 +353,7 @@ export default function Upload() {
-
+
-
+
("binary");
const [textContent, setTextContent] = createSignal("");
const [imageSrc, setImageSrc] = createSignal("");
- const [imageAlt, setImageAlt] = createSignal("");
+ const [mediaSrc, setMediaSrc] = createSignal("");
const [fileName, setFileName] = createSignal("");
+ const [copied, setCopied] = createSignal(false);
let decryptedBlob: Blob | null = null;
const worker = new Worker(
@@ -47,6 +48,7 @@ export default function View() {
type: "module",
},
);
+
onCleanup(() => worker.terminate());
onMount(async () => {
@@ -78,22 +80,43 @@ export default function View() {
setSize(info.size);
setExpiresAt(info.expiresAt);
setBurnAfterRead(info.burnAfterRead);
- setStage("meta");
+
+ if (info.burnAfterRead) {
+ setStage("meta");
+ } else {
+ handleView();
+ }
});
const handleView = async () => {
- setLoadBtnDisabled(true);
- setLoadBtnText("Decrypting\u2026");
+ setStage("decrypting");
+ setProgress(0);
+ setDecrypting(false);
try {
- const res = await fetch(`/api/file/${params.id}`);
- if (!res.ok) {
- setError("File not found or expired.");
- setStage("error");
- return;
- }
+ const xhr = new XMLHttpRequest();
+ xhr.open("GET", `/api/file/${params.id}`);
+ xhr.responseType = "arraybuffer";
+
+ const buf = await new Promise((resolve, reject) => {
+ xhr.onprogress = (e) => {
+ if (e.lengthComputable) {
+ setProgress(Math.round((e.loaded / e.total) * 100));
+ }
+ };
+ xhr.onload = () => {
+ if (xhr.status >= 200 && xhr.status < 300) {
+ setProgress(100);
+ setDecrypting(true);
+ resolve(xhr.response);
+ } else {
+ reject(new Error("File not found or expired."));
+ }
+ };
+ xhr.onerror = () => reject(new Error("Download failed."));
+ xhr.send();
+ });
- const buf = await res.arrayBuffer();
const { fileName: name, fileData } = await new Promise<{
fileName: string;
fileData: Uint8Array;
@@ -111,18 +134,28 @@ export default function View() {
[buf],
);
});
+
const ext = getExt(name);
setFileName(name);
if (burnAfterRead()) setBurned(true);
- if (IMAGE_EXTS.has(ext)) {
- const mime = IMAGE_MIME[ext] || "image/png";
+ const mime =
+ IMAGE_MIME[ext] || VIDEO_MIME[ext] || AUDIO_MIME[ext] || undefined;
+ if (mime) {
const blob = new Blob([fileData], { type: mime });
decryptedBlob = blob;
- setImageSrc(URL.createObjectURL(blob));
- setImageAlt(name);
- setContentType("image");
+ const url = URL.createObjectURL(blob);
+ if (IMAGE_EXTS.has(ext)) {
+ setImageSrc(url);
+ setContentType("image");
+ } else if (VIDEO_EXTS.has(ext)) {
+ setMediaSrc(url);
+ setContentType("video");
+ } else {
+ setMediaSrc(url);
+ setContentType("audio");
+ }
} else if (TEXT_EXTS.has(ext)) {
setTextContent(new TextDecoder().decode(fileData));
setContentType("text");
@@ -132,153 +165,146 @@ export default function View() {
}
setStage("content");
- } catch {
- setError("Failed to decrypt. The key may be wrong.");
- setLoadBtnDisabled(false);
- setLoadBtnText("Retry");
+ } catch (e: any) {
+ setError(e.message || "Failed to decrypt.");
+ setStage("error");
}
};
const copyText = () => {
navigator.clipboard.writeText(textContent());
- };
-
- const viewRaw = () => {
- const blob = new Blob([textContent()], { type: "text/plain" });
- window.open(URL.createObjectURL(blob));
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1500);
};
const saveFile = () => {
- if (decryptedBlob) triggerDownload(decryptedBlob, fileName());
+ if (contentType() === "text") {
+ const blob = new Blob([textContent()], { type: "text/plain" });
+ triggerDownload(blob, fileName());
+ } else if (decryptedBlob) {
+ triggerDownload(decryptedBlob, fileName());
+ }
};
return (
<>
-
-
-
Encrypted file
-
- {formatBytes(size())}
- {formatExpiry(expiresAt())}
-
-
-
-
- Burns after viewing
-
-
-
-
-
+
+
+
+
+
+ {formatBytes(size())}
+
+
+
+ {formatExpiry(expiresAt())} · burns after viewing
+
+
+
+
+
+
+ {error()}
-
-
-
-
-