mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
1528 lines
63 KiB
HTML
1528 lines
63 KiB
HTML
<!-- components/profile-modal.html -->
|
||
<div
|
||
id="profileModal"
|
||
class="bv-modal hidden items-start"
|
||
role="dialog"
|
||
aria-modal="true"
|
||
aria-labelledby="profileModalTitle"
|
||
tabindex="-1"
|
||
data-ds="new"
|
||
data-mobile-view="menu"
|
||
>
|
||
<div class="bv-modal-backdrop"></div>
|
||
|
||
<div
|
||
class="bv-modal__panel modal-sheet profile-modal"
|
||
tabindex="-1"
|
||
data-mobile-view="menu"
|
||
>
|
||
<div class="profile-modal__content">
|
||
<!-- Modal Header -->
|
||
<div class="modal-header profile-modal__header">
|
||
<div class="profile-modal__header-title">
|
||
<button
|
||
id="profileModalBack"
|
||
type="button"
|
||
class="modal-back btn-ghost focus-ring hidden"
|
||
aria-hidden="true"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
class="h-4 w-4"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M15 19l-7-7 7-7"
|
||
/>
|
||
</svg>
|
||
<span class="text-xs font-medium">Menu</span>
|
||
</button>
|
||
<h2 id="profileModalTitle" class="text-lg font-bold text-text">Profile</h2>
|
||
</div>
|
||
<button id="closeProfileModal" 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>
|
||
|
||
<!-- Modal Body -->
|
||
<div class="profile-modal__body">
|
||
<div
|
||
class="profile-modal__layout"
|
||
data-profile-layout
|
||
data-mobile-view="menu"
|
||
>
|
||
<!-- Left navigation column -->
|
||
<div class="profile-modal__menu" data-profile-mobile-menu aria-hidden="false">
|
||
<nav class="profile-modal__nav" aria-label="Profile sections">
|
||
<button
|
||
id="profileNavAccount"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneAccount"
|
||
aria-selected="true"
|
||
data-state="active"
|
||
>
|
||
Account
|
||
</button>
|
||
<button
|
||
id="profileNavRelays"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneRelays"
|
||
aria-selected="false"
|
||
>
|
||
Relays
|
||
</button>
|
||
<button
|
||
id="profileNavWallet"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneWallet"
|
||
aria-selected="false"
|
||
>
|
||
Wallet
|
||
</button>
|
||
<button
|
||
id="profileNavStorage"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneStorage"
|
||
aria-selected="false"
|
||
>
|
||
Storage
|
||
</button>
|
||
<button
|
||
id="profileNavHashtags"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneHashtags"
|
||
aria-selected="false"
|
||
>
|
||
Hashtags
|
||
</button>
|
||
<button
|
||
id="profileNavSubscriptions"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneSubscriptions"
|
||
aria-selected="false"
|
||
>
|
||
Subscriptions
|
||
</button>
|
||
<button
|
||
id="profileNavFriends"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneFriends"
|
||
aria-selected="false"
|
||
>
|
||
Friends
|
||
</button>
|
||
<button
|
||
id="profileNavBlocked"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneBlocked"
|
||
aria-selected="false"
|
||
>
|
||
Blocked
|
||
</button>
|
||
<button
|
||
id="profileNavMessages"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneMessages"
|
||
aria-selected="false"
|
||
>
|
||
Messages
|
||
</button>
|
||
<button
|
||
id="profileNavHistory"
|
||
type="button"
|
||
class="btn-ghost focus-ring w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneHistory"
|
||
aria-selected="false"
|
||
>
|
||
Watch history
|
||
</button>
|
||
<button
|
||
id="profileNavAdmin"
|
||
type="button"
|
||
class="btn-ghost focus-ring hidden w-full justify-start gap-2 px-3 py-2 text-xs font-medium"
|
||
aria-controls="profilePaneAdmin"
|
||
aria-selected="false"
|
||
>
|
||
Admin (Moderation)
|
||
</button>
|
||
</nav>
|
||
</div>
|
||
|
||
<!-- Right content panes -->
|
||
<div
|
||
class="profile-modal__pane-wrapper"
|
||
data-profile-mobile-pane
|
||
aria-hidden="true"
|
||
>
|
||
<div id="profileModalPanes" class="profile-modal__panes">
|
||
<section
|
||
id="profilePaneAccount"
|
||
class="profile-pane space-y-6"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavAccount"
|
||
>
|
||
<!--
|
||
The account pane now relies on bespoke CSS (see css/tailwind.source.css under
|
||
the "profile modal" section) so we are not dependent on utility
|
||
classes that may be purged or overridden by other builds.
|
||
-->
|
||
<div id="profileSwitcherTray" class="card space-y-5 p-5">
|
||
<div class="flex flex-wrap items-start gap-4">
|
||
<div class="space-y-2">
|
||
<h3 class="text-base font-semibold text-text">Switch profiles</h3>
|
||
<p class="text-sm text-muted">
|
||
Save multiple Nostr profiles so you can swap identities without leaving bitvid.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
class="card profile-modal__active-profile"
|
||
aria-label="Current Nostr profile"
|
||
aria-pressed="true"
|
||
role="button"
|
||
tabindex="0"
|
||
data-state="active"
|
||
>
|
||
<span
|
||
class="flex h-12 w-12 flex-shrink-0 items-center justify-center overflow-hidden rounded-full border-2 border-info/50 bg-surface"
|
||
>
|
||
<img
|
||
id="profileModalAvatar"
|
||
src="assets/svg/default-profile.svg"
|
||
alt="Active profile avatar"
|
||
class="h-full w-full object-cover"
|
||
/>
|
||
</span>
|
||
<div class="profile-modal__active-details">
|
||
<div class="profile-modal__active-meta">
|
||
<span class="text-3xs font-semibold uppercase tracking-extra-wide text-info">
|
||
Active profile
|
||
</span>
|
||
</div>
|
||
<span id="profileModalName" class="truncate text-sm font-semibold text-text">Loading...</span>
|
||
<span id="profileModalNpub" class="break-all font-mono text-xs text-muted">npub1...</span>
|
||
</div>
|
||
<div class="flex flex-wrap items-center gap-2 profile-modal__actions">
|
||
<a
|
||
id="profileChannelLink"
|
||
href="#"
|
||
class="btn-ghost focus-ring hidden text-xs"
|
||
>
|
||
View channel
|
||
</a>
|
||
<button
|
||
id="profileEditBtn"
|
||
type="button"
|
||
class="btn-ghost focus-ring text-xs"
|
||
>
|
||
Edit profile
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
id="profileSwitcherList"
|
||
class="grid-stack"
|
||
aria-live="polite"
|
||
aria-label="Additional Nostr profiles"
|
||
role="list"
|
||
data-orientation="vertical"
|
||
></div>
|
||
|
||
<button
|
||
type="button"
|
||
id="profileAddAccountBtn"
|
||
class="profile-modal__add-profile"
|
||
aria-label="Add a new Nostr profile"
|
||
>
|
||
<span class="profile-modal__add-profile-icon" aria-hidden="true">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
stroke-width="2"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
>
|
||
<line x1="12" y1="5" x2="12" y2="19" />
|
||
<line x1="5" y1="12" x2="19" y2="12" />
|
||
</svg>
|
||
</span>
|
||
<span class="flex flex-col text-left">
|
||
<span data-profile-add-title class="text-sm font-semibold text-text">Add profile</span>
|
||
<span data-profile-add-hint class="text-xs text-muted-strong">Link another npub</span>
|
||
</span>
|
||
</button>
|
||
|
||
<p class="text-sm text-muted">
|
||
Profiles you add here will stay on this device so you can quickly switch before posting or moderating.
|
||
</p>
|
||
</div>
|
||
|
||
<div id="profileModerationSettings" class="card space-y-5 p-5">
|
||
<div class="space-y-2">
|
||
<h3 class="text-base font-semibold text-text">Safety & Moderation</h3>
|
||
<p class="text-sm text-muted">
|
||
Choose how many trusted reports or mutes it takes to blur thumbnails, block autoplay previews, or hide videos entirely.
|
||
Lower numbers are stricter.
|
||
</p>
|
||
<div class="rounded-lg border border-border bg-bg-muted p-3 text-xs text-muted">
|
||
<p>
|
||
Trusted counts include your first-degree follows (F1) plus seeded moderators.
|
||
</p>
|
||
<dl class="mt-2 grid gap-1">
|
||
<div class="flex items-center justify-between gap-3">
|
||
<dt>Trusted contacts</dt>
|
||
<dd id="profileModerationTrustedContactsCount" class="font-semibold text-text">0</dd>
|
||
</div>
|
||
<div class="flex items-center justify-between gap-3">
|
||
<dt>Active trusted mute contributors</dt>
|
||
<dd id="profileModerationTrustedMuteCount" class="font-semibold text-text">0</dd>
|
||
</div>
|
||
<div class="flex items-center justify-between gap-3">
|
||
<dt>Active trusted report contributors</dt>
|
||
<dd id="profileModerationTrustedReportCount" class="font-semibold text-text">0</dd>
|
||
</div>
|
||
</dl>
|
||
<p
|
||
id="profileModerationSeedOnlyIndicator"
|
||
class="mt-2 flex items-center gap-1 text-xs text-warning-strong"
|
||
hidden
|
||
>
|
||
<span aria-hidden="true">●</span>
|
||
<span>Seeded moderators only</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="grid gap-4 sm:grid-cols-2">
|
||
<label
|
||
class="block text-sm text-muted-strong"
|
||
for="profileModerationBlurThreshold"
|
||
>
|
||
<span class="mb-2 block font-medium">Blur after trusted reports</span>
|
||
<input
|
||
id="profileModerationBlurThreshold"
|
||
type="number"
|
||
inputmode="numeric"
|
||
min="0"
|
||
step="1"
|
||
class="input"
|
||
/>
|
||
<span class="mt-1 block text-xs text-muted">
|
||
Thumbnails blur when trusted contacts report nudity at or above this count.
|
||
</span>
|
||
</label>
|
||
<label
|
||
class="block text-sm text-muted-strong"
|
||
for="profileModerationAutoplayThreshold"
|
||
>
|
||
<span class="mb-2 block font-medium">Block autoplay after reports</span>
|
||
<input
|
||
id="profileModerationAutoplayThreshold"
|
||
type="number"
|
||
inputmode="numeric"
|
||
min="0"
|
||
step="1"
|
||
class="input"
|
||
/>
|
||
<span class="mt-1 block text-xs text-muted">
|
||
Autoplay previews pause once trusted reports meet this threshold.
|
||
</span>
|
||
</label>
|
||
</div>
|
||
<div
|
||
class="grid gap-4 sm:grid-cols-2"
|
||
data-role="trusted-hide-controls"
|
||
>
|
||
<label
|
||
class="block text-sm text-muted-strong"
|
||
for="profileModerationMuteHideThreshold"
|
||
data-role="trusted-hide-control"
|
||
>
|
||
<span class="mb-2 block font-medium">Hide after trusted mutes</span>
|
||
<input
|
||
id="profileModerationMuteHideThreshold"
|
||
type="number"
|
||
inputmode="numeric"
|
||
min="0"
|
||
step="1"
|
||
class="input"
|
||
/>
|
||
<span class="mt-1 block text-xs text-muted">
|
||
Hide videos when this many trusted contacts add the author to their mute list.
|
||
</span>
|
||
</label>
|
||
<label
|
||
class="block text-sm text-muted-strong"
|
||
for="profileModerationSpamHideThreshold"
|
||
data-role="trusted-hide-control"
|
||
>
|
||
<span class="mb-2 block font-medium">Hide after trusted spam reports</span>
|
||
<input
|
||
id="profileModerationSpamHideThreshold"
|
||
type="number"
|
||
inputmode="numeric"
|
||
min="0"
|
||
step="1"
|
||
class="input"
|
||
/>
|
||
<span class="mt-1 block text-xs text-muted">
|
||
Hide videos entirely once trusted contacts report spam at or above this count.
|
||
</span>
|
||
</label>
|
||
</div>
|
||
<div class="flex flex-wrap items-center gap-3">
|
||
<button
|
||
id="profileModerationSave"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
disabled
|
||
>
|
||
Save moderation settings
|
||
</button>
|
||
<button
|
||
id="profileModerationReset"
|
||
type="button"
|
||
class="btn-ghost focus-ring"
|
||
>
|
||
Restore defaults
|
||
</button>
|
||
</div>
|
||
<p
|
||
id="profileModerationStatus"
|
||
class="text-sm text-muted"
|
||
role="status"
|
||
aria-live="polite"
|
||
></p>
|
||
<div class="space-y-3 border-t border-border/60 pt-4">
|
||
<div class="space-y-1">
|
||
<h4 class="text-sm font-semibold text-text">Active overrides</h4>
|
||
<p class="text-xs text-muted">
|
||
Videos you unblocked with “Show anyway” stay listed here until reset.
|
||
</p>
|
||
</div>
|
||
<div
|
||
id="profileModerationOverridesEmpty"
|
||
class="card border border-dashed border-border/60 p-4 text-center text-xs text-muted"
|
||
>
|
||
No moderation overrides yet.
|
||
</div>
|
||
<ul
|
||
id="profileModerationOverridesList"
|
||
class="grid-stack hidden"
|
||
data-orientation="vertical"
|
||
></ul>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneEdit"
|
||
class="profile-pane hidden space-y-6"
|
||
role="tabpanel"
|
||
aria-label="Edit profile"
|
||
>
|
||
<div class="flex items-center gap-2 mb-4">
|
||
<button
|
||
id="profileEditBackBtn"
|
||
type="button"
|
||
class="btn-ghost focus-ring p-1 -ml-1 text-muted hover:text-text"
|
||
aria-label="Back to account"
|
||
>
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||
</svg>
|
||
</button>
|
||
<h3 class="text-lg font-bold text-text">Edit Profile</h3>
|
||
</div>
|
||
|
||
<div class="space-y-5">
|
||
<!-- Basic Info -->
|
||
<div class="grid gap-4 sm:grid-cols-2">
|
||
<label class="block text-sm text-muted-strong" for="editNameInput">
|
||
<span class="mb-2 block font-medium">Username</span>
|
||
<input
|
||
id="editNameInput"
|
||
type="text"
|
||
class="input"
|
||
placeholder="Name"
|
||
/>
|
||
</label>
|
||
<label class="block text-sm text-muted-strong" for="editDisplayNameInput">
|
||
<span class="mb-2 block font-medium">Display Name</span>
|
||
<input
|
||
id="editDisplayNameInput"
|
||
type="text"
|
||
class="input"
|
||
placeholder="Display Name (optional)"
|
||
/>
|
||
</label>
|
||
</div>
|
||
|
||
<label class="block text-sm text-muted-strong" for="editAboutInput">
|
||
<span class="mb-2 block font-medium">About</span>
|
||
<textarea
|
||
id="editAboutInput"
|
||
class="input min-h-[var(--size-80px)]"
|
||
placeholder="Tell us about yourself..."
|
||
></textarea>
|
||
</label>
|
||
|
||
<label class="block text-sm text-muted-strong" for="editWebsiteInput">
|
||
<span class="mb-2 block font-medium">Website</span>
|
||
<input
|
||
id="editWebsiteInput"
|
||
type="url"
|
||
class="input"
|
||
placeholder="https://example.com"
|
||
/>
|
||
</label>
|
||
|
||
<!-- Verifications -->
|
||
<div class="grid gap-4 sm:grid-cols-2">
|
||
<label class="block text-sm text-muted-strong" for="editNip05Input">
|
||
<span class="mb-2 block font-medium">NIP-05 Verification</span>
|
||
<input
|
||
id="editNip05Input"
|
||
type="text"
|
||
class="input"
|
||
placeholder="[email protected]"
|
||
/>
|
||
</label>
|
||
<label class="block text-sm text-muted-strong" for="editLud16Input">
|
||
<span class="mb-2 block font-medium">Lightning Address (LUD16)</span>
|
||
<input
|
||
id="editLud16Input"
|
||
type="text"
|
||
class="input"
|
||
placeholder="[email protected]"
|
||
/>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Images -->
|
||
<div class="space-y-4">
|
||
<div class="flex flex-col gap-2">
|
||
<label class="block text-sm text-muted-strong" for="editPictureInput">
|
||
<span class="mb-2 block font-medium">Profile Picture URL</span>
|
||
</label>
|
||
<div class="flex gap-2">
|
||
<input
|
||
id="editPictureInput"
|
||
type="url"
|
||
class="input flex-1"
|
||
placeholder="https://..."
|
||
/>
|
||
<input type="file" id="editPictureFile" hidden accept="image/*" />
|
||
<button
|
||
id="editPictureUploadBtn"
|
||
type="button"
|
||
class="btn-ghost focus-ring"
|
||
title="Upload profile picture"
|
||
>
|
||
Upload
|
||
</button>
|
||
</div>
|
||
<div id="editPictureStorageHint" class="text-xs text-muted hidden">
|
||
<a href="#" id="editPictureConfigureLink" class="underline hover:text-text">Configure storage</a> to enable uploads.
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex flex-col gap-2">
|
||
<label class="block text-sm text-muted-strong" for="editBannerInput">
|
||
<span class="mb-2 block font-medium">Banner Image URL</span>
|
||
</label>
|
||
<div class="flex gap-2">
|
||
<input
|
||
id="editBannerInput"
|
||
type="url"
|
||
class="input flex-1"
|
||
placeholder="https://..."
|
||
/>
|
||
<input type="file" id="editBannerFile" hidden accept="image/*" />
|
||
<button
|
||
id="editBannerUploadBtn"
|
||
type="button"
|
||
class="btn-ghost focus-ring"
|
||
title="Upload banner image"
|
||
>
|
||
Upload
|
||
</button>
|
||
</div>
|
||
<div id="editBannerStorageHint" class="text-xs text-muted hidden">
|
||
<a href="#" id="editBannerConfigureLink" class="underline hover:text-text">Configure storage</a> to enable uploads.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Actions -->
|
||
<div class="flex flex-wrap items-center gap-3 pt-4 border-t border-border/60">
|
||
<button
|
||
id="editSaveBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Save Profile
|
||
</button>
|
||
<button
|
||
id="editCancelBtn"
|
||
type="button"
|
||
class="btn-ghost focus-ring"
|
||
>
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
<p id="editStatusText" class="text-sm text-muted" role="status" aria-live="polite"></p>
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneRelays"
|
||
class="profile-pane hidden space-y-6"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavRelays"
|
||
>
|
||
<div class="space-y-4">
|
||
<p class="text-sm text-muted">
|
||
Configure the relays bitvid will use to discover and publish your videos.
|
||
</p>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label class="flex-1 text-sm text-muted-strong" for="relayInput">
|
||
<span class="mb-2 block font-medium">Add a relay</span>
|
||
<input
|
||
id="relayInput"
|
||
type="url"
|
||
placeholder="wss://relay.example.com"
|
||
class="input"
|
||
/>
|
||
</label>
|
||
<div class="flex gap-2">
|
||
<button
|
||
id="addRelayBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Add relay
|
||
</button>
|
||
<button
|
||
id="restoreRelaysBtn"
|
||
type="button"
|
||
class="btn-ghost focus-ring"
|
||
>
|
||
Restore defaults
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<ul id="relayList" class="grid-stack" data-orientation="vertical"></ul>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneWallet"
|
||
class="profile-pane hidden space-y-6"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavWallet"
|
||
>
|
||
<div class="space-y-4">
|
||
<p class="text-sm text-muted">
|
||
Connect a Nostr Wallet Connect (NWC) compatible wallet to zap creators directly from bitvid.
|
||
</p>
|
||
<div class="space-y-4">
|
||
<label class="block text-sm text-muted-strong" for="profileWalletUri">
|
||
<span class="mb-2 block font-medium">Wallet Connect URI</span>
|
||
<input
|
||
id="profileWalletUri"
|
||
type="url"
|
||
placeholder="nostr+walletconnect://<public-key>?relay=wss://relay.example.com"
|
||
class="input"
|
||
/>
|
||
</label>
|
||
<label class="block text-sm text-muted-strong" for="profileWalletDefaultZap">
|
||
<span class="mb-2 block font-medium">Default zap amount (sats)</span>
|
||
<input
|
||
id="profileWalletDefaultZap"
|
||
type="number"
|
||
inputmode="numeric"
|
||
min="0"
|
||
step="1"
|
||
placeholder="500"
|
||
class="input"
|
||
/>
|
||
</label>
|
||
</div>
|
||
<div class="flex flex-wrap items-center gap-3">
|
||
<button
|
||
id="profileWalletSave"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Save wallet
|
||
</button>
|
||
<button
|
||
id="profileWalletTest"
|
||
type="button"
|
||
class="btn-ghost focus-ring"
|
||
>
|
||
Test connection
|
||
</button>
|
||
<button
|
||
id="profileWalletDisconnect"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
data-variant="danger"
|
||
>
|
||
Disconnect wallet
|
||
</button>
|
||
</div>
|
||
<p
|
||
id="profileWalletStatus"
|
||
class="text-sm text-muted"
|
||
role="status"
|
||
aria-live="polite"
|
||
></p>
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneStorage"
|
||
class="profile-pane hidden space-y-6"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavStorage"
|
||
>
|
||
<div class="space-y-4">
|
||
<div class="flex items-center justify-between">
|
||
<h3 class="text-base font-semibold text-text">Storage Connections</h3>
|
||
<div id="profileStorageStatus" class="text-xs text-muted"></div>
|
||
</div>
|
||
<p class="text-sm text-muted">
|
||
Configure external storage providers for your uploads. Credentials are encrypted on your device.
|
||
</p>
|
||
|
||
<!-- Unlock UI -->
|
||
<div id="profileStorageUnlock" class="card space-y-4 p-5 hidden">
|
||
<p class="text-sm text-muted">
|
||
Unlock your secure storage to manage connections.
|
||
</p>
|
||
<button id="profileStorageUnlockBtn" type="button" class="btn focus-ring">
|
||
Unlock Storage
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Connection Form -->
|
||
<div id="profileStorageForm" class="space-y-5 hidden">
|
||
<div class="grid gap-4">
|
||
<label class="block text-sm text-muted-strong" for="storageProvider">
|
||
<span class="mb-2 block font-medium">Provider</span>
|
||
<select id="storageProvider" class="input">
|
||
<option value="cloudflare_r2" selected>Cloudflare R2</option>
|
||
<option value="aws_s3" disabled>S3 Compatible (Coming Soon)</option>
|
||
</select>
|
||
</label>
|
||
|
||
<!-- R2 Helper -->
|
||
<div id="storageR2Helper" class="card border border-info/30 bg-info/10 p-3 text-xs text-info">
|
||
Enter your Cloudflare R2 credentials. You can find these in your Cloudflare dashboard under R2 > Manage R2 API Tokens. Ensure the token has "Object Read & Write" permissions.
|
||
</div>
|
||
|
||
<label class="block text-sm text-muted-strong hidden" for="storageEndpoint">
|
||
<span class="mb-2 block font-medium">Endpoint</span>
|
||
<input id="storageEndpoint" type="url" class="input" placeholder="https://..." />
|
||
</label>
|
||
|
||
<label class="block text-sm text-muted-strong" for="storageRegion">
|
||
<span class="mb-2 block font-medium">Region</span>
|
||
<input id="storageRegion" type="text" class="input" placeholder="auto" value="auto" />
|
||
</label>
|
||
|
||
<label class="block text-sm text-muted-strong" for="storageAccessKey">
|
||
<span class="mb-2 block font-medium">Access Key ID</span>
|
||
<input id="storageAccessKey" type="text" class="input" />
|
||
</label>
|
||
|
||
<label class="block text-sm text-muted-strong" for="storageSecretKey">
|
||
<span class="mb-2 block font-medium">Secret Access Key</span>
|
||
<input id="storageSecretKey" type="password" class="input" />
|
||
</label>
|
||
|
||
<label class="block text-sm text-muted-strong" for="storageBucket">
|
||
<span class="mb-2 block font-medium">Bucket Name</span>
|
||
<input id="storageBucket" type="text" class="input" />
|
||
</label>
|
||
|
||
<label class="block text-sm text-muted-strong" for="storagePrefix">
|
||
<span class="mb-2 block font-medium">Public URL / Prefix</span>
|
||
<input id="storagePrefix" type="url" class="input" placeholder="https://pub-xxx.r2.dev" />
|
||
<span class="mt-1 block text-xs text-muted">
|
||
The public URL where your files will be accessible.
|
||
</span>
|
||
</label>
|
||
|
||
<label class="flex items-center gap-2 text-sm text-muted-strong">
|
||
<input id="storageDefault" type="checkbox" class="checkbox" />
|
||
<span>Use as default for uploads</span>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="flex flex-wrap items-center gap-3">
|
||
<button id="storageSaveBtn" type="button" class="btn focus-ring">
|
||
Save Connection
|
||
</button>
|
||
<button id="storageTestBtn" type="button" class="btn-ghost focus-ring">
|
||
Test Connection
|
||
</button>
|
||
<button id="storageClearBtn" type="button" class="btn-ghost focus-ring text-status-danger">
|
||
Clear
|
||
</button>
|
||
</div>
|
||
<p id="storageFormStatus" class="text-sm" role="status" aria-live="polite"></p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneHashtags"
|
||
class="profile-pane hidden space-y-6"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavHashtags"
|
||
>
|
||
<div class="space-y-3">
|
||
<p class="text-sm text-muted">
|
||
Follow or mute hashtags to personalize the videos we show you.
|
||
</p>
|
||
<p
|
||
id="profileHashtagStatus"
|
||
class="text-sm text-muted"
|
||
role="status"
|
||
aria-live="polite"
|
||
></p>
|
||
</div>
|
||
|
||
<div class="card space-y-4 p-5">
|
||
<div class="space-y-1">
|
||
<h3 class="text-base font-semibold text-text">Interests</h3>
|
||
<p class="text-sm text-muted">
|
||
Videos tagged with these hashtags will be highlighted for you.
|
||
</p>
|
||
</div>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label
|
||
class="flex-1 text-sm text-muted-strong"
|
||
for="profileHashtagInterestInput"
|
||
>
|
||
<span class="mb-2 block font-medium">Add a hashtag</span>
|
||
<input
|
||
id="profileHashtagInterestInput"
|
||
type="text"
|
||
inputmode="text"
|
||
autocomplete="off"
|
||
class="input"
|
||
placeholder="nostr"
|
||
/>
|
||
</label>
|
||
<button
|
||
id="profileAddHashtagInterestBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Add interest
|
||
</button>
|
||
</div>
|
||
<p
|
||
id="profileHashtagInterestEmpty"
|
||
class="card border border-dashed border-border/60 p-4 text-center text-sm text-muted"
|
||
>
|
||
You haven’t added any interest hashtags yet.
|
||
</p>
|
||
<ul
|
||
id="profileHashtagInterestList"
|
||
class="profile-hashtag-list"
|
||
data-orientation="horizontal"
|
||
></ul>
|
||
</div>
|
||
|
||
<div class="card space-y-4 p-5">
|
||
<div class="space-y-1">
|
||
<h3 class="text-base font-semibold text-text">Disinterests</h3>
|
||
<p class="text-sm text-muted">
|
||
We’ll de-emphasize or hide videos using these hashtags.
|
||
</p>
|
||
</div>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label
|
||
class="flex-1 text-sm text-muted-strong"
|
||
for="profileHashtagDisinterestInput"
|
||
>
|
||
<span class="mb-2 block font-medium">Add a hashtag</span>
|
||
<input
|
||
id="profileHashtagDisinterestInput"
|
||
type="text"
|
||
inputmode="text"
|
||
autocomplete="off"
|
||
class="input"
|
||
placeholder="spam"
|
||
/>
|
||
</label>
|
||
<button
|
||
id="profileAddHashtagDisinterestBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Add disinterest
|
||
</button>
|
||
</div>
|
||
<p
|
||
id="profileHashtagDisinterestEmpty"
|
||
class="card border border-dashed border-border/60 p-4 text-center text-sm text-muted"
|
||
>
|
||
You haven’t muted any hashtags yet.
|
||
</p>
|
||
<ul
|
||
id="profileHashtagDisinterestList"
|
||
class="profile-hashtag-list"
|
||
data-orientation="horizontal"
|
||
></ul>
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneSubscriptions"
|
||
class="profile-pane hidden space-y-4"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavSubscriptions"
|
||
>
|
||
<p class="text-sm text-muted">
|
||
These are the creators you’ve subscribed to directly in bitvid.
|
||
</p>
|
||
<div
|
||
id="subscriptionsEmpty"
|
||
class="card border border-dashed border-border/60 p-6 text-center text-sm text-muted"
|
||
>
|
||
You haven’t subscribed to any creators yet.
|
||
</div>
|
||
<ul id="subscriptionsList" class="grid-stack" data-orientation="vertical"></ul>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneFriends"
|
||
class="profile-pane hidden space-y-4"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavFriends"
|
||
>
|
||
<p class="text-sm text-muted">
|
||
Add friends to spotlight creators you want to keep up with across bitvid.
|
||
</p>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label class="flex-1 text-sm text-muted-strong" for="friendsInput">
|
||
<span class="mb-2 block font-medium">Creator npub</span>
|
||
<input
|
||
id="friendsInput"
|
||
type="text"
|
||
inputmode="text"
|
||
autocomplete="off"
|
||
class="input"
|
||
placeholder="npub1..."
|
||
/>
|
||
</label>
|
||
<button id="addFriendBtn" type="button" class="btn focus-ring">
|
||
Add friend
|
||
</button>
|
||
</div>
|
||
<div
|
||
id="friendsEmpty"
|
||
class="card border border-dashed border-border/60 p-6 text-center text-sm text-muted"
|
||
>
|
||
You haven’t added any friends yet.
|
||
</div>
|
||
<ul id="friendsList" class="grid-stack" data-orientation="vertical"></ul>
|
||
</section>
|
||
|
||
<section
|
||
id="profilePaneBlocked"
|
||
class="profile-pane hidden space-y-4"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavBlocked"
|
||
>
|
||
<p class="text-sm text-muted">
|
||
Creators you block are hidden from your bitvid experience. This
|
||
list is private to your account.
|
||
</p>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label class="flex-1 text-sm text-muted-strong" for="blockedInput">
|
||
<span class="mb-2 block font-medium">Creator npub</span>
|
||
<input
|
||
id="blockedInput"
|
||
type="text"
|
||
inputmode="text"
|
||
autocomplete="off"
|
||
class="input"
|
||
placeholder="npub1..."
|
||
/>
|
||
</label>
|
||
<button
|
||
id="addBlockedBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Block creator
|
||
</button>
|
||
</div>
|
||
<div
|
||
id="blockedEmpty"
|
||
class="card border border-dashed border-border/60 p-6 text-center text-sm text-muted"
|
||
>
|
||
You haven’t blocked any creators yet.
|
||
</div>
|
||
<ul id="blockedList" class="grid-stack" data-orientation="vertical"></ul>
|
||
</section>
|
||
<section
|
||
id="profilePaneMessages"
|
||
class="profile-pane hidden space-y-4"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavMessages"
|
||
>
|
||
<p class="text-sm text-muted">
|
||
View encrypted conversations sent with NIP-04 or NIP-44. bitvid keeps message data on this device only.
|
||
</p>
|
||
<div class="card space-y-4 p-4">
|
||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||
<div class="space-y-1">
|
||
<h3 class="text-sm font-semibold text-text">Direct message actions</h3>
|
||
<p class="text-xs text-muted">
|
||
Start a new DM or jump to relay preferences before you send.
|
||
</p>
|
||
</div>
|
||
<button
|
||
id="profileMessagesSendDm"
|
||
type="button"
|
||
class="btn focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Send DM
|
||
</button>
|
||
</div>
|
||
<div class="flex flex-wrap items-center justify-between gap-3 border-t border-border/60 pt-3">
|
||
<div class="space-y-1">
|
||
<h4 class="text-xs font-semibold text-text">Recipient DM relays</h4>
|
||
<p class="text-xs text-muted">
|
||
Use the relays advertised in kind 10050 to prioritize encrypted delivery.
|
||
</p>
|
||
</div>
|
||
<button
|
||
id="profileMessagesOpenRelays"
|
||
type="button"
|
||
class="btn-ghost focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Use DM relays
|
||
</button>
|
||
</div>
|
||
<div class="flex flex-wrap items-center justify-between gap-3 border-t border-border/60 pt-3">
|
||
<div class="space-y-1">
|
||
<h4 class="text-xs font-semibold text-text">NIP-17 privacy delivery</h4>
|
||
<p class="text-xs text-muted">
|
||
Send gift-wrapped DMs to the relays your recipient advertises in kind 10050.
|
||
</p>
|
||
</div>
|
||
<label
|
||
for="profileMessagesPrivacyToggle"
|
||
class="inline-flex items-center gap-2 text-xs font-medium text-muted-strong"
|
||
title="NIP-17 gift-wraps your DM so relays only see the wrapper and relay hints, not the plaintext. It requires the recipient to publish kind 10050 relay hints."
|
||
>
|
||
<input
|
||
id="profileMessagesPrivacyToggle"
|
||
type="checkbox"
|
||
class="checkbox"
|
||
/>
|
||
Use NIP-17
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="card space-y-4 p-4">
|
||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||
<div class="space-y-1">
|
||
<h3 class="text-sm font-semibold text-text">Your DM relay hints</h3>
|
||
<p class="text-xs text-muted">
|
||
Publish kind 10050 so contacts can deliver NIP-17 gift-wrapped DMs to you.
|
||
</p>
|
||
</div>
|
||
<button
|
||
id="profileMessagesRelayPublish"
|
||
type="button"
|
||
class="btn focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
title="Publishing kind 10050 makes your DM relay list public so others can deliver NIP-17 messages."
|
||
>
|
||
Publish relays
|
||
</button>
|
||
</div>
|
||
<label class="flex-1 text-sm text-muted-strong" for="profileMessagesRelayInput">
|
||
<span class="mb-2 block font-medium">Add a DM relay</span>
|
||
<div class="flex flex-wrap items-center gap-2">
|
||
<input
|
||
id="profileMessagesRelayInput"
|
||
type="url"
|
||
inputmode="url"
|
||
autocomplete="off"
|
||
class="input flex-1"
|
||
placeholder="wss://relay.example.com"
|
||
/>
|
||
<button
|
||
id="profileMessagesRelayAdd"
|
||
type="button"
|
||
class="btn-ghost focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Add
|
||
</button>
|
||
</div>
|
||
</label>
|
||
<ul
|
||
id="profileMessagesRelayList"
|
||
class="grid-stack"
|
||
data-orientation="vertical"
|
||
aria-live="polite"
|
||
aria-label="Your DM relay hints"
|
||
></ul>
|
||
<p
|
||
id="profileMessagesRelayStatus"
|
||
class="text-xs text-muted"
|
||
role="status"
|
||
aria-live="polite"
|
||
></p>
|
||
</div>
|
||
<div
|
||
id="profileMessagesStatus"
|
||
class="sr-only"
|
||
role="status"
|
||
aria-live="polite"
|
||
></div>
|
||
<div
|
||
id="profileMessagesLoading"
|
||
class="hidden py-6"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
<div class="sidebar-loading-wrapper">
|
||
<div class="sidebar-loading-indicator">
|
||
<span
|
||
class="status-spinner status-spinner--inline"
|
||
aria-hidden="true"
|
||
></span>
|
||
<span class="sidebar-loading-text"
|
||
>Fetching direct messages from relays…</span
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
id="profileMessagesError"
|
||
class="card hidden border border-critical/40 bg-critical/10 px-4 py-3 text-sm text-critical"
|
||
role="alert"
|
||
aria-live="assertive"
|
||
></div>
|
||
<div
|
||
id="profileMessagesEmpty"
|
||
class="card hidden border border-dashed border-border/60 p-6 text-center text-sm text-muted"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
No direct messages yet.
|
||
</div>
|
||
<div class="flex items-center justify-end gap-2">
|
||
<button
|
||
id="profileMessagesReload"
|
||
type="button"
|
||
class="btn-ghost focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Refresh
|
||
</button>
|
||
</div>
|
||
<ul
|
||
id="profileMessagesList"
|
||
class="grid-stack hidden"
|
||
data-orientation="vertical"
|
||
aria-live="polite"
|
||
aria-label="Direct message threads"
|
||
></ul>
|
||
<div
|
||
id="profileMessagesConversation"
|
||
class="hidden flex flex-col gap-3"
|
||
role="log"
|
||
aria-live="polite"
|
||
aria-label="Direct message conversation"
|
||
></div>
|
||
<p
|
||
id="profileMessagesConversationEmpty"
|
||
class="hidden text-sm text-muted"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
Select a thread to view messages and attachments.
|
||
</p>
|
||
<div class="flex flex-col gap-3 border-t border-border/60 pt-4">
|
||
<label class="sr-only" for="profileMessageInput">Message</label>
|
||
<textarea
|
||
id="profileMessageInput"
|
||
class="input min-h-[var(--size-96px)]"
|
||
rows="3"
|
||
placeholder="Write a message…"
|
||
></textarea>
|
||
<div class="flex flex-col gap-2">
|
||
<input
|
||
id="profileMessageAttachmentInput"
|
||
type="file"
|
||
class="sr-only"
|
||
multiple
|
||
/>
|
||
<div class="flex flex-wrap items-center gap-2">
|
||
<button
|
||
id="profileMessageAttachmentButton"
|
||
type="button"
|
||
class="btn-ghost focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Attach file
|
||
</button>
|
||
<label class="inline-flex items-center gap-2 text-xs text-muted">
|
||
<input
|
||
id="profileMessageAttachmentEncrypt"
|
||
type="checkbox"
|
||
class="checkbox"
|
||
/>
|
||
Encrypt attachments
|
||
</label>
|
||
<button
|
||
id="profileMessageAttachmentClearCache"
|
||
type="button"
|
||
class="btn-ghost focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Clear attachment cache
|
||
</button>
|
||
</div>
|
||
<div
|
||
id="profileMessageAttachmentList"
|
||
class="grid-stack"
|
||
data-orientation="vertical"
|
||
></div>
|
||
</div>
|
||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||
<p
|
||
id="profileMessagesComposerHelper"
|
||
class="text-xs text-muted hidden"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
Sign in to send messages.
|
||
</p>
|
||
<p
|
||
id="profileMessagesPrivacyMode"
|
||
class="text-xs text-muted"
|
||
title="NIP-04 sends a direct encrypted DM; NIP-17 gift-wraps messages to reduce metadata exposure but relies on relay hints."
|
||
>
|
||
Privacy: NIP-04
|
||
</p>
|
||
<button
|
||
id="profileMessageSendBtn"
|
||
type="button"
|
||
class="btn focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Send
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section
|
||
id="profilePaneHistory"
|
||
class="profile-pane hidden space-y-4"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavHistory"
|
||
>
|
||
<p class="text-sm text-muted">
|
||
Your recently watched videos appear here so you can jump back into favorites.
|
||
</p>
|
||
<div
|
||
id="profileHistoryFeatureBanner"
|
||
class="card hidden border border-status-warning-border bg-status-warning-surface px-4 py-3 text-sm text-status-warning-on"
|
||
role="status"
|
||
aria-live="polite"
|
||
></div>
|
||
<div class="space-y-4">
|
||
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||
<div
|
||
class="flex flex-wrap gap-2"
|
||
role="group"
|
||
aria-label="Watch history actions"
|
||
>
|
||
<button
|
||
id="profileHistoryClear"
|
||
type="button"
|
||
class="btn-ghost focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Clear local cache
|
||
</button>
|
||
<button
|
||
id="profileHistoryRepublish"
|
||
type="button"
|
||
class="btn focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Republish now
|
||
</button>
|
||
<button
|
||
id="profileHistoryRefresh"
|
||
type="button"
|
||
class="btn-ghost focus-ring inline-flex items-center gap-2"
|
||
data-size="sm"
|
||
>
|
||
Refresh
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div
|
||
id="profileHistoryPrivacyBanner"
|
||
class="card hidden border border-status-info-border bg-status-info-surface px-4 py-3 text-sm text-status-info-on"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
<p id="profileHistoryPrivacyMessage" class="mb-2"></p>
|
||
<div class="flex flex-wrap items-center gap-2">
|
||
<button
|
||
id="profileHistoryPrivacyDismiss"
|
||
type="button"
|
||
class="btn-ghost focus-ring text-xs"
|
||
>
|
||
Dismiss
|
||
</button>
|
||
<button
|
||
id="profileHistoryPrivacyToggle"
|
||
type="button"
|
||
class="btn-ghost focus-ring text-xs hidden"
|
||
>
|
||
Change settings
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<p id="profileHistoryInfo" class="text-xs text-muted"></p>
|
||
<div
|
||
id="profileHistorySessionWarning"
|
||
class="hidden pill"
|
||
data-variant="warning"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
Session actor in use. Connect a NIP-07 extension to publish as your main profile.
|
||
</div>
|
||
<div id="profileHistoryLoading" class="py-6">
|
||
<div id="profileHistoryStatus">
|
||
<div
|
||
class="sidebar-loading-wrapper"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
<div class="sidebar-loading-indicator">
|
||
<span
|
||
class="status-spinner status-spinner--inline"
|
||
aria-hidden="true"
|
||
></span>
|
||
<span class="sidebar-loading-text"
|
||
>Fetching watch history from relays…</span
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
id="profileHistoryError"
|
||
class="card hidden border border-critical/40 bg-critical/10 px-4 py-3 text-sm text-critical"
|
||
role="alert"
|
||
aria-live="assertive"
|
||
></div>
|
||
<div
|
||
id="profileHistoryEmpty"
|
||
class="card hidden border border-dashed border-border/60 p-6 text-center text-sm text-muted"
|
||
>
|
||
You haven’t watched any videos yet.
|
||
</div>
|
||
<div id="profileHistoryScroll" class="relative">
|
||
<div
|
||
id="profileHistoryGrid"
|
||
class="grid-stack hidden"
|
||
data-variant="cards"
|
||
data-orientation="vertical"
|
||
role="list"
|
||
></div>
|
||
<div id="profileHistorySentinel" class="h-1 w-full"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<section
|
||
id="profilePaneAdmin"
|
||
class="profile-pane hidden space-y-6"
|
||
role="tabpanel"
|
||
aria-labelledby="profileNavAdmin"
|
||
>
|
||
<div class="space-y-2">
|
||
<h3 class="text-base font-semibold text-text">Platform moderation</h3>
|
||
<p class="text-sm text-muted">
|
||
Manage bitvid’s moderator roles and creator allow/block lists. Changes apply instantly across the app.
|
||
</p>
|
||
</div>
|
||
|
||
<div id="adminModeratorsSection" class="space-y-4 hidden" aria-hidden="true">
|
||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||
<h4 class="text-base font-semibold text-text">Moderators</h4>
|
||
<p class="text-xs uppercase tracking-wide text-muted">
|
||
Super Admin only
|
||
</p>
|
||
</div>
|
||
<p class="text-sm text-muted">
|
||
Moderators can edit the whitelist and blacklist but cannot manage other moderators or whitelist mode.
|
||
</p>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label class="flex-1 text-sm text-muted-strong" for="adminModeratorInput">
|
||
<span class="mb-2 block font-medium">Moderator npub</span>
|
||
<input
|
||
id="adminModeratorInput"
|
||
type="text"
|
||
inputmode="text"
|
||
autocomplete="off"
|
||
class="input"
|
||
placeholder="npub1..."
|
||
/>
|
||
</label>
|
||
<button
|
||
id="adminAddModeratorBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Add moderator
|
||
</button>
|
||
</div>
|
||
<div
|
||
id="adminModeratorsEmpty"
|
||
class="card border border-dashed border-border/60 p-4 text-center text-sm text-muted"
|
||
>
|
||
No moderators configured yet.
|
||
</div>
|
||
<ul id="adminModeratorList" class="grid-stack" data-orientation="vertical"></ul>
|
||
</div>
|
||
|
||
<div id="adminWhitelistSection" class="space-y-4">
|
||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||
<h4 class="text-base font-semibold text-text">Whitelist</h4>
|
||
</div>
|
||
<p class="text-sm text-muted">
|
||
Creators listed here remain visible when whitelist mode is enabled. Accounts can appear in both lists, and the blacklist always takes precedence.
|
||
</p>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label class="flex-1 text-sm text-muted-strong" for="adminWhitelistInput">
|
||
<span class="mb-2 block font-medium">Creator npub</span>
|
||
<input
|
||
id="adminWhitelistInput"
|
||
type="text"
|
||
inputmode="text"
|
||
autocomplete="off"
|
||
class="input"
|
||
placeholder="npub1..."
|
||
/>
|
||
</label>
|
||
<button
|
||
id="adminAddWhitelistBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Add to whitelist
|
||
</button>
|
||
</div>
|
||
<div
|
||
id="adminWhitelistEmpty"
|
||
class="card border border-dashed border-border/60 p-4 text-center text-sm text-muted"
|
||
>
|
||
No whitelisted creators yet.
|
||
</div>
|
||
<ul id="adminWhitelistList" class="grid-stack" data-orientation="vertical"></ul>
|
||
</div>
|
||
|
||
<div id="adminBlacklistSection" class="space-y-4">
|
||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||
<h4 class="text-base font-semibold text-text">Blacklist</h4>
|
||
</div>
|
||
<p class="text-sm text-muted">
|
||
Creators on the blacklist are hidden from bitvid everywhere. Accounts can appear in both lists, and the blacklist always takes precedence.
|
||
</p>
|
||
<div class="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||
<label class="flex-1 text-sm text-muted-strong" for="adminBlacklistInput">
|
||
<span class="mb-2 block font-medium">Creator npub</span>
|
||
<input
|
||
id="adminBlacklistInput"
|
||
type="text"
|
||
inputmode="text"
|
||
autocomplete="off"
|
||
class="input"
|
||
placeholder="npub1..."
|
||
/>
|
||
</label>
|
||
<button
|
||
id="adminAddBlacklistBtn"
|
||
type="button"
|
||
class="btn focus-ring"
|
||
>
|
||
Add to blacklist
|
||
</button>
|
||
</div>
|
||
<div
|
||
id="adminBlacklistEmpty"
|
||
class="card border border-dashed border-border/60 p-4 text-center text-sm text-muted"
|
||
>
|
||
No blacklisted creators yet.
|
||
</div>
|
||
<ul id="adminBlacklistList" class="grid-stack" data-orientation="vertical"></ul>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal Footer -->
|
||
<div class="flex items-center justify-end gap-3 border-t border-border/60 p-4">
|
||
<button id="profileLogoutBtn" class="btn focus-ring" data-variant="danger">
|
||
Logout
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|