mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-29 00:42:30 +00:00
Refactor upload modal into component
This commit is contained in:
1 parent
7c295b57be
commit
2cfcbc8366
2 files changed
+750
-573
No files matched your search
@@ -44,6 +44,7 @@ import {
|
||||
removeTrackingScripts,
|
||||
} from "./utils/domUtils.js";
|
||||
import { VideoModal } from "./ui/components/VideoModal.js";
|
||||
import { UploadModal } from "./ui/components/UploadModal.js";
|
||||
import {
|
||||
getPubkey as getStoredPubkey,
|
||||
setPubkey as setStoredPubkey,
|
||||
@@ -456,48 +457,27 @@ class bitvidApp {
|
||||
this.profileSwitcherSelectionPubkey = null;
|
||||
this.currentUserNpub = null;
|
||||
|
||||
// Upload modal elements
|
||||
// Upload modal component
|
||||
this.uploadButton = document.getElementById("uploadButton") || null;
|
||||
this.uploadModal = document.getElementById("uploadModal") || null;
|
||||
this.closeUploadModalBtn =
|
||||
document.getElementById("closeUploadModal") || null;
|
||||
this.uploadForm = document.getElementById("uploadForm") || null;
|
||||
this.uploadEnableCommentsInput = null;
|
||||
this.uploadModeToggleButtons = [];
|
||||
this.customUploadSection = null;
|
||||
this.cloudflareUploadSection = null;
|
||||
this.activeUploadMode = "custom";
|
||||
this.r2Service = r2Service;
|
||||
this.cloudflareSettings = this.r2Service.getSettings();
|
||||
this.cloudflareSettingsForm = null;
|
||||
this.cloudflareClearSettingsButton = null;
|
||||
this.cloudflareSettingsStatus = null;
|
||||
this.cloudflareBucketPreview = null;
|
||||
this.cloudflareUploadForm = null;
|
||||
this.cloudflareFileInput = null;
|
||||
this.cloudflareUploadButton = null;
|
||||
this.cloudflareUploadStatus = null;
|
||||
this.cloudflareProgressBar = null;
|
||||
this.cloudflareProgressFill = null;
|
||||
this.cloudflareTitleInput = null;
|
||||
this.cloudflareDescriptionInput = null;
|
||||
this.cloudflareThumbnailInput = null;
|
||||
this.cloudflareMagnetInput = null;
|
||||
this.cloudflareWsInput = null;
|
||||
this.cloudflareXsInput = null;
|
||||
this.cloudflareEnableCommentsInput = null;
|
||||
this.cloudflareAdvancedToggle = null;
|
||||
this.cloudflareAdvancedToggleLabel = null;
|
||||
this.cloudflareAdvancedToggleIcon = null;
|
||||
this.cloudflareAdvancedFields = null;
|
||||
this.cloudflareAdvancedVisible = this.r2Service.getCloudflareAdvancedVisibility();
|
||||
this.r2AccountIdInput = null;
|
||||
this.r2AccessKeyIdInput = null;
|
||||
this.r2SecretAccessKeyInput = null;
|
||||
this.r2ApiTokenInput = null;
|
||||
this.r2ZoneIdInput = null;
|
||||
this.r2BaseDomainInput = null;
|
||||
this.r2ServiceUnsubscribes = [];
|
||||
const uploadModalEvents = new EventTarget();
|
||||
this.uploadModal = new UploadModal({
|
||||
authService: this.authService,
|
||||
r2Service: this.r2Service,
|
||||
publishVideoNote: (payload, options) =>
|
||||
this.publishVideoNote(payload, options),
|
||||
removeTrackingScripts,
|
||||
setGlobalModalState,
|
||||
showError: (message) => this.showError(message),
|
||||
showSuccess: (message) => this.showSuccess(message),
|
||||
getCurrentPubkey: () => this.pubkey,
|
||||
safeEncodeNpub: (pubkey) => this.safeEncodeNpub(pubkey),
|
||||
eventTarget: uploadModalEvents,
|
||||
container: document.getElementById("modalContainer") || null,
|
||||
});
|
||||
this.uploadModal.addEventListener("upload:submit", (event) => {
|
||||
this.handleUploadSubmitEvent(event);
|
||||
});
|
||||
|
||||
// Edit video modal elements
|
||||
this.editVideoModal = null;
|
||||
@@ -783,7 +763,12 @@ class bitvidApp {
|
||||
}
|
||||
|
||||
// 2. Initialize the upload modal (components/upload-modal.html)
|
||||
await this.initUploadModal();
|
||||
try {
|
||||
await this.uploadModal.load();
|
||||
} catch (error) {
|
||||
console.error("initUploadModal failed:", error);
|
||||
this.showError(`Failed to initialize upload modal: ${error.message}`);
|
||||
}
|
||||
initQuickR2Upload(this);
|
||||
|
||||
// 2.5 Initialize the edit modal (components/edit-video-modal.html)
|
||||
@@ -935,294 +920,6 @@ class bitvidApp {
|
||||
return this.videoModal.forceRemovePoster(reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the upload modal (upload-modal.html).
|
||||
*/
|
||||
async initUploadModal() {
|
||||
try {
|
||||
const resp = await fetch("components/upload-modal.html");
|
||||
if (!resp.ok) {
|
||||
throw new Error(`HTTP error! status: ${resp.status}`);
|
||||
}
|
||||
const html = await resp.text();
|
||||
|
||||
const modalContainer = document.getElementById("modalContainer");
|
||||
if (!modalContainer) {
|
||||
throw new Error("Modal container element not found!");
|
||||
}
|
||||
// Append the upload modal markup
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.innerHTML = html;
|
||||
removeTrackingScripts(wrapper);
|
||||
modalContainer.appendChild(wrapper);
|
||||
|
||||
// Grab references
|
||||
this.uploadModal = document.getElementById("uploadModal") || null;
|
||||
this.closeUploadModalBtn =
|
||||
document.getElementById("closeUploadModal") || null;
|
||||
this.uploadForm = document.getElementById("uploadForm") || null;
|
||||
this.uploadEnableCommentsInput =
|
||||
document.getElementById("uploadEnableComments") || null;
|
||||
this.uploadModeToggleButtons = Array.from(
|
||||
document.querySelectorAll(".upload-mode-toggle[data-upload-mode]")
|
||||
);
|
||||
this.customUploadSection =
|
||||
document.getElementById("customUploadSection") || null;
|
||||
this.cloudflareUploadSection =
|
||||
document.getElementById("cloudflareUploadSection") || null;
|
||||
this.cloudflareSettingsForm =
|
||||
document.getElementById("cloudflareSettingsForm") || null;
|
||||
this.cloudflareClearSettingsButton =
|
||||
document.getElementById("cloudflareClearSettings") || null;
|
||||
this.cloudflareSettingsStatus =
|
||||
document.getElementById("cloudflareSettingsStatus") || null;
|
||||
this.cloudflareBucketPreview =
|
||||
document.getElementById("cloudflareBucketPreview") || null;
|
||||
this.cloudflareUploadForm =
|
||||
document.getElementById("cloudflareUploadForm") || null;
|
||||
this.cloudflareFileInput =
|
||||
document.getElementById("cloudflareFile") || null;
|
||||
this.cloudflareUploadButton =
|
||||
document.getElementById("cloudflareUploadButton") || null;
|
||||
this.cloudflareUploadStatus =
|
||||
document.getElementById("cloudflareUploadStatus") || null;
|
||||
this.cloudflareProgressBar =
|
||||
document.getElementById("cloudflareProgressBar") || null;
|
||||
this.cloudflareProgressFill =
|
||||
document.getElementById("cloudflareProgressFill") || null;
|
||||
this.cloudflareTitleInput =
|
||||
document.getElementById("cloudflareTitle") || null;
|
||||
this.cloudflareDescriptionInput =
|
||||
document.getElementById("cloudflareDescription") || null;
|
||||
this.cloudflareThumbnailInput =
|
||||
document.getElementById("cloudflareThumbnail") || null;
|
||||
this.cloudflareMagnetInput =
|
||||
document.getElementById("cloudflareMagnet") || null;
|
||||
this.cloudflareWsInput =
|
||||
document.getElementById("cloudflareWs") || null;
|
||||
this.cloudflareXsInput =
|
||||
document.getElementById("cloudflareXs") || null;
|
||||
this.cloudflareEnableCommentsInput =
|
||||
document.getElementById("cloudflareEnableComments") || null;
|
||||
this.cloudflareAdvancedToggle =
|
||||
document.getElementById("cloudflareAdvancedToggle") || null;
|
||||
this.cloudflareAdvancedToggleLabel =
|
||||
document.getElementById("cloudflareAdvancedToggleLabel") || null;
|
||||
this.cloudflareAdvancedToggleIcon =
|
||||
document.getElementById("cloudflareAdvancedToggleIcon") || null;
|
||||
this.cloudflareAdvancedFields =
|
||||
document.getElementById("cloudflareAdvancedFields") || null;
|
||||
this.r2AccountIdInput =
|
||||
document.getElementById("r2AccountId") || null;
|
||||
this.r2AccessKeyIdInput =
|
||||
document.getElementById("r2AccessKeyId") || null;
|
||||
this.r2SecretAccessKeyInput =
|
||||
document.getElementById("r2SecretAccessKey") || null;
|
||||
this.r2ApiTokenInput =
|
||||
document.getElementById("r2ApiToken") || null;
|
||||
this.r2ZoneIdInput = document.getElementById("r2ZoneId") || null;
|
||||
this.r2BaseDomainInput =
|
||||
document.getElementById("r2BaseDomain") || null;
|
||||
|
||||
// Optional: if close button found, wire up
|
||||
if (this.closeUploadModalBtn) {
|
||||
this.closeUploadModalBtn.addEventListener("click", () => {
|
||||
if (this.uploadModal) {
|
||||
this.uploadModal.classList.add("hidden");
|
||||
setGlobalModalState("upload", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
// If the form is found, wire up
|
||||
if (this.uploadForm) {
|
||||
this.uploadForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
this.handleUploadSubmit();
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(this.r2ServiceUnsubscribes)) {
|
||||
this.r2ServiceUnsubscribes.forEach((fn) => {
|
||||
try {
|
||||
if (typeof fn === "function") {
|
||||
fn();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("Failed to unsubscribe from R2 service event:", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.r2ServiceUnsubscribes = [];
|
||||
|
||||
const registerSubscription = (unsubscribe) => {
|
||||
if (typeof unsubscribe === "function") {
|
||||
this.r2ServiceUnsubscribes.push(unsubscribe);
|
||||
}
|
||||
};
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("advancedVisibilityChange", ({ visible } = {}) => {
|
||||
this.renderCloudflareAdvancedVisibility(visible);
|
||||
})
|
||||
);
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("settingsStatus", ({ message, variant } = {}) => {
|
||||
this.applyCloudflareStatus(
|
||||
this.cloudflareSettingsStatus,
|
||||
message,
|
||||
variant
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("uploadStatus", ({ message, variant } = {}) => {
|
||||
this.applyCloudflareStatus(
|
||||
this.cloudflareUploadStatus,
|
||||
message,
|
||||
variant
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("uploadStateChange", ({ isUploading } = {}) => {
|
||||
this.renderCloudflareUploadingState(isUploading);
|
||||
})
|
||||
);
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("uploadProgress", ({ fraction } = {}) => {
|
||||
this.updateCloudflareProgress(fraction);
|
||||
})
|
||||
);
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("settingsPopulated", ({ settings } = {}) => {
|
||||
this.fillCloudflareSettingsInputs(settings);
|
||||
})
|
||||
);
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("settingsChanged", ({ settings } = {}) => {
|
||||
this.cloudflareSettings = settings || this.cloudflareSettings;
|
||||
})
|
||||
);
|
||||
|
||||
registerSubscription(
|
||||
this.r2Service.on("bucketPreview", (detail = {}) => {
|
||||
this.renderCloudflareBucketPreview(detail);
|
||||
})
|
||||
);
|
||||
|
||||
if (this.cloudflareSettingsForm) {
|
||||
this.cloudflareSettingsForm.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const formValues = this.collectCloudflareSettingsFormValues();
|
||||
const saved = await this.r2Service.saveSettings(formValues);
|
||||
if (saved) {
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to save Cloudflare settings:", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.cloudflareClearSettingsButton) {
|
||||
this.cloudflareClearSettingsButton.addEventListener("click", async () => {
|
||||
try {
|
||||
const cleared = await this.r2Service.clearSettings();
|
||||
if (cleared) {
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to clear Cloudflare settings:", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.cloudflareUploadForm) {
|
||||
this.cloudflareUploadForm.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
if (!this.pubkey) {
|
||||
this.showError("Please login to post a video.");
|
||||
return;
|
||||
}
|
||||
|
||||
const npub = this.safeEncodeNpub(this.pubkey) || "";
|
||||
const file = this.cloudflareFileInput?.files?.[0] || null;
|
||||
const metadata = {
|
||||
title: (this.cloudflareTitleInput?.value || "").trim(),
|
||||
description: (this.cloudflareDescriptionInput?.value || "").trim(),
|
||||
thumbnail: (this.cloudflareThumbnailInput?.value || "").trim(),
|
||||
magnet: (this.cloudflareMagnetInput?.value || "").trim(),
|
||||
ws: (this.cloudflareWsInput?.value || "").trim(),
|
||||
xs: (this.cloudflareXsInput?.value || "").trim(),
|
||||
enableComments: this.cloudflareEnableCommentsInput
|
||||
? this.cloudflareEnableCommentsInput.checked
|
||||
: true,
|
||||
};
|
||||
|
||||
try {
|
||||
await this.r2Service.uploadVideo({
|
||||
npub,
|
||||
file,
|
||||
metadata,
|
||||
settingsInput: this.collectCloudflareSettingsFormValues(),
|
||||
publishVideoNote: (payload, options) =>
|
||||
this.publishVideoNote(payload, options),
|
||||
onReset: () => this.resetCloudflareUploadForm(),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Cloudflare upload failed:", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.uploadModeToggleButtons.length > 0) {
|
||||
this.uploadModeToggleButtons.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const mode = btn.dataset.uploadMode || "custom";
|
||||
this.setUploadMode(mode);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggle) {
|
||||
this.cloudflareAdvancedToggle.addEventListener("click", () => {
|
||||
this.r2Service.setCloudflareAdvancedVisibility(
|
||||
!this.r2Service.getCloudflareAdvancedVisibility()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
this.cloudflareSettings = this.r2Service.getSettings();
|
||||
this.renderCloudflareAdvancedVisibility(
|
||||
this.r2Service.getCloudflareAdvancedVisibility()
|
||||
);
|
||||
|
||||
try {
|
||||
await this.r2Service.loadSettings();
|
||||
} catch (err) {
|
||||
// errors already reported by the service
|
||||
}
|
||||
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
|
||||
this.setUploadMode(this.activeUploadMode);
|
||||
|
||||
console.log("Upload modal initialization successful");
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("initUploadModal failed:", error);
|
||||
this.showError(`Failed to initialize upload modal: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async initEditVideoModal() {
|
||||
try {
|
||||
let modal = document.getElementById("editVideoModal");
|
||||
@@ -2540,214 +2237,6 @@ class bitvidApp {
|
||||
}
|
||||
}
|
||||
|
||||
setUploadMode(mode) {
|
||||
const normalized = mode === "cloudflare" ? "cloudflare" : "custom";
|
||||
this.activeUploadMode = normalized;
|
||||
|
||||
if (this.customUploadSection) {
|
||||
if (normalized === "custom") {
|
||||
this.customUploadSection.classList.remove("hidden");
|
||||
} else {
|
||||
this.customUploadSection.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cloudflareUploadSection) {
|
||||
if (normalized === "cloudflare") {
|
||||
this.cloudflareUploadSection.classList.remove("hidden");
|
||||
} else {
|
||||
this.cloudflareUploadSection.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(this.uploadModeToggleButtons)) {
|
||||
this.uploadModeToggleButtons.forEach((btn) => {
|
||||
if (!btn || !btn.dataset) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isActive = btn.dataset.uploadMode === normalized;
|
||||
btn.classList.toggle("bg-blue-500", isActive);
|
||||
btn.classList.toggle("text-white", isActive);
|
||||
btn.classList.toggle("shadow", isActive);
|
||||
btn.classList.toggle("text-gray-300", !isActive);
|
||||
btn.setAttribute("aria-pressed", isActive ? "true" : "false");
|
||||
});
|
||||
}
|
||||
|
||||
if (normalized === "cloudflare") {
|
||||
this.refreshCloudflareBucketPreview();
|
||||
}
|
||||
}
|
||||
|
||||
collectCloudflareSettingsFormValues() {
|
||||
return {
|
||||
accountId: (this.r2AccountIdInput?.value || "").trim(),
|
||||
accessKeyId: (this.r2AccessKeyIdInput?.value || "").trim(),
|
||||
secretAccessKey: (this.r2SecretAccessKeyInput?.value || "").trim(),
|
||||
apiToken: (this.r2ApiTokenInput?.value || "").trim(),
|
||||
zoneId: (this.r2ZoneIdInput?.value || "").trim(),
|
||||
baseDomain: this.r2BaseDomainInput?.value || "",
|
||||
};
|
||||
}
|
||||
|
||||
applyCloudflareStatus(element, message = "", variant = "info") {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.textContent = message || "";
|
||||
element.classList.remove(
|
||||
"text-green-400",
|
||||
"text-red-400",
|
||||
"text-yellow-400",
|
||||
"text-gray-400"
|
||||
);
|
||||
if (!message) {
|
||||
element.classList.add("text-gray-400");
|
||||
return;
|
||||
}
|
||||
|
||||
let cls = "text-gray-400";
|
||||
if (variant === "success") {
|
||||
cls = "text-green-400";
|
||||
} else if (variant === "error") {
|
||||
cls = "text-red-400";
|
||||
} else if (variant === "warning") {
|
||||
cls = "text-yellow-400";
|
||||
}
|
||||
element.classList.add(cls);
|
||||
}
|
||||
|
||||
renderCloudflareAdvancedVisibility(visible) {
|
||||
const isVisible = Boolean(visible);
|
||||
this.cloudflareAdvancedVisible = isVisible;
|
||||
|
||||
if (this.cloudflareAdvancedFields) {
|
||||
if (isVisible) {
|
||||
this.cloudflareAdvancedFields.classList.remove("hidden");
|
||||
} else {
|
||||
this.cloudflareAdvancedFields.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggle) {
|
||||
this.cloudflareAdvancedToggle.setAttribute(
|
||||
"aria-expanded",
|
||||
isVisible ? "true" : "false"
|
||||
);
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggleLabel) {
|
||||
this.cloudflareAdvancedToggleLabel.textContent = isVisible
|
||||
? "Hide advanced options"
|
||||
: "Show advanced options";
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggleIcon) {
|
||||
this.cloudflareAdvancedToggleIcon.classList.toggle("rotate-90", isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
renderCloudflareUploadingState(isUploading) {
|
||||
if (this.cloudflareUploadButton) {
|
||||
this.cloudflareUploadButton.disabled = Boolean(isUploading);
|
||||
this.cloudflareUploadButton.textContent = isUploading
|
||||
? "Uploading…"
|
||||
: "Upload to R2 & publish";
|
||||
}
|
||||
|
||||
if (this.cloudflareFileInput) {
|
||||
this.cloudflareFileInput.disabled = Boolean(isUploading);
|
||||
}
|
||||
|
||||
if (this.cloudflareEnableCommentsInput) {
|
||||
this.cloudflareEnableCommentsInput.disabled = Boolean(isUploading);
|
||||
}
|
||||
}
|
||||
|
||||
fillCloudflareSettingsInputs(settings) {
|
||||
const data = settings || {};
|
||||
|
||||
if (this.r2AccountIdInput) {
|
||||
this.r2AccountIdInput.value = data.accountId || "";
|
||||
}
|
||||
if (this.r2AccessKeyIdInput) {
|
||||
this.r2AccessKeyIdInput.value = data.accessKeyId || "";
|
||||
}
|
||||
if (this.r2SecretAccessKeyInput) {
|
||||
this.r2SecretAccessKeyInput.value = data.secretAccessKey || "";
|
||||
}
|
||||
if (this.r2ApiTokenInput) {
|
||||
this.r2ApiTokenInput.value = data.apiToken || "";
|
||||
}
|
||||
if (this.r2ZoneIdInput) {
|
||||
this.r2ZoneIdInput.value = data.zoneId || "";
|
||||
}
|
||||
if (this.r2BaseDomainInput) {
|
||||
this.r2BaseDomainInput.value = data.baseDomain || "";
|
||||
}
|
||||
}
|
||||
|
||||
renderCloudflareBucketPreview({ text = "", title = "" } = {}) {
|
||||
if (!this.cloudflareBucketPreview) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cloudflareBucketPreview.textContent = text || "";
|
||||
if (title) {
|
||||
this.cloudflareBucketPreview.setAttribute("title", title);
|
||||
} else {
|
||||
this.cloudflareBucketPreview.removeAttribute("title");
|
||||
}
|
||||
}
|
||||
|
||||
async refreshCloudflareBucketPreview() {
|
||||
if (!this.r2Service) {
|
||||
return;
|
||||
}
|
||||
const hasPubkey = Boolean(this.pubkey);
|
||||
const npub = hasPubkey ? this.safeEncodeNpub(this.pubkey) : "";
|
||||
await this.r2Service.updateCloudflareBucketPreview({ hasPubkey, npub });
|
||||
}
|
||||
|
||||
updateCloudflareProgress(fraction) {
|
||||
if (!this.cloudflareProgressBar || !this.cloudflareProgressFill) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Number.isFinite(fraction) || fraction < 0) {
|
||||
this.cloudflareProgressBar.classList.add("hidden");
|
||||
this.cloudflareProgressBar.setAttribute("aria-hidden", "true");
|
||||
this.cloudflareProgressFill.style.width = "0%";
|
||||
this.cloudflareProgressFill.setAttribute("aria-valuenow", "0");
|
||||
return;
|
||||
}
|
||||
|
||||
const clamped = Math.max(0, Math.min(1, fraction));
|
||||
const percent = Math.round(clamped * 100);
|
||||
|
||||
this.cloudflareProgressBar.classList.remove("hidden");
|
||||
this.cloudflareProgressBar.setAttribute("aria-hidden", "false");
|
||||
this.cloudflareProgressFill.style.width = `${percent}%`;
|
||||
this.cloudflareProgressFill.setAttribute("aria-valuenow", `${percent}`);
|
||||
}
|
||||
|
||||
resetCloudflareUploadForm() {
|
||||
if (this.cloudflareTitleInput) this.cloudflareTitleInput.value = "";
|
||||
if (this.cloudflareDescriptionInput)
|
||||
this.cloudflareDescriptionInput.value = "";
|
||||
if (this.cloudflareThumbnailInput)
|
||||
this.cloudflareThumbnailInput.value = "";
|
||||
if (this.cloudflareMagnetInput) this.cloudflareMagnetInput.value = "";
|
||||
if (this.cloudflareWsInput) this.cloudflareWsInput.value = "";
|
||||
if (this.cloudflareXsInput) this.cloudflareXsInput.value = "";
|
||||
if (this.cloudflareEnableCommentsInput)
|
||||
this.cloudflareEnableCommentsInput.checked = true;
|
||||
if (this.cloudflareFileInput) this.cloudflareFileInput.value = "";
|
||||
this.updateCloudflareProgress(Number.NaN);
|
||||
}
|
||||
|
||||
renderSavedProfiles() {
|
||||
const fallbackAvatar = "assets/svg/default-profile.svg";
|
||||
const normalizedActive = this.normalizeHexPubkey(
|
||||
@@ -4862,8 +4351,7 @@ class bitvidApp {
|
||||
if (this.uploadButton) {
|
||||
this.uploadButton.addEventListener("click", () => {
|
||||
if (this.uploadModal) {
|
||||
this.uploadModal.classList.remove("hidden");
|
||||
setGlobalModalState("upload", true);
|
||||
this.uploadModal.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -5207,8 +4695,7 @@ class bitvidApp {
|
||||
await onSuccess();
|
||||
}
|
||||
if (suppressModalClose !== true && this.uploadModal) {
|
||||
this.uploadModal.classList.add("hidden");
|
||||
setGlobalModalState("upload", false);
|
||||
this.uploadModal.close();
|
||||
}
|
||||
await this.loadVideos();
|
||||
this.showSuccess("Video shared successfully!");
|
||||
@@ -5359,42 +4846,17 @@ class bitvidApp {
|
||||
/**
|
||||
* Actually handle the upload form submission.
|
||||
*/
|
||||
async handleUploadSubmit() {
|
||||
const titleEl = document.getElementById("uploadTitle");
|
||||
const urlEl = document.getElementById("uploadUrl");
|
||||
const magnetEl = document.getElementById("uploadMagnet");
|
||||
const wsEl = document.getElementById("uploadWs");
|
||||
const xsEl = document.getElementById("uploadXs");
|
||||
const thumbEl = document.getElementById("uploadThumbnail");
|
||||
const descEl = document.getElementById("uploadDescription");
|
||||
const privEl = document.getElementById("uploadIsPrivate");
|
||||
const commentsEl = document.getElementById("uploadEnableComments");
|
||||
|
||||
const payload = {
|
||||
title: titleEl?.value.trim() || "",
|
||||
url: urlEl?.value.trim() || "",
|
||||
magnet: magnetEl?.value.trim() || "",
|
||||
ws: wsEl?.value.trim() || "",
|
||||
xs: xsEl?.value.trim() || "",
|
||||
thumbnail: thumbEl?.value.trim() || "",
|
||||
description: descEl?.value.trim() || "",
|
||||
enableComments: commentsEl ? commentsEl.checked : true,
|
||||
};
|
||||
async handleUploadSubmitEvent(event) {
|
||||
const payload = event?.detail?.payload || {};
|
||||
|
||||
try {
|
||||
await this.authService.handleUploadSubmit(payload, {
|
||||
publish: (data) =>
|
||||
this.publishVideoNote(data, {
|
||||
onSuccess: () => {
|
||||
if (titleEl) titleEl.value = "";
|
||||
if (urlEl) urlEl.value = "";
|
||||
if (magnetEl) magnetEl.value = "";
|
||||
if (wsEl) wsEl.value = "";
|
||||
if (xsEl) xsEl.value = "";
|
||||
if (thumbEl) thumbEl.value = "";
|
||||
if (descEl) descEl.value = "";
|
||||
if (privEl) privEl.checked = false;
|
||||
if (commentsEl) commentsEl.checked = true;
|
||||
if (this.uploadModal?.resetCustomForm) {
|
||||
this.uploadModal.resetCustomForm();
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -5528,7 +4990,9 @@ class bitvidApp {
|
||||
|
||||
await this.loadVideos();
|
||||
this.forceRefreshAllProfiles();
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
if (this.uploadModal?.refreshCloudflareBucketPreview) {
|
||||
await this.uploadModal.refreshCloudflareBucketPreview();
|
||||
}
|
||||
}
|
||||
|
||||
async handleAuthLogout(detail = {}) {
|
||||
@@ -5587,7 +5051,9 @@ class bitvidApp {
|
||||
|
||||
await this.loadVideos();
|
||||
this.forceRefreshAllProfiles();
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
if (this.uploadModal?.refreshCloudflareBucketPreview) {
|
||||
await this.uploadModal.refreshCloudflareBucketPreview();
|
||||
}
|
||||
}
|
||||
|
||||
handleProfileUpdated(detail = {}) {
|
||||
|
||||
@@ -0,0 +1,711 @@
|
||||
export class UploadModal {
|
||||
constructor({
|
||||
authService,
|
||||
r2Service,
|
||||
publishVideoNote,
|
||||
removeTrackingScripts,
|
||||
setGlobalModalState,
|
||||
showError,
|
||||
showSuccess,
|
||||
getCurrentPubkey,
|
||||
safeEncodeNpub,
|
||||
eventTarget,
|
||||
container,
|
||||
} = {}) {
|
||||
this.authService = authService || null;
|
||||
this.r2Service = r2Service || null;
|
||||
this.publishVideoNote = typeof publishVideoNote === "function" ? publishVideoNote : null;
|
||||
this.removeTrackingScripts =
|
||||
typeof removeTrackingScripts === "function" ? removeTrackingScripts : () => {};
|
||||
this.setGlobalModalState =
|
||||
typeof setGlobalModalState === "function" ? setGlobalModalState : () => {};
|
||||
this.showError = typeof showError === "function" ? showError : () => {};
|
||||
this.showSuccess = typeof showSuccess === "function" ? showSuccess : () => {};
|
||||
this.getCurrentPubkey = typeof getCurrentPubkey === "function" ? getCurrentPubkey : null;
|
||||
this.safeEncodeNpub = typeof safeEncodeNpub === "function" ? safeEncodeNpub : () => "";
|
||||
this.eventTarget = eventTarget instanceof EventTarget ? eventTarget : new EventTarget();
|
||||
this.container = container || null;
|
||||
|
||||
this.root = null;
|
||||
this.activeMode = "custom";
|
||||
this.isVisible = false;
|
||||
this.cloudflareSettings = this.r2Service?.getSettings?.() || null;
|
||||
this.cloudflareAdvancedVisible =
|
||||
this.r2Service?.getCloudflareAdvancedVisibility?.() || false;
|
||||
this.r2Unsubscribes = [];
|
||||
|
||||
this.uploadModeButtons = [];
|
||||
this.customSection = null;
|
||||
this.cloudflareSection = null;
|
||||
|
||||
this.customForm = null;
|
||||
this.customFormInputs = {};
|
||||
|
||||
this.cloudflareSettingsForm = null;
|
||||
this.cloudflareClearSettingsButton = null;
|
||||
this.cloudflareSettingsStatus = null;
|
||||
this.cloudflareBucketPreview = null;
|
||||
this.cloudflareUploadForm = null;
|
||||
this.cloudflareFileInput = null;
|
||||
this.cloudflareUploadButton = null;
|
||||
this.cloudflareUploadStatus = null;
|
||||
this.cloudflareProgressBar = null;
|
||||
this.cloudflareProgressFill = null;
|
||||
this.cloudflareTitleInput = null;
|
||||
this.cloudflareDescriptionInput = null;
|
||||
this.cloudflareThumbnailInput = null;
|
||||
this.cloudflareMagnetInput = null;
|
||||
this.cloudflareWsInput = null;
|
||||
this.cloudflareXsInput = null;
|
||||
this.cloudflareEnableCommentsInput = null;
|
||||
this.cloudflareAdvancedToggle = null;
|
||||
this.cloudflareAdvancedToggleLabel = null;
|
||||
this.cloudflareAdvancedToggleIcon = null;
|
||||
this.cloudflareAdvancedFields = null;
|
||||
this.r2AccountIdInput = null;
|
||||
this.r2AccessKeyIdInput = null;
|
||||
this.r2SecretAccessKeyInput = null;
|
||||
this.r2ApiTokenInput = null;
|
||||
this.r2ZoneIdInput = null;
|
||||
this.r2BaseDomainInput = null;
|
||||
}
|
||||
|
||||
addEventListener(type, listener, options) {
|
||||
if (!this.eventTarget) {
|
||||
return;
|
||||
}
|
||||
this.eventTarget.addEventListener(type, listener, options);
|
||||
}
|
||||
|
||||
removeEventListener(type, listener, options) {
|
||||
if (!this.eventTarget) {
|
||||
return;
|
||||
}
|
||||
this.eventTarget.removeEventListener(type, listener, options);
|
||||
}
|
||||
|
||||
emit(type, detail) {
|
||||
if (!this.eventTarget) {
|
||||
return;
|
||||
}
|
||||
this.eventTarget.dispatchEvent(new CustomEvent(type, { detail }));
|
||||
}
|
||||
|
||||
getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
async load({ container } = {}) {
|
||||
if (this.root) {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
const targetContainer =
|
||||
container || this.container || document.getElementById("modalContainer");
|
||||
if (!targetContainer) {
|
||||
throw new Error("Modal container element not found!");
|
||||
}
|
||||
|
||||
const response = await fetch("components/upload-modal.html");
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const html = await response.text();
|
||||
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.innerHTML = html;
|
||||
this.removeTrackingScripts(wrapper);
|
||||
targetContainer.appendChild(wrapper);
|
||||
|
||||
this.container = targetContainer;
|
||||
this.root = wrapper.querySelector("#uploadModal");
|
||||
if (!this.root) {
|
||||
throw new Error("Upload modal markup missing after load.");
|
||||
}
|
||||
|
||||
this.cacheElements(wrapper);
|
||||
this.bindEvents();
|
||||
this.registerR2Subscriptions();
|
||||
|
||||
this.renderCloudflareAdvancedVisibility(
|
||||
this.r2Service?.getCloudflareAdvancedVisibility?.()
|
||||
);
|
||||
|
||||
try {
|
||||
await this.loadR2Settings();
|
||||
} catch (error) {
|
||||
// Errors already surfaced via the service listeners.
|
||||
}
|
||||
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
this.setMode(this.activeMode);
|
||||
this.updateCloudflareProgress(Number.NaN);
|
||||
|
||||
return this.root;
|
||||
}
|
||||
|
||||
cacheElements(context) {
|
||||
this.uploadModeButtons = Array.from(
|
||||
context.querySelectorAll(".upload-mode-toggle[data-upload-mode]")
|
||||
);
|
||||
this.customSection = context.querySelector("#customUploadSection") || null;
|
||||
this.cloudflareSection = context.querySelector("#cloudflareUploadSection") || null;
|
||||
|
||||
this.customForm = context.querySelector("#uploadForm") || null;
|
||||
this.customFormInputs = {
|
||||
title: context.querySelector("#uploadTitle") || null,
|
||||
url: context.querySelector("#uploadUrl") || null,
|
||||
magnet: context.querySelector("#uploadMagnet") || null,
|
||||
ws: context.querySelector("#uploadWs") || null,
|
||||
xs: context.querySelector("#uploadXs") || null,
|
||||
thumbnail: context.querySelector("#uploadThumbnail") || null,
|
||||
description: context.querySelector("#uploadDescription") || null,
|
||||
enableComments: context.querySelector("#uploadEnableComments") || null,
|
||||
isPrivate: context.querySelector("#uploadIsPrivate") || null,
|
||||
};
|
||||
|
||||
this.closeButton = context.querySelector("#closeUploadModal") || null;
|
||||
|
||||
this.cloudflareSettingsForm = context.querySelector("#cloudflareSettingsForm") || null;
|
||||
this.cloudflareClearSettingsButton =
|
||||
context.querySelector("#cloudflareClearSettings") || null;
|
||||
this.cloudflareSettingsStatus =
|
||||
context.querySelector("#cloudflareSettingsStatus") || null;
|
||||
this.cloudflareBucketPreview =
|
||||
context.querySelector("#cloudflareBucketPreview") || null;
|
||||
this.cloudflareUploadForm = context.querySelector("#cloudflareUploadForm") || null;
|
||||
this.cloudflareFileInput = context.querySelector("#cloudflareFile") || null;
|
||||
this.cloudflareUploadButton = context.querySelector("#cloudflareUploadButton") || null;
|
||||
this.cloudflareUploadStatus =
|
||||
context.querySelector("#cloudflareUploadStatus") || null;
|
||||
this.cloudflareProgressBar = context.querySelector("#cloudflareProgressBar") || null;
|
||||
this.cloudflareProgressFill = context.querySelector("#cloudflareProgressFill") || null;
|
||||
this.cloudflareTitleInput = context.querySelector("#cloudflareTitle") || null;
|
||||
this.cloudflareDescriptionInput =
|
||||
context.querySelector("#cloudflareDescription") || null;
|
||||
this.cloudflareThumbnailInput =
|
||||
context.querySelector("#cloudflareThumbnail") || null;
|
||||
this.cloudflareMagnetInput = context.querySelector("#cloudflareMagnet") || null;
|
||||
this.cloudflareWsInput = context.querySelector("#cloudflareWs") || null;
|
||||
this.cloudflareXsInput = context.querySelector("#cloudflareXs") || null;
|
||||
this.cloudflareEnableCommentsInput =
|
||||
context.querySelector("#cloudflareEnableComments") || null;
|
||||
this.cloudflareAdvancedToggle =
|
||||
context.querySelector("#cloudflareAdvancedToggle") || null;
|
||||
this.cloudflareAdvancedToggleLabel =
|
||||
context.querySelector("#cloudflareAdvancedToggleLabel") || null;
|
||||
this.cloudflareAdvancedToggleIcon =
|
||||
context.querySelector("#cloudflareAdvancedToggleIcon") || null;
|
||||
this.cloudflareAdvancedFields =
|
||||
context.querySelector("#cloudflareAdvancedFields") || null;
|
||||
this.r2AccountIdInput = context.querySelector("#r2AccountId") || null;
|
||||
this.r2AccessKeyIdInput = context.querySelector("#r2AccessKeyId") || null;
|
||||
this.r2SecretAccessKeyInput = context.querySelector("#r2SecretAccessKey") || null;
|
||||
this.r2ApiTokenInput = context.querySelector("#r2ApiToken") || null;
|
||||
this.r2ZoneIdInput = context.querySelector("#r2ZoneId") || null;
|
||||
this.r2BaseDomainInput = context.querySelector("#r2BaseDomain") || null;
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
if (this.closeButton) {
|
||||
this.closeButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.customForm) {
|
||||
this.customForm.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
this.handleCustomSubmit();
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(this.uploadModeButtons)) {
|
||||
this.uploadModeButtons.forEach((button) => {
|
||||
if (!button) return;
|
||||
button.addEventListener("click", () => {
|
||||
const mode = button.dataset.uploadMode || "custom";
|
||||
this.setMode(mode);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (this.cloudflareSettingsForm) {
|
||||
this.cloudflareSettingsForm.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
await this.handleCloudflareSettingsSubmit();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.cloudflareClearSettingsButton) {
|
||||
this.cloudflareClearSettingsButton.addEventListener("click", async () => {
|
||||
await this.handleCloudflareClearSettings();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.cloudflareUploadForm) {
|
||||
this.cloudflareUploadForm.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
await this.handleCloudflareUploadSubmit();
|
||||
});
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggle) {
|
||||
this.cloudflareAdvancedToggle.addEventListener("click", () => {
|
||||
if (this.r2Service?.setCloudflareAdvancedVisibility &&
|
||||
this.r2Service?.getCloudflareAdvancedVisibility) {
|
||||
const nextState = !this.r2Service.getCloudflareAdvancedVisibility();
|
||||
this.r2Service.setCloudflareAdvancedVisibility(nextState);
|
||||
} else {
|
||||
this.renderCloudflareAdvancedVisibility(!this.cloudflareAdvancedVisible);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerR2Subscriptions() {
|
||||
if (!this.r2Service?.on) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(this.r2Unsubscribes)) {
|
||||
this.r2Unsubscribes.forEach((unsubscribe) => {
|
||||
try {
|
||||
if (typeof unsubscribe === "function") {
|
||||
unsubscribe();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[UploadModal] Failed to remove R2 listener", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.r2Unsubscribes = [];
|
||||
|
||||
const register = (unsubscribe) => {
|
||||
if (typeof unsubscribe === "function") {
|
||||
this.r2Unsubscribes.push(unsubscribe);
|
||||
}
|
||||
};
|
||||
|
||||
register(
|
||||
this.r2Service.on("advancedVisibilityChange", ({ visible } = {}) => {
|
||||
this.renderCloudflareAdvancedVisibility(visible);
|
||||
})
|
||||
);
|
||||
|
||||
register(
|
||||
this.r2Service.on("settingsStatus", ({ message, variant } = {}) => {
|
||||
this.applyCloudflareStatus(this.cloudflareSettingsStatus, message, variant);
|
||||
})
|
||||
);
|
||||
|
||||
register(
|
||||
this.r2Service.on("uploadStatus", ({ message, variant } = {}) => {
|
||||
this.applyCloudflareStatus(this.cloudflareUploadStatus, message, variant);
|
||||
})
|
||||
);
|
||||
|
||||
register(
|
||||
this.r2Service.on("uploadStateChange", ({ isUploading } = {}) => {
|
||||
this.renderCloudflareUploadingState(isUploading);
|
||||
})
|
||||
);
|
||||
|
||||
register(
|
||||
this.r2Service.on("uploadProgress", ({ fraction } = {}) => {
|
||||
this.updateCloudflareProgress(fraction);
|
||||
})
|
||||
);
|
||||
|
||||
register(
|
||||
this.r2Service.on("settingsPopulated", ({ settings } = {}) => {
|
||||
this.fillCloudflareSettingsInputs(settings);
|
||||
})
|
||||
);
|
||||
|
||||
register(
|
||||
this.r2Service.on("settingsChanged", ({ settings } = {}) => {
|
||||
this.cloudflareSettings = settings || this.cloudflareSettings;
|
||||
})
|
||||
);
|
||||
|
||||
register(
|
||||
this.r2Service.on("bucketPreview", (detail = {}) => {
|
||||
this.renderCloudflareBucketPreview(detail);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
open() {
|
||||
if (!this.root) {
|
||||
return;
|
||||
}
|
||||
if (!this.root.classList.contains("hidden")) {
|
||||
this.isVisible = true;
|
||||
return;
|
||||
}
|
||||
this.root.classList.remove("hidden");
|
||||
this.isVisible = true;
|
||||
this.setGlobalModalState("upload", true);
|
||||
this.emit("upload:open", { mode: this.activeMode });
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!this.root) {
|
||||
return;
|
||||
}
|
||||
if (this.root.classList.contains("hidden")) {
|
||||
this.isVisible = false;
|
||||
return;
|
||||
}
|
||||
this.root.classList.add("hidden");
|
||||
this.isVisible = false;
|
||||
this.setGlobalModalState("upload", false);
|
||||
this.emit("upload:close", { mode: this.activeMode });
|
||||
}
|
||||
|
||||
setMode(mode) {
|
||||
const normalized = mode === "cloudflare" ? "cloudflare" : "custom";
|
||||
this.activeMode = normalized;
|
||||
|
||||
if (this.customSection) {
|
||||
if (normalized === "custom") {
|
||||
this.customSection.classList.remove("hidden");
|
||||
} else {
|
||||
this.customSection.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cloudflareSection) {
|
||||
if (normalized === "cloudflare") {
|
||||
this.cloudflareSection.classList.remove("hidden");
|
||||
} else {
|
||||
this.cloudflareSection.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(this.uploadModeButtons)) {
|
||||
this.uploadModeButtons.forEach((button) => {
|
||||
if (!button?.dataset) {
|
||||
return;
|
||||
}
|
||||
const isActive = button.dataset.uploadMode === normalized;
|
||||
button.classList.toggle("bg-blue-500", isActive);
|
||||
button.classList.toggle("text-white", isActive);
|
||||
button.classList.toggle("shadow", isActive);
|
||||
button.classList.toggle("text-gray-300", !isActive);
|
||||
button.setAttribute("aria-pressed", isActive ? "true" : "false");
|
||||
});
|
||||
}
|
||||
|
||||
if (normalized === "cloudflare") {
|
||||
this.refreshCloudflareBucketPreview();
|
||||
}
|
||||
}
|
||||
|
||||
handleCustomSubmit() {
|
||||
const payload = {
|
||||
title: this.customFormInputs.title?.value?.trim() || "",
|
||||
url: this.customFormInputs.url?.value?.trim() || "",
|
||||
magnet: this.customFormInputs.magnet?.value?.trim() || "",
|
||||
ws: this.customFormInputs.ws?.value?.trim() || "",
|
||||
xs: this.customFormInputs.xs?.value?.trim() || "",
|
||||
thumbnail: this.customFormInputs.thumbnail?.value?.trim() || "",
|
||||
description: this.customFormInputs.description?.value?.trim() || "",
|
||||
enableComments: this.customFormInputs.enableComments
|
||||
? !!this.customFormInputs.enableComments.checked
|
||||
: true,
|
||||
};
|
||||
|
||||
if (this.customFormInputs.isPrivate) {
|
||||
payload.isPrivate = !!this.customFormInputs.isPrivate.checked;
|
||||
}
|
||||
|
||||
this.emit("upload:submit", { payload });
|
||||
}
|
||||
|
||||
collectCloudflareSettingsFormValues() {
|
||||
return {
|
||||
accountId: this.r2AccountIdInput?.value?.trim() || "",
|
||||
accessKeyId: this.r2AccessKeyIdInput?.value?.trim() || "",
|
||||
secretAccessKey: this.r2SecretAccessKeyInput?.value?.trim() || "",
|
||||
apiToken: this.r2ApiTokenInput?.value?.trim() || "",
|
||||
zoneId: this.r2ZoneIdInput?.value?.trim() || "",
|
||||
baseDomain: this.r2BaseDomainInput?.value || "",
|
||||
};
|
||||
}
|
||||
|
||||
applyCloudflareStatus(element, message = "", variant = "info") {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.textContent = message || "";
|
||||
element.classList.remove(
|
||||
"text-green-400",
|
||||
"text-red-400",
|
||||
"text-yellow-400",
|
||||
"text-gray-400"
|
||||
);
|
||||
|
||||
if (!message) {
|
||||
element.classList.add("text-gray-400");
|
||||
return;
|
||||
}
|
||||
|
||||
let className = "text-gray-400";
|
||||
if (variant === "success") {
|
||||
className = "text-green-400";
|
||||
} else if (variant === "error") {
|
||||
className = "text-red-400";
|
||||
} else if (variant === "warning") {
|
||||
className = "text-yellow-400";
|
||||
}
|
||||
element.classList.add(className);
|
||||
}
|
||||
|
||||
renderCloudflareAdvancedVisibility(visible) {
|
||||
const isVisible = Boolean(visible);
|
||||
this.cloudflareAdvancedVisible = isVisible;
|
||||
|
||||
if (this.cloudflareAdvancedFields) {
|
||||
if (isVisible) {
|
||||
this.cloudflareAdvancedFields.classList.remove("hidden");
|
||||
} else {
|
||||
this.cloudflareAdvancedFields.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggle) {
|
||||
this.cloudflareAdvancedToggle.setAttribute(
|
||||
"aria-expanded",
|
||||
isVisible ? "true" : "false"
|
||||
);
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggleLabel) {
|
||||
this.cloudflareAdvancedToggleLabel.textContent = isVisible
|
||||
? "Hide advanced options"
|
||||
: "Show advanced options";
|
||||
}
|
||||
|
||||
if (this.cloudflareAdvancedToggleIcon) {
|
||||
this.cloudflareAdvancedToggleIcon.classList.toggle("rotate-90", isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
renderCloudflareUploadingState(isUploading) {
|
||||
if (this.cloudflareUploadButton) {
|
||||
this.cloudflareUploadButton.disabled = Boolean(isUploading);
|
||||
this.cloudflareUploadButton.textContent = isUploading
|
||||
? "Uploading…"
|
||||
: "Upload to R2 & publish";
|
||||
}
|
||||
|
||||
if (this.cloudflareFileInput) {
|
||||
this.cloudflareFileInput.disabled = Boolean(isUploading);
|
||||
}
|
||||
|
||||
if (this.cloudflareEnableCommentsInput) {
|
||||
this.cloudflareEnableCommentsInput.disabled = Boolean(isUploading);
|
||||
}
|
||||
}
|
||||
|
||||
fillCloudflareSettingsInputs(settings) {
|
||||
const data = settings || {};
|
||||
|
||||
if (this.r2AccountIdInput) {
|
||||
this.r2AccountIdInput.value = data.accountId || "";
|
||||
}
|
||||
if (this.r2AccessKeyIdInput) {
|
||||
this.r2AccessKeyIdInput.value = data.accessKeyId || "";
|
||||
}
|
||||
if (this.r2SecretAccessKeyInput) {
|
||||
this.r2SecretAccessKeyInput.value = data.secretAccessKey || "";
|
||||
}
|
||||
if (this.r2ApiTokenInput) {
|
||||
this.r2ApiTokenInput.value = data.apiToken || "";
|
||||
}
|
||||
if (this.r2ZoneIdInput) {
|
||||
this.r2ZoneIdInput.value = data.zoneId || "";
|
||||
}
|
||||
if (this.r2BaseDomainInput) {
|
||||
this.r2BaseDomainInput.value = data.baseDomain || "";
|
||||
}
|
||||
}
|
||||
|
||||
renderCloudflareBucketPreview({ text = "", title = "" } = {}) {
|
||||
if (!this.cloudflareBucketPreview) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cloudflareBucketPreview.textContent = text || "";
|
||||
if (title) {
|
||||
this.cloudflareBucketPreview.setAttribute("title", title);
|
||||
} else {
|
||||
this.cloudflareBucketPreview.removeAttribute("title");
|
||||
}
|
||||
}
|
||||
|
||||
async refreshCloudflareBucketPreview() {
|
||||
if (!this.r2Service?.updateCloudflareBucketPreview) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pubkey =
|
||||
(this.getCurrentPubkey && this.getCurrentPubkey()) ||
|
||||
(this.authService?.getActivePubkey?.() ?? null);
|
||||
const hasPubkey = Boolean(pubkey);
|
||||
const npub = hasPubkey ? this.safeEncodeNpub(pubkey) : "";
|
||||
|
||||
await this.r2Service.updateCloudflareBucketPreview({ hasPubkey, npub });
|
||||
}
|
||||
|
||||
updateCloudflareProgress(fraction) {
|
||||
if (!this.cloudflareProgressBar || !this.cloudflareProgressFill) {
|
||||
this.emit("upload:r2-progress", { fraction: null });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Number.isFinite(fraction) || fraction < 0) {
|
||||
this.cloudflareProgressBar.classList.add("hidden");
|
||||
this.cloudflareProgressBar.setAttribute("aria-hidden", "true");
|
||||
this.cloudflareProgressFill.style.width = "0%";
|
||||
this.cloudflareProgressFill.setAttribute("aria-valuenow", "0");
|
||||
this.emit("upload:r2-progress", { fraction: null });
|
||||
return;
|
||||
}
|
||||
|
||||
const clamped = Math.max(0, Math.min(1, fraction));
|
||||
const percent = Math.round(clamped * 100);
|
||||
|
||||
this.cloudflareProgressBar.classList.remove("hidden");
|
||||
this.cloudflareProgressBar.setAttribute("aria-hidden", "false");
|
||||
this.cloudflareProgressFill.style.width = `${percent}%`;
|
||||
this.cloudflareProgressFill.setAttribute("aria-valuenow", `${percent}`);
|
||||
this.emit("upload:r2-progress", { fraction: clamped });
|
||||
}
|
||||
|
||||
resetCloudflareUploadForm() {
|
||||
if (this.cloudflareTitleInput) this.cloudflareTitleInput.value = "";
|
||||
if (this.cloudflareDescriptionInput) this.cloudflareDescriptionInput.value = "";
|
||||
if (this.cloudflareThumbnailInput) this.cloudflareThumbnailInput.value = "";
|
||||
if (this.cloudflareMagnetInput) this.cloudflareMagnetInput.value = "";
|
||||
if (this.cloudflareWsInput) this.cloudflareWsInput.value = "";
|
||||
if (this.cloudflareXsInput) this.cloudflareXsInput.value = "";
|
||||
if (this.cloudflareEnableCommentsInput)
|
||||
this.cloudflareEnableCommentsInput.checked = true;
|
||||
if (this.cloudflareFileInput) this.cloudflareFileInput.value = "";
|
||||
this.updateCloudflareProgress(Number.NaN);
|
||||
}
|
||||
|
||||
resetCustomForm() {
|
||||
if (this.customFormInputs.title) this.customFormInputs.title.value = "";
|
||||
if (this.customFormInputs.url) this.customFormInputs.url.value = "";
|
||||
if (this.customFormInputs.magnet) this.customFormInputs.magnet.value = "";
|
||||
if (this.customFormInputs.ws) this.customFormInputs.ws.value = "";
|
||||
if (this.customFormInputs.xs) this.customFormInputs.xs.value = "";
|
||||
if (this.customFormInputs.thumbnail) this.customFormInputs.thumbnail.value = "";
|
||||
if (this.customFormInputs.description)
|
||||
this.customFormInputs.description.value = "";
|
||||
if (this.customFormInputs.enableComments)
|
||||
this.customFormInputs.enableComments.checked = true;
|
||||
if (this.customFormInputs.isPrivate)
|
||||
this.customFormInputs.isPrivate.checked = false;
|
||||
}
|
||||
|
||||
async loadR2Settings() {
|
||||
if (!this.r2Service?.loadSettings) {
|
||||
return null;
|
||||
}
|
||||
const settings = await this.r2Service.loadSettings();
|
||||
this.cloudflareSettings = settings;
|
||||
return settings;
|
||||
}
|
||||
|
||||
async handleCloudflareSettingsSubmit() {
|
||||
if (!this.r2Service?.saveSettings) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const formValues = this.collectCloudflareSettingsFormValues();
|
||||
const saved = await this.r2Service.saveSettings(formValues);
|
||||
if (saved) {
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[UploadModal] Failed to save Cloudflare settings", error);
|
||||
}
|
||||
}
|
||||
|
||||
async handleCloudflareClearSettings() {
|
||||
if (!this.r2Service?.clearSettings) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const cleared = await this.r2Service.clearSettings();
|
||||
if (cleared) {
|
||||
await this.refreshCloudflareBucketPreview();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[UploadModal] Failed to clear Cloudflare settings", error);
|
||||
}
|
||||
}
|
||||
|
||||
async handleCloudflareUploadSubmit() {
|
||||
if (!this.r2Service?.uploadVideo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pubkey =
|
||||
(this.getCurrentPubkey && this.getCurrentPubkey()) ||
|
||||
(this.authService?.getActivePubkey?.() ?? null);
|
||||
if (!pubkey) {
|
||||
this.showError("Please login to post a video.");
|
||||
return;
|
||||
}
|
||||
|
||||
const npub = this.safeEncodeNpub(pubkey) || "";
|
||||
const file = this.cloudflareFileInput?.files?.[0] || null;
|
||||
const metadata = {
|
||||
title: this.cloudflareTitleInput?.value?.trim() || "",
|
||||
description: this.cloudflareDescriptionInput?.value?.trim() || "",
|
||||
thumbnail: this.cloudflareThumbnailInput?.value?.trim() || "",
|
||||
magnet: this.cloudflareMagnetInput?.value?.trim() || "",
|
||||
ws: this.cloudflareWsInput?.value?.trim() || "",
|
||||
xs: this.cloudflareXsInput?.value?.trim() || "",
|
||||
enableComments: this.cloudflareEnableCommentsInput
|
||||
? !!this.cloudflareEnableCommentsInput.checked
|
||||
: true,
|
||||
};
|
||||
|
||||
try {
|
||||
await this.r2Service.uploadVideo({
|
||||
npub,
|
||||
file,
|
||||
metadata,
|
||||
settingsInput: this.collectCloudflareSettingsFormValues(),
|
||||
publishVideoNote: (payload, options) =>
|
||||
this.publishVideoNote ? this.publishVideoNote(payload, options) : null,
|
||||
onReset: () => this.resetCloudflareUploadForm(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[UploadModal] Cloudflare upload failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (Array.isArray(this.r2Unsubscribes)) {
|
||||
this.r2Unsubscribes.forEach((unsubscribe) => {
|
||||
try {
|
||||
if (typeof unsubscribe === "function") {
|
||||
unsubscribe();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[UploadModal] Failed to cleanup R2 listener", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.r2Unsubscribes = [];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user