Files
bitvid/js/ui/components/EditModal.js
T

976 lines
29 KiB
JavaScript

import { extractMagnetHints, normalizeAndAugmentMagnet } from "../../magnet.js";
import { Nip71FormManager } from "./nip71FormManager.js";
export class EditModal {
constructor({
removeTrackingScripts,
setGlobalModalState,
sanitizers = {},
escapeHtml,
showError,
getMode,
eventTarget,
container,
} = {}) {
this.removeTrackingScripts =
typeof removeTrackingScripts === "function" ? removeTrackingScripts : () => {};
this.setGlobalModalState =
typeof setGlobalModalState === "function" ? setGlobalModalState : () => {};
this.escapeHtml = typeof escapeHtml === "function" ? escapeHtml : (value) => `${value ?? ""}`;
this.showError = typeof showError === "function" ? showError : () => {};
this.getMode = typeof getMode === "function" ? getMode : () => "live";
this.eventTarget = eventTarget instanceof EventTarget ? eventTarget : new EventTarget();
this.container = container || null;
this.sanitizers = {
text:
typeof sanitizers.text === "function"
? sanitizers.text
: (value) => (typeof value === "string" ? value.trim() : ""),
url:
typeof sanitizers.url === "function"
? sanitizers.url
: (value) => (typeof value === "string" ? value.trim() : ""),
magnet:
typeof sanitizers.magnet === "function"
? sanitizers.magnet
: (value) => (typeof value === "string" ? value.trim() : ""),
checkbox:
typeof sanitizers.checkbox === "function"
? sanitizers.checkbox
: (value) => !!value,
};
this.root = null;
this.overlay = null;
this.form = null;
this.closeButton = null;
this.cancelButton = null;
this.submitButton = null;
this.fieldButtons = [];
this.fields = {};
this.visibility = {
container: null,
buttons: [],
};
this.activeVideo = null;
this.isVisible = false;
this.eventsBound = false;
this.loadPromise = null;
this.pendingSubmit = false;
this.pendingSubmitVideo = null;
this.nip71FormManager = new Nip71FormManager();
this.nip71SectionKey = "edit";
this.originalNip71Metadata = null;
this.originalNip71MetadataJson = null;
}
addEventListener(type, listener, options) {
this.eventTarget.addEventListener(type, listener, options);
}
removeEventListener(type, listener, options) {
this.eventTarget.removeEventListener(type, listener, options);
}
emit(type, detail) {
this.eventTarget.dispatchEvent(new CustomEvent(type, { detail }));
}
getRoot() {
return this.root;
}
async load({ container } = {}) {
if (this.root) {
return this.root;
}
if (this.loadPromise) {
return this.loadPromise;
}
this.loadPromise = (async () => {
const targetContainer =
container || this.container || document.getElementById("modalContainer");
if (!targetContainer) {
throw new Error("Modal container element not found!");
}
let modal = targetContainer.querySelector("#editVideoModal");
if (!modal) {
const response = await fetch("components/edit-video-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);
modal = wrapper.querySelector("#editVideoModal");
}
if (!modal) {
throw new Error("Edit video modal markup missing after load.");
}
this.container = targetContainer;
this.root = modal;
this.cacheElements(modal);
if (!this.eventsBound) {
this.bindEvents();
this.eventsBound = true;
}
this.reset();
return this.root;
})();
try {
return await this.loadPromise;
} finally {
this.loadPromise = null;
}
}
cacheElements(context) {
this.overlay = context.querySelector("#editVideoModalOverlay") || null;
this.form = context.querySelector("#editVideoForm") || null;
this.closeButton = context.querySelector("#closeEditVideoModal") || null;
this.cancelButton = context.querySelector("#cancelEditVideo") || null;
this.submitButton = context.querySelector("#submitEditVideo") || null;
this.updateSubmitButtonState();
this.fieldButtons = Array.from(context.querySelectorAll("[data-edit-target]"));
this.fields = {
title: context.querySelector("#editVideoTitle") || null,
url: context.querySelector("#editVideoUrl") || null,
magnet: context.querySelector("#editVideoMagnet") || null,
ws: context.querySelector("#editVideoWs") || null,
xs: context.querySelector("#editVideoXs") || null,
thumbnail: context.querySelector("#editVideoThumbnail") || null,
description: context.querySelector("#editVideoDescription") || null,
isPrivate: context.querySelector("#editVideoIsPrivate") || null,
enableComments: context.querySelector("#editEnableComments") || null,
isNsfw: context.querySelector("#editVideoIsNsfw") || null,
isForKids: context.querySelector("#editVideoIsForKids") || null,
};
this.visibility = {
container: context.querySelector("[data-visibility-toggle]") || null,
buttons: Array.from(
context.querySelectorAll("[data-visibility-option]") || []
),
helper: context.querySelector("[data-visibility-helper]") || null,
helperDefault: "",
};
if (this.visibility.helper) {
this.visibility.helperDefault = this.visibility.helper.textContent || "";
}
const nip71Context = this.form || context;
this.nip71FormManager.registerSection(this.nip71SectionKey, nip71Context);
}
bindEvents() {
if (this.form) {
this.form.addEventListener("submit", (event) => {
event.preventDefault();
this.submit();
});
}
const cancelHandler = (event) => {
event?.preventDefault?.();
this.close({ emitCancel: true });
};
if (this.overlay) {
this.overlay.addEventListener("click", cancelHandler);
}
if (this.closeButton) {
this.closeButton.addEventListener("click", cancelHandler);
}
if (this.cancelButton) {
this.cancelButton.addEventListener("click", cancelHandler);
}
if (Array.isArray(this.fieldButtons)) {
this.fieldButtons.forEach((button) => {
if (!button || button.dataset.editListenerAttached === "true") {
return;
}
button.addEventListener("click", (event) => {
this.handleEditFieldToggle(event);
});
button.dataset.editListenerAttached = "true";
});
}
if (Array.isArray(this.visibility?.buttons)) {
this.visibility.buttons.forEach((button) => {
button.addEventListener("click", (event) => {
const option = event?.currentTarget?.dataset?.visibilityOption;
if (!option) {
return;
}
this.setVisibility(option);
});
});
}
if (this.fields.isPrivate) {
this.fields.isPrivate.addEventListener("change", () => {
this.handleIsPrivateChange({ emit: true });
});
}
if (this.fields.enableComments) {
this.fields.enableComments.addEventListener("change", (event) => {
const target = event.currentTarget;
const value = this.sanitizers.checkbox(target?.checked);
const detail = {
field: "enableComments",
value,
videoId: this.escapeHtml(this.activeVideo?.id || ""),
};
this.emit("video:edit-visibility-change", detail);
});
}
if (this.fields.isNsfw && this.fields.isForKids) {
this.setupMutuallyExclusiveCheckboxes(
this.fields.isNsfw,
this.fields.isForKids
);
}
this.nip71FormManager.bindSection(this.nip71SectionKey);
}
setupMutuallyExclusiveCheckboxes(firstInput, secondInput) {
if (!firstInput || !secondInput) {
return;
}
const enforceExclusion = (primary, secondary) => {
if (primary.checked) {
secondary.checked = false;
}
};
const handleFirstChange = () => enforceExclusion(firstInput, secondInput);
const handleSecondChange = () => enforceExclusion(secondInput, firstInput);
if (firstInput.checked && secondInput.checked) {
secondInput.checked = false;
}
firstInput.addEventListener("change", handleFirstChange);
secondInput.addEventListener("change", handleSecondChange);
}
sanitizeAudienceFlags(flags = {}) {
const isNsfw = flags?.isNsfw === true;
const isForKids = flags?.isForKids === true && !isNsfw;
return { isNsfw, isForKids };
}
reset() {
if (!this.root) {
this.activeVideo = null;
this.nip71FormManager.resetSection(this.nip71SectionKey);
this.originalNip71Metadata = null;
this.originalNip71MetadataJson = null;
this.setSubmitState({ pending: false });
return;
}
const inputs = Object.values(this.fields);
inputs.forEach((input) => {
if (!input) {
return;
}
if (input.type === "checkbox") {
const defaultAttr = input.dataset?.defaultChecked;
const defaultChecked =
defaultAttr === "true"
? true
: defaultAttr === "false"
? false
: input.defaultChecked;
input.checked = defaultChecked;
input.disabled = false;
input.removeAttribute("disabled");
delete input.dataset.isEditing;
} else {
input.value = "";
input.readOnly = false;
input.removeAttribute("readonly");
input.classList.remove("locked-input");
delete input.dataset.isEditing;
}
delete input.dataset.originalValue;
});
if (Array.isArray(this.fieldButtons)) {
this.fieldButtons.forEach((button) => {
button.classList.add("hidden");
button.dataset.mode = "locked";
button.textContent = "Edit field";
});
}
this.handleIsPrivateChange({ emit: false });
this.nip71FormManager.resetSection(this.nip71SectionKey);
this.originalNip71Metadata = null;
this.originalNip71MetadataJson = null;
this.activeVideo = null;
this.setSubmitState({ pending: false });
}
async open(video) {
await this.load();
if (!video) {
throw new Error("No video provided for editing.");
}
this.reset();
const magnetSource = video.magnet || video.rawMagnet || "";
const magnetHints = extractMagnetHints(magnetSource);
const effectiveWs = video.ws || magnetHints.ws || "";
const effectiveXs = video.xs || magnetHints.xs || "";
const enableCommentsValue =
typeof video.enableComments === "boolean" ? video.enableComments : true;
const isPrivateValue = video.isPrivate === true;
const rawIsNsfw = typeof video.isNsfw === "boolean" ? video.isNsfw : false;
const rawIsForKids =
typeof video.isForKids === "boolean" ? video.isForKids : false;
const { isNsfw: isNsfwValue, isForKids: isForKidsValue } =
this.sanitizeAudienceFlags({
isNsfw: rawIsNsfw,
isForKids: rawIsForKids,
});
const editContext = {
...video,
ws: effectiveWs,
xs: effectiveXs,
enableComments: enableCommentsValue,
isPrivate: isPrivateValue,
isNsfw: isNsfwValue,
isForKids: isForKidsValue,
};
this.applyVideoToForm(editContext);
this.nip71FormManager.resetSection(this.nip71SectionKey);
if (video.nip71 && typeof video.nip71 === "object") {
this.nip71FormManager.hydrateSection(this.nip71SectionKey, video.nip71);
}
const initialNip71 = this.nip71FormManager.collectSection(this.nip71SectionKey);
this.originalNip71Metadata = this.cloneNip71Metadata(initialNip71);
this.originalNip71MetadataJson = JSON.stringify(this.originalNip71Metadata);
this.activeVideo = editContext;
if (this.root) {
this.root.classList.remove("hidden");
this.isVisible = true;
this.setGlobalModalState("editVideo", true);
}
return true;
}
applyVideoToForm(editContext) {
if (!editContext) {
return;
}
const sanitizedFlags = this.sanitizeAudienceFlags({
isNsfw: editContext.isNsfw,
isForKids: editContext.isForKids,
});
editContext.isNsfw = sanitizedFlags.isNsfw;
editContext.isForKids = sanitizedFlags.isForKids;
const fieldMap = {
title: editContext.title || "",
url: editContext.url || "",
magnet: editContext.magnet || "",
ws: editContext.ws || "",
xs: editContext.xs || "",
thumbnail: editContext.thumbnail || "",
description: editContext.description || "",
isPrivate: editContext.isPrivate,
enableComments: editContext.enableComments,
isNsfw: editContext.isNsfw,
isForKids: editContext.isForKids,
};
Object.entries(fieldMap).forEach(([key, rawValue]) => {
const input = this.fields[key];
const targetId = this.fieldIdForKey(key);
const button = this.fieldButtons.find(
(item) => item?.dataset?.editTarget === targetId
);
if (!input) {
if (button) {
button.classList.add("hidden");
button.dataset.mode = "locked";
button.textContent = "Edit field";
}
return;
}
if (targetId) {
input.id = targetId;
}
const isCheckbox = input.type === "checkbox";
if (isCheckbox) {
const hasValue = rawValue !== undefined;
const boolValue = rawValue === true;
input.checked = boolValue;
input.disabled = hasValue;
if (hasValue) {
input.setAttribute("disabled", "disabled");
} else {
input.removeAttribute("disabled");
}
input.dataset.originalValue = boolValue ? "true" : "false";
input.dataset.isEditing = hasValue ? "false" : "true";
if (button) {
if (hasValue) {
button.classList.remove("hidden");
button.dataset.mode = "locked";
button.textContent = "Edit field";
} else {
button.classList.add("hidden");
button.dataset.mode = "locked";
button.textContent = "Edit field";
}
}
return;
}
const value = typeof rawValue === "string" ? rawValue : "";
const hasValue = value.trim().length > 0;
input.value = value;
input.dataset.originalValue = value;
input.dataset.isEditing = hasValue ? "false" : "true";
if (hasValue) {
input.readOnly = true;
input.setAttribute("readonly", "readonly");
input.classList.add("locked-input");
} else {
input.readOnly = false;
input.removeAttribute("readonly");
input.classList.remove("locked-input");
}
if (button) {
if (hasValue) {
button.classList.remove("hidden");
button.dataset.mode = "locked";
button.textContent = "Edit field";
} else {
button.classList.add("hidden");
button.dataset.mode = "locked";
button.textContent = "Edit field";
}
}
});
this.handleIsPrivateChange({ emit: false });
}
fieldIdForKey(key) {
switch (key) {
case "title":
return "editVideoTitle";
case "url":
return "editVideoUrl";
case "magnet":
return "editVideoMagnet";
case "ws":
return "editVideoWs";
case "xs":
return "editVideoXs";
case "thumbnail":
return "editVideoThumbnail";
case "description":
return "editVideoDescription";
case "isPrivate":
return "editVideoIsPrivate";
case "enableComments":
return "editEnableComments";
case "isNsfw":
return "editVideoIsNsfw";
case "isForKids":
return "editVideoIsForKids";
default:
return null;
}
}
close({ emitCancel = false } = {}) {
if (this.root) {
this.root.classList.add("hidden");
}
this.isVisible = false;
this.setGlobalModalState("editVideo", false);
const cancelledVideo = this.activeVideo;
this.reset();
if (emitCancel) {
this.emit("video:edit-cancel", {
videoId: this.escapeHtml(cancelledVideo?.id || ""),
pubkey: this.escapeHtml(cancelledVideo?.pubkey || ""),
});
}
}
handleEditFieldToggle(event) {
const button = event?.currentTarget;
if (!button) {
return;
}
const targetId = button.dataset?.editTarget;
if (!targetId) {
return;
}
const input = this.root?.querySelector(`#${targetId}`);
if (!input) {
return;
}
const isCheckbox = input.type === "checkbox";
const isLocked = isCheckbox
? input.disabled === true
: input.readOnly === true;
if (isLocked) {
if (isCheckbox) {
input.disabled = false;
input.removeAttribute("disabled");
input.dataset.isEditing = "true";
} else {
input.disabled = false;
input.readOnly = false;
input.removeAttribute("readonly");
input.classList.remove("locked-input");
input.dataset.isEditing = "true";
}
button.dataset.mode = "editing";
button.textContent = "Restore original";
if (!isCheckbox && typeof input.focus === "function") {
input.focus();
if (typeof input.setSelectionRange === "function") {
const length = input.value.length;
try {
input.setSelectionRange(length, length);
} catch (error) {
// Ignore selection errors (e.g. unsupported input types)
}
}
}
if (input === this.fields.isPrivate) {
this.updateVisibilityToggleUI();
}
return;
}
const originalValue = input.dataset?.originalValue || "";
if (isCheckbox) {
input.checked = originalValue === "true";
input.disabled = true;
input.setAttribute("disabled", "disabled");
input.dataset.isEditing = "false";
button.dataset.mode = "locked";
button.textContent = "Edit field";
if (input === this.fields.isPrivate) {
this.updateVisibilityToggleUI();
}
return;
}
input.value = originalValue;
if (originalValue) {
input.readOnly = true;
input.setAttribute("readonly", "readonly");
input.classList.add("locked-input");
input.dataset.isEditing = "false";
button.dataset.mode = "locked";
button.textContent = "Edit field";
} else {
input.readOnly = false;
input.removeAttribute("readonly");
input.classList.remove("locked-input");
input.dataset.isEditing = "true";
button.classList.add("hidden");
button.dataset.mode = "locked";
button.textContent = "Edit field";
}
}
setVisibility(option, { emit = true } = {}) {
const checkbox = this.fields.isPrivate;
if (!checkbox) {
return;
}
const normalized = typeof option === "string" ? option.toLowerCase() : "";
if (normalized !== "public" && normalized !== "private") {
return;
}
if (checkbox.disabled) {
this.updateVisibilityToggleUI();
return;
}
const wantPrivate = normalized === "private";
if (checkbox.checked === wantPrivate) {
this.updateVisibilityToggleUI();
return;
}
checkbox.checked = wantPrivate;
this.handleIsPrivateChange({ emit });
}
updateVisibilityToggleUI() {
const checkbox = this.fields.isPrivate;
const buttons = Array.isArray(this.visibility?.buttons)
? this.visibility.buttons
: [];
if (!checkbox) {
return;
}
const isPrivate = checkbox.checked === true;
const disabled = checkbox.disabled === true;
buttons.forEach((button) => {
if (!button) {
return;
}
const option = button.dataset?.visibilityOption || "";
const normalized = option.toLowerCase();
const isActive =
(normalized === "private" && isPrivate) ||
(normalized === "public" && !isPrivate);
button.dataset.active = isActive ? "true" : "false";
button.setAttribute("aria-pressed", isActive ? "true" : "false");
if (disabled) {
button.classList.add("is-disabled");
button.setAttribute("aria-disabled", "true");
button.tabIndex = -1;
} else {
button.classList.remove("is-disabled");
button.removeAttribute("aria-disabled");
button.tabIndex = 0;
}
});
if (this.visibility?.container) {
this.visibility.container.dataset.state = isPrivate ? "private" : "public";
if (disabled) {
this.visibility.container.dataset.disabled = "true";
} else {
delete this.visibility.container.dataset.disabled;
}
}
const helper = this.visibility?.helper;
if (helper) {
const privateCopy = this.visibility.helperDefault || helper.textContent;
const publicCopy =
"Public notes appear in feeds for everyone who can view your channel.";
helper.textContent = isPrivate ? privateCopy : publicCopy;
}
}
handleIsPrivateChange({ emit = true } = {}) {
const checkbox = this.fields.isPrivate;
if (!checkbox) {
return;
}
this.updateVisibilityToggleUI();
if (!emit) {
return;
}
const value = this.sanitizers.checkbox(checkbox.checked);
const detail = {
field: "isPrivate",
value,
videoId: this.escapeHtml(this.activeVideo?.id || ""),
};
this.emit("video:edit-visibility-change", detail);
}
cloneNip71Metadata(metadata) {
if (metadata == null) {
return null;
}
try {
return JSON.parse(JSON.stringify(metadata));
} catch (error) {
return metadata;
}
}
submit() {
if (this.pendingSubmit) {
return;
}
if (!this.activeVideo || !this.root) {
this.showError("No video selected for editing.");
return;
}
this.setSubmitState({ pending: true, video: this.activeVideo });
const fieldValue = (key) => {
const input = this.fields[key];
if (!input || typeof input.value !== "string") {
return "";
}
if (key === "url" || key === "ws" || key === "xs") {
return this.sanitizers.url(input.value);
}
if (key === "magnet") {
return this.sanitizers.magnet(input.value);
}
return this.sanitizers.text(input.value);
};
const original = this.pendingSubmitVideo || this.activeVideo;
const titleInput = this.fields.title;
const urlInput = this.fields.url;
const magnetInput = this.fields.magnet;
const wsInput = this.fields.ws;
const xsInput = this.fields.xs;
const thumbnailInput = this.fields.thumbnail;
const descriptionInput = this.fields.description;
const commentsInput = this.fields.enableComments;
const privateInput = this.fields.isPrivate;
const nsfwInput = this.fields.isNsfw;
const forKidsInput = this.fields.isForKids;
const newTitle = fieldValue("title");
const newUrl = fieldValue("url");
const newMagnet = fieldValue("magnet");
const newWs = fieldValue("ws");
const newXs = fieldValue("xs");
const newThumbnail = fieldValue("thumbnail");
const newDescription = fieldValue("description");
const isEditing = (input) => {
if (!input) {
return true;
}
if (input.dataset?.isEditing === "true") {
return true;
}
if (input.type === "checkbox") {
return input.disabled === false;
}
return input.readOnly === false;
};
const titleWasEdited = isEditing(titleInput);
const urlWasEdited = isEditing(urlInput);
const magnetWasEdited = isEditing(magnetInput);
const finalTitle = titleWasEdited ? newTitle : original.title || "";
const finalUrl = urlWasEdited ? newUrl : original.url || "";
const shouldUseOriginalWs = wsInput ? wsInput.readOnly !== false : true;
const shouldUseOriginalXs = xsInput ? xsInput.readOnly !== false : true;
let finalWs = shouldUseOriginalWs ? original.ws || "" : newWs;
let finalXs = shouldUseOriginalXs ? original.xs || "" : newXs;
let finalMagnet = magnetWasEdited ? newMagnet : original.magnet || "";
const finalThumbnail = isEditing(thumbnailInput)
? newThumbnail
: original.thumbnail || "";
const finalDescription = isEditing(descriptionInput)
? newDescription
: original.description || "";
const originalEnableComments =
typeof original.enableComments === "boolean" ? original.enableComments : true;
const originalIsPrivate = original.isPrivate === true;
const originalIsNsfw = original.isNsfw === true;
const originalIsForKids = original.isForKids === true;
const currentNip71 = this.cloneNip71Metadata(
this.nip71FormManager.collectSection(this.nip71SectionKey)
);
const hasImetaSource = Array.isArray(currentNip71?.imeta)
? currentNip71.imeta.some((variant) => {
if (!variant || typeof variant !== "object") {
return false;
}
const hasUrl = typeof variant.url === "string" && variant.url.trim();
const hasMagnet = typeof variant.x === "string" && variant.x.trim();
return Boolean(hasUrl || hasMagnet);
})
: false;
let finalEnableComments = originalEnableComments;
if (commentsInput) {
if (commentsInput.disabled) {
finalEnableComments = commentsInput.dataset.originalValue === "true";
} else {
finalEnableComments = this.sanitizers.checkbox(commentsInput.checked);
}
}
let finalIsPrivate = originalIsPrivate;
if (privateInput) {
if (privateInput.disabled) {
finalIsPrivate = privateInput.dataset.originalValue === "true";
} else {
finalIsPrivate = this.sanitizers.checkbox(privateInput.checked);
}
}
let finalIsNsfw = originalIsNsfw;
if (nsfwInput) {
if (nsfwInput.disabled) {
finalIsNsfw = nsfwInput.dataset.originalValue === "true";
} else {
finalIsNsfw = this.sanitizers.checkbox(nsfwInput.checked);
}
}
let finalIsForKids = originalIsForKids;
if (forKidsInput) {
if (forKidsInput.disabled) {
finalIsForKids = forKidsInput.dataset.originalValue === "true";
} else {
finalIsForKids = this.sanitizers.checkbox(forKidsInput.checked);
}
}
({ isNsfw: finalIsNsfw, isForKids: finalIsForKids } =
this.sanitizeAudienceFlags({
isNsfw: finalIsNsfw,
isForKids: finalIsForKids,
}));
if (!finalTitle || (!finalUrl && !finalMagnet && !hasImetaSource)) {
this.showError(
"Title and at least one of URL, Magnet, or a NIP-71 media variant is required."
);
return;
}
if (finalUrl && !/^https:\/\//i.test(finalUrl)) {
this.showError("Hosted video URLs must use HTTPS.");
return;
}
if (finalMagnet) {
const normalizedMagnet = normalizeAndAugmentMagnet(finalMagnet, {
ws: finalWs,
xs: finalXs,
});
finalMagnet = normalizedMagnet;
const hints = extractMagnetHints(normalizedMagnet);
finalWs = hints.ws;
finalXs = hints.xs;
} else {
finalWs = "";
finalXs = "";
}
const updatedData = {
version: original.version || 2,
title: finalTitle,
magnet: finalMagnet,
url: finalUrl,
thumbnail: finalThumbnail,
description: finalDescription,
mode: this.getMode(),
ws: finalWs,
xs: finalXs,
wsEdited: !shouldUseOriginalWs,
xsEdited: !shouldUseOriginalXs,
urlEdited: urlWasEdited,
magnetEdited: magnetWasEdited,
enableComments: finalEnableComments,
isPrivate: finalIsPrivate,
isNsfw: finalIsNsfw,
isForKids: finalIsForKids,
};
const currentNip71Json = JSON.stringify(currentNip71);
const nip71Edited = currentNip71Json !== this.originalNip71MetadataJson;
updatedData.nip71 = currentNip71;
updatedData.nip71Edited = nip71Edited;
const originalEvent = {
id: this.sanitizers.text(original.id || ""),
pubkey: this.sanitizers.text(original.pubkey || ""),
videoRootId: this.sanitizers.text(original.videoRootId || ""),
};
this.emit("video:edit-submit", {
originalEvent,
updatedData,
video: { ...original },
});
}
setSubmitState({ pending = false, video } = {}) {
const nextPending = Boolean(pending);
if (nextPending) {
this.pendingSubmitVideo = video || this.activeVideo || this.pendingSubmitVideo;
} else {
this.pendingSubmitVideo = null;
}
this.pendingSubmit = nextPending;
this.updateSubmitButtonState();
}
updateSubmitButtonState() {
if (!this.submitButton) {
return;
}
if (this.pendingSubmit) {
this.submitButton.disabled = true;
this.submitButton.setAttribute("disabled", "disabled");
} else {
this.submitButton.disabled = false;
this.submitButton.removeAttribute("disabled");
}
}
}