license and some functions
This commit is contained in:
+50
-14
@@ -1,9 +1,19 @@
|
||||
import { api } from "@rustpad/api";
|
||||
/*
|
||||
* Copyright (C) 2026 Mateusz Gruszczyński @linuxiarz.pl
|
||||
* Source-Available Code / Dual-Licensed.
|
||||
*
|
||||
* Free for non-commercial and evaluation use under terms of BSL/GPLv3.
|
||||
* Commercial or production use requires a valid paid license.
|
||||
* See LICENSE file in repository root for details.
|
||||
*/
|
||||
|
||||
import { api, uploadWithProgress } from "@rustpad/api";
|
||||
import { copyText } from "@rustpad/clipboard";
|
||||
import { prepareImageFile } from "@rustpad/image-upload";
|
||||
import { askConfirm } from "@rustpad/modal";
|
||||
import { getAuthToken } from "@rustpad/session";
|
||||
import { safeAppUrl } from "@rustpad/security";
|
||||
import { createUploadToast } from "@rustpad/toast";
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value).replace(/[&<>"']/g, character => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[character]));
|
||||
@@ -57,22 +67,48 @@ export function bindNoteFiles({ editor, endpoints, getAccessToken, canDelete, to
|
||||
let file = event.target.files[0];
|
||||
if (!file) return;
|
||||
if (file.type.startsWith("image/")) {
|
||||
file = await prepareImageFile(file);
|
||||
try {
|
||||
file = await prepareImageFile(file);
|
||||
} catch (error) {
|
||||
toast(error.message);
|
||||
input.value = "";
|
||||
return;
|
||||
}
|
||||
if (!file) { input.value = ""; return; }
|
||||
}
|
||||
const form = new FormData();
|
||||
form.append("access_token", getAccessToken() || "");
|
||||
form.append("file", file);
|
||||
try {
|
||||
const result = await api(endpoints.upload, { method: "POST", body: form, headers: {} });
|
||||
const fileUrl = safeAppUrl(result.url);
|
||||
const text = file.type.startsWith("image/") ? `` : `[${file.name}](${fileUrl})`;
|
||||
editor.setRangeText(text, editor.selectionStart, editor.selectionEnd, "end");
|
||||
editor.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
toast("File uploaded");
|
||||
await loadFiles();
|
||||
} catch (error) { toast(error.message); }
|
||||
|
||||
const uploadToast = createUploadToast(file.name);
|
||||
let completed = false;
|
||||
const retryableStatuses = new Set([408, 425, 429, 500, 502, 503, 504]);
|
||||
|
||||
const uploadFile = async () => {
|
||||
uploadToast.start();
|
||||
const form = new FormData();
|
||||
form.append("access_token", getAccessToken() || "");
|
||||
form.append("file", file);
|
||||
try {
|
||||
const result = await uploadWithProgress(endpoints.upload, {
|
||||
method: "POST",
|
||||
body: form,
|
||||
headers: {},
|
||||
onProgress: progress => uploadToast.update(progress),
|
||||
});
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
const fileUrl = safeAppUrl(result.url);
|
||||
const text = file.type.startsWith("image/") ? `` : `[${file.name}](${fileUrl})`;
|
||||
editor.setRangeText(text, editor.selectionStart, editor.selectionEnd, "end");
|
||||
editor.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
uploadToast.success();
|
||||
await loadFiles();
|
||||
} catch (error) {
|
||||
const retryable = !error.status || retryableStatuses.has(error.status);
|
||||
uploadToast.fail(error.message, { retryable, onRetry: uploadFile });
|
||||
}
|
||||
};
|
||||
|
||||
input.value = "";
|
||||
await uploadFile();
|
||||
});
|
||||
|
||||
document.querySelector("#files-button").addEventListener("click", () => loadFiles({ open: true }));
|
||||
|
||||
Reference in New Issue
Block a user