Files
bitvid/components/edit-video-modal.html
thePR0M3TH3ANandClaude Opus 4.8 5466d97544 [ui] Remove NIP-71 authoring sections from upload/edit modals (#58)
bitvid authors kind-30078 and MIRRORS to NIP-71; it never authors NIP-71 directly.
Verified before removing: the My Videos mirror (buildNip71MirrorEvent) derives
everything from the bitvid note itself (duration/dims are auto-captured at upload,
contentWarning comes from the NSFW toggle, imeta is built from the video), and the
only consumer of the form's NIP-71 metadata (publishNip71Video) is gated behind
FEATURE_PUBLISH_NIP71: false — the fields fed a dead path and confused uploaders.

Removed from the upload modal: "NIP-71 Technical" (content warning, duration,
summary + Customize toggle) and the IMETA variant editor. Removed from the edit
modal: kind (21/22) selector, published_at, alt, duration, content warning,
summary, IMETA, text tracks, segments, participants, references. KEPT in both:
hashtags (they feed hashtag feeds, the suggestion chips, and the mirror's t tags)
and the web seeds / torrent (xs) inputs (bitvid-note fields, not NIP-71). All the
JS plumbing is missing-DOM tolerant, so no wiring changes were needed to collect.

Compatibility: existing videos' NIP-71 extras survive a save. EditModal keeps the
video's full stored metadata (storedNip71Metadata) and merges the edited hashtags
into it on submit instead of replacing it with the reduced form's empty output
(t mirror key kept in sync).

Also fixes a latent pre-existing bug the reduction exposed: nip71Edited compared
the submit-side copy AFTER publishedAt normalization (string→timestamp,
delete-empty) against an UN-normalized original, so any video with publishedAt
made every save look "edited". Both sides now share normalizeCollectedNip71.

Tests: +2 in edit-modal-submit-state (stored extras survive a hashtag edit;
untouched save keeps nip71Edited false). Modal/payload/mirror suites green
(39 + 7). Lint + build green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-02 15:18:10 -04:00

448 lines
16 KiB
HTML

<!-- components/edit-video-modal.html -->
<div
id="editVideoModal"
class="bv-modal hidden items-start md:items-center"
role="dialog"
aria-modal="true"
tabindex="-1"
data-ds="new"
>
<div id="editVideoModalOverlay" class="bv-modal-backdrop"></div>
<div class="bv-modal__panel modal-sheet max-h-modal-shell overflow-y-auto" tabindex="-1">
<div class="modal-header">
<h2 class="text-xl font-bold text-text">Edit Video Note</h2>
<button id="closeEditVideoModal" class="modal-close">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<div class="modal-body space-y-6">
<p class="text-sm text-muted">
Fields that already have values are locked. Click <strong>Edit field</strong> to make changes.
</p>
<form id="editVideoForm" class="space-y-5">
<div class="space-y-1">
<label for="editVideoTitle" class="block text-sm font-medium text-text">
Title
</label>
<div class="flex flex-col gap-2 sm:flex-row">
<input
type="text"
id="editVideoTitle"
class="flex-1 input"
placeholder="Video title"
/>
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoTitle"
>
Edit field
</button>
</div>
</div>
<!-- Hashtags (kept from the old NIP-71 section: they feed bitvid's hashtag
feeds and the NIP-71 mirror; everything else in that section fed the
disabled FEATURE_PUBLISH_NIP71 path and was removed for a cleaner UI.
Existing videos' extra NIP-71 metadata is preserved on save. -->
<div class="space-y-4 rounded-lg border border-border/70 bg-overlay-black/30 p-4">
<section class="space-y-4" aria-labelledby="editNip71HashtagsLabel" data-nip71-repeater="t">
<div class="flex items-center justify-between">
<h3 id="editNip71HashtagsLabel" class="text-sm font-medium text-text">Hashtags</h3>
<button
type="button"
class="btn-ghost focus-ring text-xs font-semibold"
data-nip71-add="t"
>
Add hashtag
</button>
</div>
<p class="text-xs text-muted">
Tag your video so it shows up in hashtag feeds. Leading <code>#</code> characters are stripped automatically.
</p>
<div id="editNip71HashtagList" class="space-y-2" data-nip71-list="t"></div>
<template id="editNip71HashtagTemplate" data-nip71-template="t">
<div class="flex items-center gap-2" data-nip71-entry="t">
<input
type="text"
data-nip71-field="value"
placeholder="#nostr"
class="flex-1 input"
/>
<button
type="button"
class="btn-ghost focus-ring text-3xs font-medium"
data-nip71-remove="t"
>
Remove
</button>
</div>
</template>
</section>
</div>
<div class="space-y-1">
<label for="editVideoUrl" class="block text-sm font-medium text-text">
Hosted video URL (https)
</label>
<div class="flex flex-col gap-2 sm:flex-row">
<input
type="url"
id="editVideoUrl"
class="flex-1 input"
placeholder="https://cdn.example.com/video.mp4"
/>
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoUrl"
>
Edit field
</button>
</div>
<div class="flex items-center gap-2 flex-wrap">
<input type="file" id="editVideoFile" accept="video/*" class="hidden" />
<button
type="button"
id="editVideoFileBtn"
class="btn-ghost focus-ring text-xs"
>
Replace video file…
</button>
<p
id="editVideoUploadStatus"
class="text-2xs text-muted empty:hidden"
role="status"
aria-live="polite"
></p>
</div>
<p class="text-xs text-muted">
Uploading a new file replaces the URL, magnet, and torrent hints.
Requires unlocked storage.
</p>
</div>
<div class="space-y-1">
<label for="editVideoWs" class="block text-sm font-medium text-text">
Web seeds (ws=, one per line)
</label>
<div class="flex flex-col gap-2 sm:flex-row">
<textarea
id="editVideoWs"
rows="2"
class="flex-1 textarea"
placeholder="https://cdn.example.com/video.mp4&#10;https://backup.example.com/video.mp4"
></textarea>
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoWs"
>
Edit field
</button>
</div>
<p class="text-xs text-muted">
Add a backup origin so the swarm keeps streaming if the primary is
overloaded. Each must serve the exact same file.
</p>
</div>
<div class="space-y-1">
<label for="editVideoXs" class="block text-sm font-medium text-text">
.torrent URL (xs=)
</label>
<div class="flex flex-col gap-2 sm:flex-row">
<input
type="url"
id="editVideoXs"
class="flex-1 input"
placeholder="https://cdn.example.com/video.torrent"
/>
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoXs"
>
Edit field
</button>
</div>
</div>
<div class="space-y-1">
<label for="editVideoMagnet" class="block text-sm font-medium text-text">
Magnet link
</label>
<div class="flex flex-col gap-2 sm:flex-row">
<input
type="text"
id="editVideoMagnet"
class="flex-1 input"
placeholder="magnet:?xt=urn:btih:..."
/>
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoMagnet"
>
Edit field
</button>
</div>
</div>
<div class="space-y-1">
<label for="editVideoThumbnail" class="block text-sm font-medium text-text">
Thumbnail URL
</label>
<div class="flex flex-col gap-2 sm:flex-row">
<input
type="url"
id="editVideoThumbnail"
class="flex-1 input"
placeholder="https://cdn.example.com/preview.jpg"
/>
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoThumbnail"
>
Edit field
</button>
</div>
<div class="flex items-center gap-2 flex-wrap">
<input type="file" id="editThumbnailFile" accept="image/*" class="hidden" />
<button
type="button"
id="editThumbnailFileBtn"
class="btn-ghost focus-ring text-xs"
>
Replace thumbnail file…
</button>
<p
id="editThumbnailUploadStatus"
class="text-2xs text-muted empty:hidden"
role="status"
aria-live="polite"
></p>
</div>
</div>
<div class="space-y-1">
<label for="editVideoDescription" class="block text-sm font-medium text-text">
Description
</label>
<div class="flex flex-col gap-2">
<textarea
id="editVideoDescription"
rows="4"
class="textarea"
placeholder="What is this video about?"
></textarea>
<button
type="button"
class="btn-ghost focus-ring hidden self-start text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoDescription"
>
Edit field
</button>
</div>
</div>
<div class="space-y-1">
<span id="editVideoVisibilityLabel" class="block text-sm font-medium text-text">
Visibility
</span>
<div
class="space-y-3 rounded-md border border-border/70 bg-overlay-black/30 p-3"
role="group"
aria-labelledby="editVideoVisibilityLabel"
data-visibility-toggle
>
<div class="flex flex-wrap items-center gap-2 sm:justify-between">
<div
class="video-visibility-toggle"
data-visibility-options
>
<button
type="button"
class="video-visibility-toggle__option focus-ring"
data-visibility-option="public"
aria-pressed="false"
>
Public
</button>
<button
type="button"
class="video-visibility-toggle__option focus-ring"
data-visibility-option="private"
aria-pressed="false"
>
Private
</button>
</div>
<input
type="checkbox"
id="editVideoIsPrivate"
class="hidden"
data-default-checked="false"
aria-hidden="true"
/>
</div>
<p class="text-xs text-muted" data-visibility-helper>
Private notes stay visible only to you. Switch back to public when you're ready to share.
</p>
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoIsPrivate"
>
Edit field
</button>
</div>
</div>
<div class="space-y-2">
<label
for="editVideoIsNsfw"
id="editVideoIsNsfwLabel"
class="block text-sm font-medium text-text"
>
NSFW content
</label>
<div
class="form-control"
role="group"
aria-labelledby="editVideoIsNsfwLabel"
>
<label class="menu__item gap-3 px-0 py-0 text-left" for="editVideoIsNsfw">
<input
type="checkbox"
id="editVideoIsNsfw"
class="checkbox"
data-default-checked="false"
/>
<span class="text-sm text-muted-strong">
Mark as sensitive to blur previews and gate playback.
</span>
</label>
</div>
<div class="flex justify-end">
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoIsNsfw"
>
Edit field
</button>
</div>
</div>
<div class="space-y-2">
<label
for="editVideoIsForKids"
id="editVideoIsForKidsLabel"
class="block text-sm font-medium text-text"
>
Made for kids
</label>
<div
class="form-control"
role="group"
aria-labelledby="editVideoIsForKidsLabel"
>
<label class="menu__item gap-3 px-0 py-0 text-left" for="editVideoIsForKids">
<input
type="checkbox"
id="editVideoIsForKids"
class="checkbox"
data-default-checked="false"
/>
<span class="text-sm text-muted-strong">
Flag kid-friendly videos to adjust recommendations and controls.
</span>
</label>
</div>
<div class="flex justify-end">
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editVideoIsForKids"
>
Edit field
</button>
</div>
</div>
<div class="space-y-2">
<label
for="editEnableComments"
id="editEnableCommentsLabel"
class="block text-sm font-medium text-text"
>
Enable comments
</label>
<div
class="form-control"
role="group"
aria-labelledby="editEnableCommentsLabel"
>
<label class="menu__item gap-3 px-0 py-0 text-left" for="editEnableComments">
<input
type="checkbox"
id="editEnableComments"
class="checkbox"
data-default-checked="true"
checked
/>
<span class="text-sm text-muted-strong">
Allow replies once comment threads are available.
</span>
</label>
</div>
<div class="flex justify-end">
<button
type="button"
class="btn-ghost focus-ring hidden text-xs font-semibold uppercase tracking-wide"
data-edit-target="editEnableComments"
>
Edit field
</button>
</div>
</div>
<div class="flex flex-col gap-3 sm:flex-row sm:justify-end">
<button
type="button"
id="cancelEditVideo"
class="btn-ghost focus-ring w-full sm:w-auto"
>
Cancel
</button>
<button
type="submit"
id="submitEditVideo"
class="btn focus-ring w-full sm:w-auto"
>
Save changes
</button>
</div>
</form>
</div>
</div>
</div>