Replace the manual thumbnail <img> overlay with the browser-native
poster= attribute on the <video> element. The browser renders the
poster immediately before any video data is fetched, even on cards
with preload=none, eliminating black frames during fast scrolling.
Also changes the card background from pure black to neutral-900 as
a subtle fallback for vines with no thumbnail.
- Remove defaultZapComment config; derive from appName instead
- Add appId (string) for NIP-78 d-tag prefixes (e.g. ${appId}/metadata)
- Add client (optional NIP-89 addr) as third element of client tags
- Update useNostrPublish and useEncryptedSettings to use config.appName
and config.client for client tags instead of location.hostname
- Replace hardcoded 'ditto-metadata' d-tag with ${config.appId}/metadata
- Remove Default Zap Comment setting from AdvancedSettings UI
- Add .vine-slide-height CSS utility that subtracts env(safe-area-inset-top/bottom) so slides don't bleed under the bottom nav on PWA/APK with home indicator
- Replace all hardcoded calc(100dvh-9.5rem) vine heights with the new utility
- Preload active video with 'auto', adjacent (±1) with 'metadata', and far-away cards with 'none' to speed up video playback on swipe without wasting mobile bandwidth
When replying to your own post, the compose box excludes your pubkey
from p tags. This left self-replies with no 'Replying to' context,
making them look like top-level posts. Now falls back to the parent
e-tag author hint or the event author itself so self-replies always
show thread context.
Closes#3
Replace the dual-registry system (BUILTIN_SIDEBAR_ITEMS in useFeedSettings
+ EXTRA_KINDS in extraKinds) with a single SIDEBAR_ITEMS array in
sidebarItems.tsx. Every sidebar-capable item now has a consistent shape:
id, label, path, icon, and optional requiresAuth.
- Delete extraKindIcons.ts; icons now live in the SIDEBAR_ITEMS registry
- Remove BUILTIN_SIDEBAR_ITEMS, isBuiltinItem, getBuiltinItem, EXTRA_KIND_IDS
from useFeedSettings.ts; validation uses SIDEBAR_ITEM_IDS
- Remove HiddenSidebarItem.builtin field (was never read by consumers)
- Simplify SidebarNavItem.tsx (no more dual icon map lookup)
- All consumers now import sidebarItemIcon from @/lib/sidebarItems
- ExtraKindDef/EXTRA_KINDS unchanged (still used for feed/content settings)
Previously, extra-kind items in sidebarOrder were silently dropped
unless their corresponding feedSettings showKey was also enabled.
This made it impossible to add items like 'webxdc' to the sidebar
via build config alone.
Now computeOrderedItems uses sidebarOrder directly — any recognized
item (built-in or extra-kind with a route) present in the array is
shown. addToSidebar/removeFromSidebar no longer toggle feedSettings
showKey values, keeping sidebar config and feed settings independent.
ReplyComposeModal now renders ProfilePreview instead of raw JSON content
when the reply target is a kind 0 event, and titles the modal 'Comment on
profile' rather than 'Reply to post'.
The extra useComments(wallReplyTarget, 500) call was fetching up to 500
sub-replies from any author, bypassing the follow-list filter. Instead,
partition the already-fetched wallComments into top-level vs sub-replies
using the NIP-22 lowercase 'a' tag, then build the parent→children map
in-memory — no extra network request needed.
useWallComments queries #A (uppercase root tag) which returns all depth
levels per NIP-22, not just top-level comments. Filter orderedWallReplies
to only events whose lowercase 'a' tag points directly at the kind-0 root,
matching how useComments.topLevelComments works for other pages.
Extract shared ThreadedReplyList component and use it in PostDetailPage,
ExternalContentPage, and ProfilePage wall tab. Wall comments now also fetch
sub-replies via useComments so they render with the same threaded/threadedLast
connector-line pairing as replies.
Add useProfileWriteRelays hook that fetches a user's kind 10002 relay list
and extracts their write relays. useProfileFeed now routes feed queries
directly to those relays instead of the default pool, giving a more complete
view of the user's posts from the relays they actually publish to.
Use isPending instead of isLoading for the NIP-05 resolution loading guard.
In React Query v5, isLoading = isPending && isFetching, which is false on
the very first render before the fetch fires (fetchStatus: 'idle'). This
caused ProfilePage to immediately show 'User not found' on hard refresh
before the NIP-05 lookup even started.
After the relay race resolves (first EOSE + 100ms eoseTimeout), any pubkeys
whose kind 0 profile was not found are collected and re-queried in a second
batched request. This gives slower relays—the ones that lost the initial
race—a full chance to return profiles they have stored.
The settings page checked for the browser Notification API to determine
support, but Android WebView doesn't expose that API. The Java foreground
service handles notifications natively, so the check was wrong — it showed
'not supported' and disabled the toggle on every APK install.
- Render audio in PostDetailPage (was only rendering in feed NoteCard)
- Add volume slider to VideoPlayer and AudioVisualizer (hover to reveal)
- Move shared volume state and handlers into usePlayerControls
- Replace hardcoded purple canvas colors with hsl(var(--primary))
- Replace bg-black with bg-background on AudioVisualizer container
- Add AudioVisualizer component: mirrors VideoPlayer chrome exactly (same overlay controls, progress bar, hide-on-idle, scroll-pause) with a canvas sinewave and author avatar in the centre
- Wire audio into NoteCard (kind 1 notes), FileMetadataContent (kind 1063), and ComposeBox preview
- Extract shared utilities: formatTime, usePlayerControls hook, and mediaUrls (regexes + mimeFromExt) to eliminate duplication across VideoPlayer, AudioVisualizer, NoteCard, NoteContent, ComposeBox, and EmbeddedNote
- Fix ComposeBox: audio URLs now get correct imeta MIME tags and are excluded from link-embed detection