Merge pull request #401 from PR0M3TH3AN/codex/add-version-metadata-display-in-revertmodal

Display audience flags in revert modal
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-09 22:42:27 -04:00
commit e15aff51a4
3 files changed
+133 -4

No files matched your search

+17 -1
View File
@@ -62,7 +62,7 @@
<p class="mt-2 text-xs text-gray-400">
Versions are grouped by their <code class="px-1 py-0.5 bg-gray-900/60 rounded border border-gray-700 text-[0.7rem]">d</code>
tag or shared <code class="px-1 py-0.5 bg-gray-900/60 rounded border border-gray-700 text-[0.7rem]">videoRootId</code>. This ensures legacy
clients see consistent history when multiple edits were published.
clients see consistent history when multiple edits were published, and keeps audience flags (NSFW / for kids) aligned with the Upload modal.
</p>
</div>
@@ -117,6 +117,22 @@
</dl>
</section>
<section class="space-y-2">
<h4 class="text-xs font-semibold uppercase tracking-wide text-gray-400">
Audience flags
</h4>
<dl class="grid gap-2 text-xs text-gray-400 sm:grid-cols-2">
<div>
<dt class="font-semibold text-gray-300">NSFW flag</dt>
<dd class="mt-1 text-gray-500">Awaiting selection</dd>
</div>
<div>
<dt class="font-semibold text-gray-300">For kids flag</dt>
<dd class="mt-1 text-gray-500">Awaiting selection</dd>
</div>
</dl>
</section>
<section class="space-y-2">
<h4 class="text-xs font-semibold uppercase tracking-wide text-gray-400">
NIP-71 event metadata
+55 -3
View File
@@ -249,9 +249,44 @@ export class EditModal {
});
}
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;
@@ -322,10 +357,14 @@ export class EditModal {
const enableCommentsValue =
typeof video.enableComments === "boolean" ? video.enableComments : true;
const isPrivateValue = video.isPrivate === true;
const isNsfwValue =
typeof video.isNsfw === "boolean" ? video.isNsfw : false;
const isForKidsValue =
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,
@@ -361,6 +400,13 @@ export class EditModal {
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 || "",
@@ -831,6 +877,12 @@ export class EditModal {
}
}
({ 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."
+61
View File
@@ -571,6 +571,9 @@ export class RevertModal {
}
const chips = [];
const nsfwFlag = version.isNsfw === true;
const forKidsFlag = version.isForKids === true;
const conflictingAudienceFlags = nsfwFlag && forKidsFlag;
if (version.deleted) {
chips.push(
'<span class="inline-flex items-center rounded-full border border-red-700/70 bg-red-900/40 px-2 py-0.5 text-xs text-red-200/90">Marked deleted</span>'
@@ -581,6 +584,22 @@ export class RevertModal {
'<span class="inline-flex items-center rounded-full border border-purple-600/60 bg-purple-900/40 px-2 py-0.5 text-xs text-purple-200/90">Private</span>'
);
}
if (conflictingAudienceFlags) {
chips.push(
'<span class="inline-flex items-center rounded-full border border-amber-700/70 bg-amber-900/40 px-2 py-0.5 text-xs text-amber-200/90">NSFW + For kids conflict</span>'
);
} else {
if (nsfwFlag) {
chips.push(
'<span class="inline-flex items-center rounded-full border border-red-700/70 bg-red-900/40 px-2 py-0.5 text-xs text-red-200/90">Marked NSFW</span>'
);
}
if (forKidsFlag) {
chips.push(
'<span class="inline-flex items-center rounded-full border border-emerald-700/70 bg-emerald-900/40 px-2 py-0.5 text-xs text-emerald-200/90">For kids</span>'
);
}
}
if (version.version !== undefined) {
chips.push(
`<span class="inline-flex items-center rounded-full border border-gray-700 bg-gray-800/80 px-2 py-0.5 text-xs text-gray-200">Schema v${escape(
@@ -589,6 +608,35 @@ export class RevertModal {
);
}
const renderFlagStatus = (value, {
yesLabel = "Yes",
noLabel = "No",
unspecifiedLabel = "Not specified",
} = {}) => {
if (value === true) {
return `<span class="text-gray-200">${escape(yesLabel)}</span>`;
}
if (value === false) {
return `<span class="text-gray-200">${escape(noLabel)}</span>`;
}
return this.renderPlaceholder(unspecifiedLabel);
};
const nsfwStatusHtml = renderFlagStatus(version.isNsfw, {
yesLabel: "Yes — marked NSFW",
noLabel: "No — not flagged as NSFW",
unspecifiedLabel: "Not specified",
});
const kidsStatusHtml = renderFlagStatus(version.isForKids, {
yesLabel: "Yes — marked for kids",
noLabel: "No — not marked for kids",
unspecifiedLabel: "Not specified",
});
const audienceConflictNotice = conflictingAudienceFlags
? '<p class="text-xs text-amber-300">Conflicting flags detected — this revision is marked both NSFW and for kids. Review before reverting.</p>'
: "";
const descriptionHtml = description
? `<p class="whitespace-pre-wrap text-gray-200">${escape(description)}</p>`
: '<p class="text-gray-500">No description provided.</p>';
@@ -718,6 +766,11 @@ export class RevertModal {
definition("Alt text", altHtml, { span: 2 }),
].join("");
const audienceRows = [
definition("NSFW flag", nsfwStatusHtml),
definition("For kids flag", kidsStatusHtml),
].join("");
this.details.innerHTML = `
<div class="space-y-6">
<div class="flex flex-col gap-4 lg:flex-row lg:items-start">
@@ -759,6 +812,14 @@ export class RevertModal {
${descriptionHtml}
</section>
<section class="space-y-2">
<h4 class="text-sm font-semibold text-gray-200">Audience flags</h4>
<dl class="grid gap-3 sm:grid-cols-2 text-xs text-gray-300">
${audienceRows}
</dl>
${audienceConflictNotice}
</section>
<section class="space-y-2">
<h4 class="text-sm font-semibold text-gray-200">NIP-71 event metadata</h4>
<dl class="grid gap-3 sm:grid-cols-2 text-xs text-gray-300">