Extract useFeedTab hook that persists the active Follows/Global tab
in sessionStorage with a per-feed key (e.g. ditto:feed-tab:home,
ditto:feed-tab:vines). Each feed page remembers its own tab
independently — switching to Global on the home feed no longer
affects Vines, Videos, Photos, etc.
Applied to Feed.tsx (home), KindFeedPage, BooksPage, VideosFeedPage,
VinesFeedPage, PhotosFeedPage, and EventsFeedPage.
Kind 1063 is generic file metadata, so webxdc events require filtering
by #m=application/x-webxdc. Add extraFilters support to useInfiniteHotFeed
and pass webxdc as a separate filter object with the MIME constraint.
Switch the logged-out hot feed from useInfiniteSortedPosts (kind 1 only)
to useInfiniteHotFeed with a curated list of visually rich content types:
photos, videos, articles, vines, music, themes, polls, calendar events,
color moments, follow packs, and emoji packs.
The active feed tab (Follows/Global/Communities/saved feeds) was stored
in useState, so it was destroyed when navigating to a post and reset
to 'follows' on return. Now the selected tab is saved to sessionStorage
on change and restored on mount, surviving navigation while still
resetting when the browser tab is closed.
Validates stored tab against core tabs and saved feed IDs to prevent
restoring a deleted feed.
Closes#82
When searching from the sidebar while already on /search, the
SearchPage component stayed mounted and its internal state (allEvents,
isLoading) from the previous search would briefly flash before the
new query's effect could clear it.
Fix: wrap SearchPage with a keyed component that uses ?q as the React
key. When the query changes, React unmounts the old instance entirely
and mounts a fresh one — all state initializes cleanly from the new
URL with no transitional renders showing stale data.
Closes#72
Replace the plain 'Join' CTA banner with an engaging first-impression
experience: logo + tagline, interactive theme swatches that transform
the page on click, and a trending hashtags strip — all above the
existing hot feed. Staggered fade-in animations add polish.
The useEffect that clears allEvents runs *after* render, so the posts
useMemo would still filter/return old events during the first render
with the new query. Track the current query in a ref and return an
empty array from the useMemo when the query has just changed, preventing
stale results from ever reaching the UI.
When navigating from the sidebar search, the URL param (?q=) updates
immediately but the local searchQuery state lagged one render cycle
behind (synced via useEffect). During that gap, the old query's
results would briefly flash before being cleared.
Fix: derive an effectiveQuery directly from the URL search params and
use it for all data fetching and conditional rendering. The local
searchQuery state is kept only as the controlled input value.
Closes#72
On desktop the URL bar and 'Copy Link' in the More menu already
cover sharing. The native share sheet is the unique value on mobile,
so hide the button at the sidebar breakpoint (900px+).
Add a dedicated Share button using the Web Share API with clipboard
fallback across three locations:
- NoteCard action bar (between Zap and More)
- PostDetailPage action bars (both compact and expanded views)
- ProfilePage header (between More and Zap buttons)
On mobile and supported desktop browsers, tapping Share opens the
native OS share sheet with all installed apps (Twitter, WhatsApp,
etc.). On unsupported browsers, falls back to copying the link to
clipboard with a toast confirmation.
Closes#39
Rename toggle to 'Sync app theme with your profile theme' and update
description to explain what disabling it actually does, so users
understand it unsyncs the two themes rather than just stopping posts.
Consistent wording across both ThemeSelector and ThemesPage.
Closes#80
Look up the content type's icon from CONTENT_KIND_ICONS (same icons
used in the sidebar) and display it in both the label row and as the
fallback thumbnail placeholder.
When viewing a kind 1111 comment that replies to an addressable event
like a vine (34236), music, or article, the detail page now shows a
rich preview banner with thumbnail, kind label, title, and author —
matching the existing pattern for URL, profile, and community previews.
Works generically for all addressable event kinds by looking up the
kind label from EXTRA_KINDS and extracting thumbnails from image/imeta
tags.
Closes#75
Delete src/components/comments/ (CommentsSection, CommentForm, Comment)
which were never imported by any page or component. Rewrite the
nostr-comments SKILL.md to document the actual commenting architecture:
ComposeBox, ThreadedReplyList, useComments, usePostComment, and the
dual NIP-10/NIP-22 protocol system used across PostDetailPage,
ExternalContentPage, and other pages.
The profile page effect cleanup was restoring the background using a
stale previousBgCss value captured when the effect first ran, before
'Copy Theme' was clicked. Now the cleanup always rebuilds background
CSS from ownThemeRef.current (like colors already do), so it reflects
any theme changes made while on the profile page.
Closes#71
Portal the popover into the DialogContent element using
PortalContainerProvider so Radix pointer-event blocking
doesn't prevent the dropdown from opening.
Closes#68
Rename 'Search query' to 'Filter by Word' and 'Kinds' to 'Content Kinds'
for clarity. Reorder fields to: Tab name, Content Kinds, Filter by Word,
Authors — placing the higher-order Kind filter before the word filter.
Closes#68
Country feeds (iso3166) are social feeds and should display in
reverse-chronological order. URL and ISBN comment threads keep
the existing oldest-first conversation order.
Closes#64
Delete MobileBottomNav component and all related code including the
noBottomSpacer layout option, bottom nav height spacer, and CSS
calculations that accounted for the bottom nav height.
When a kind 1111 comment's parent is another comment (k tag = '1111'), show
'Replying to @user' with a link to the parent comment instead of the generic
'Commenting on [root]' label that always pointed to the thread root.
- Include kind 1111 in all three notification query filters (useNotifications, useHasUnreadNotifications, useNativeNotifications)
- Add CommentNotification component with context-aware action label: 'replied to your comment' when parent kind is 1111, otherwise 'commented on your post'
- Add 'New comment' title for native/browser push notifications
- Include kind 1111 in the Mentions tab filter alongside kind 1
- Raised dialog z-index from z-[150] to z-[250] so the overlay sits above
CommentsSheet's backdrop (z-[200]) and receives touch events
- Removed stopPropagation from DialogOverlay/DialogContent onClick handlers
which was preventing Radix DismissableLayer's deferred click listener from
reaching document on iOS Safari
On iOS touch, Radix registers a one-time click listener on document after
pointerdown. The stopPropagation was intercepting that click before it could
reach document, so the dismiss callback never fired. Z-index stacking now
handles isolation between the dialog and the CommentsSheet backdrop.
The picker was positioned relative to the trigger button via a Popover,
which felt awkward especially on smaller screens or inside other modals.
Now renders as a centered Dialog with a backdrop for a more traditional
modal experience.
Radix Dialog's RemoveScroll blocks wheel events on elements outside the
Dialog's DOM tree. The GIF picker Popover was portaling to document.body,
placing it outside the RemoveScroll boundary. Trackpad scrolling was
blocked while scrollbar dragging still worked.
Added PortalContainerContext so PopoverContent can portal into the modal
element instead of document.body when rendered inside a dialog/modal.
Layout changes:
- Switch from flex justify-between to 3-column grid layout
- Left group now uses justify-end (closer to center)
- Right group now uses justify-start (closer to center)
- Reduce group gap from gap-1 to gap-0.5
- Reduce container padding from px-3 to px-2
Center button adjustments:
- Reduce vertical offset from -mt-6 to -mt-4 (more integrated)
- Reduce size from size-14 to size-12
- Add mx-1 for controlled horizontal spacing
- Replace Zap icon with Sparkles (better fits Blobbi identity)
- Reduce icon size from size-6 to size-5
Side button adjustments:
- Reduce horizontal padding from px-3 to px-2.5
- Reduce vertical padding from py-2 to py-1.5
- Reduce min-width from 60px to 52px
Result: More compact, balanced bottom bar with groups visually
closer to the center action button