Add NSFW and kids toggles to edit modal

This commit is contained in:
thePR0M3TH3AN committed 2025-10-09 22:10:46 -04:00
1 parent 4fad4eb1a8
commit dd8b0b9907
3 files changed
+144

No files matched your search

+56
View File
@@ -818,6 +818,62 @@
</div>
</div>
<div class="space-y-1">
<label for="editVideoIsNsfw" class="block text-sm font-medium text-gray-200">
NSFW content
</label>
<div
class="flex flex-col gap-2 rounded-md border border-gray-800 bg-black/30 p-3 sm:flex-row sm:items-center sm:justify-between"
>
<div class="flex items-center gap-3">
<input
type="checkbox"
id="editVideoIsNsfw"
class="h-5 w-5 rounded border-gray-600 bg-gray-800 text-blue-500 focus:ring-blue-500"
data-default-checked="false"
/>
<span class="text-sm text-gray-300">
Mark as sensitive to blur previews and gate playback.
</span>
</div>
<button
type="button"
class="edit-field-button hidden"
data-edit-target="editVideoIsNsfw"
>
Edit field
</button>
</div>
</div>
<div class="space-y-1">
<label for="editVideoIsForKids" class="block text-sm font-medium text-gray-200">
Made for kids
</label>
<div
class="flex flex-col gap-2 rounded-md border border-gray-800 bg-black/30 p-3 sm:flex-row sm:items-center sm:justify-between"
>
<div class="flex items-center gap-3">
<input
type="checkbox"
id="editVideoIsForKids"
class="h-5 w-5 rounded border-gray-600 bg-gray-800 text-blue-500 focus:ring-blue-500"
data-default-checked="false"
/>
<span class="text-sm text-gray-300">
Flag kid-friendly videos to adjust recommendations and controls.
</span>
</div>
<button
type="button"
class="edit-field-button hidden"
data-edit-target="editVideoIsForKids"
>
Edit field
</button>
</div>
</div>
<div class="space-y-1">
<label for="editEnableComments" class="block text-sm font-medium text-gray-200">
Enable comments
+38
View File
@@ -160,6 +160,8 @@ export class EditModal {
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 = {
@@ -320,6 +322,10 @@ 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 =
typeof video.isForKids === "boolean" ? video.isForKids : false;
const editContext = {
...video,
@@ -327,6 +333,8 @@ export class EditModal {
xs: effectiveXs,
enableComments: enableCommentsValue,
isPrivate: isPrivateValue,
isNsfw: isNsfwValue,
isForKids: isForKidsValue,
};
this.applyVideoToForm(editContext);
@@ -363,6 +371,8 @@ export class EditModal {
description: editContext.description || "",
isPrivate: editContext.isPrivate,
enableComments: editContext.enableComments,
isNsfw: editContext.isNsfw,
isForKids: editContext.isForKids,
};
Object.entries(fieldMap).forEach(([key, rawValue]) => {
@@ -464,6 +474,10 @@ export class EditModal {
return "editVideoIsPrivate";
case "enableComments":
return "editEnableComments";
case "isNsfw":
return "editVideoIsNsfw";
case "isForKids":
return "editVideoIsForKids";
default:
return null;
}
@@ -720,6 +734,8 @@ export class EditModal {
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");
@@ -762,6 +778,8 @@ export class EditModal {
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)
@@ -795,6 +813,24 @@ export class EditModal {
}
}
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);
}
}
if (!finalTitle || (!finalUrl && !finalMagnet && !hasImetaSource)) {
this.showError(
"Title and at least one of URL, Magnet, or a NIP-71 media variant is required."
@@ -837,6 +873,8 @@ export class EditModal {
magnetEdited: magnetWasEdited,
enableComments: finalEnableComments,
isPrivate: finalIsPrivate,
isNsfw: finalIsNsfw,
isForKids: finalIsForKids,
};
const currentNip71Json = JSON.stringify(currentNip71);
+50
View File
@@ -104,6 +104,8 @@ test('ignores additional submissions while pending without spurious errors', asy
description: '',
enableComments: true,
isPrivate: false,
isNsfw: true,
isForKids: false,
};
await modal.open(video);
@@ -127,6 +129,19 @@ test('ignores additional submissions while pending without spurious errors', asy
);
assert.equal(modal.submitButton.disabled, true, 'button should disable while pending');
const firstSubmission = submissions[0];
assert.ok(firstSubmission, 'expected first submission payload');
assert.equal(
firstSubmission.updatedData.isNsfw,
true,
'locked NSFW flag should persist in emitted payload',
);
assert.equal(
firstSubmission.updatedData.isForKids,
false,
'locked kids flag should persist in emitted payload',
);
modal.submit();
assert.equal(submissions.length, 1, 'duplicate submit should be ignored');
@@ -140,4 +155,39 @@ test('ignores additional submissions while pending without spurious errors', asy
assert.equal(modal.pendingSubmit, false, 'pending flag should reset after release');
assert.equal(modal.submitButton.disabled, false, 'button should re-enable after release');
const unlockButton = (selector) =>
modal.root.querySelector(selector)?.dispatchEvent(
new dom.window.Event('click', { bubbles: true }),
);
unlockButton('[data-edit-target="editVideoIsNsfw"]');
unlockButton('[data-edit-target="editVideoIsForKids"]');
const nsfwCheckbox = modal.fields.isNsfw;
const kidsCheckbox = modal.fields.isForKids;
assert.ok(nsfwCheckbox, 'expected nsfw checkbox to be cached');
assert.ok(kidsCheckbox, 'expected kids checkbox to be cached');
nsfwCheckbox.checked = false;
kidsCheckbox.checked = true;
modal.submit();
assert.equal(submissions.length, 2, 'unlocked submit should emit again');
const secondSubmission = submissions[1];
assert.ok(secondSubmission, 'expected second submission payload');
assert.equal(
secondSubmission.updatedData.isNsfw,
false,
'updated NSFW flag should reflect unlocked checkbox state',
);
assert.equal(
secondSubmission.updatedData.isForKids,
true,
'updated kids flag should reflect unlocked checkbox state',
);
modal.setSubmitState({ pending: false });
});