Commit Graph

4447 Commits

Author SHA1 Message Date
Chad Curtis 08787c542e Add stream card rendering in feeds with action headers and pause-on-scroll
Stream events (kind 30311) now render properly in mixed feeds via NoteCard
with inline LiveStreamPlayer for live streams, thumbnail for ended/planned,
title + summary clickable to open stream details, and 'is streaming' /
'streamed' action headers. LiveStreamPlayer pauses when scrolled out of view.
2026-02-21 04:54:00 -06:00
Chad Curtis 9ffe893d59 Remove client-side deletion filtering; rely on relays to handle kind 5 deletion 2026-02-21 04:40:25 -06:00
Chad Curtis 900608c265 Add post deletion support (NIP-09) with confirmation dialog and feed filtering 2026-02-21 04:35:47 -06:00
Chad Curtis d24f8c345a Add MutedContentGuard for post detail pages
Show a dismissible 'Muted Content' overlay instead of rendering muted
posts directly when navigating to their detail page. Follows the same
pattern as ContentWarningGuard — children are not mounted until the
user clicks 'Show Anyway', so media and nested queries are deferred.
2026-02-21 04:10:50 -06:00
Chad Curtis 21f7b3a1eb Persist decrypted mute list to localStorage for immediate availability on page load
Cache parsed mute items in localStorage (like the follow list pattern) so
muted content is filtered immediately on page refresh without waiting for
the relay fetch + decrypt cycle. The cache is written on initial sync,
after each successful decrypt, and eagerly on add/remove mutations.
2026-02-21 04:06:37 -06:00
Chad Curtis 24fb356eb1 Sync mute list (kind 10000) during initial login alongside relay list and settings
Fetches the user's encrypted mute list in parallel with the relay list
and encrypted settings during useInitialSync, then seeds both the raw
event and decrypted mute items into the TanStack Query cache. This
ensures muted content is filtered immediately on first login without
waiting for useMuteList's own delayed query.
2026-02-21 04:02:19 -06:00
Chad Curtis fe6e2065bc Implement mute user/conversation actions and apply mute filtering across all feeds
Wire up the previously stubbed 'Mute @user' and 'Mute Conversation' menu
actions in NoteMoreMenu and ProfilePage to call useMuteList().addMute,
with toggle support for unmuting. Apply mute filtering (via isEventMuted
from muteHelpers) across all feed surfaces: main feed, profile feed,
notifications, post detail replies, search/explore stream, hashtag page,
domain feed, trending posts, and right sidebar.
2026-02-21 03:57:59 -06:00
Chad Curtis 43e501ad74 Improve onboarding: add profile fields, fix sync gate for logged-out users, update profile cache
- Move 'New here?' link above extension login button
- Use profile-intro.png instead of MewLogo on profile step
- Add 'More details' collapsible with banner, website, and custom fields
- Skip sync/onboarding when logged out (guard in InitialSyncGate)
- Persist sync completion in localStorage instead of sessionStorage
- Initialize sync phase synchronously to prevent flash on refresh
- Invalidate author cache after profile publish for immediate updates
2026-02-21 03:42:02 -06:00
Chad Curtis c94fefd02e Add MewLogo at top of profile step in onboarding 2026-02-21 03:19:02 -06:00
Chad Curtis 63a54281d6 Replace hard border with rounded-full pill underline on settings section titles 2026-02-21 03:16:33 -06:00
Chad Curtis 28a9f13fc9 Fix post stats clearing during feed pagination
Rewrite useBatchEventStats as an incremental effect-based prefetcher
that only fetches uncached event IDs. The previous useQuery approach
used a query key containing all event IDs, which changed on every
pagination and raced against a 500ms NIP-85 timeout that overwrote
good cached stats with zeros.

Also stabilize useEventInteractions query key by replacing the
nip85Stats.data object reference with a boolean to prevent
unnecessary refetches on every NIP-85 staleTime cycle.
2026-02-21 02:47:09 -06:00
Chad Curtis 031f35495a Fix checkerboard pattern rendering on iOS by replacing SVG background with CSS grid 2026-02-21 01:43:41 -06:00
Chad Curtis cebded8383 Fix infinite tab loop on SearchPage by deriving tab from URL params
The activeTab state and searchParams were synced bidirectionally via two
competing useEffect hooks. When navigating between tabs via sidebar links
(e.g., 'View All'), setSearchParams produced a new reference, re-triggering
the URL→state effect with a stale closure, causing an infinite loop.

Fix: derive activeTab directly from searchParams (single source of truth)
and update the URL via a callback instead of a separate state variable.
2026-02-21 01:20:04 -06:00
Chad Curtis f36c9a67b5 Add markdown rendering for articles (kind 30023)
- Create ArticleContent component using react-markdown + rehype-sanitize
- Enable @tailwindcss/typography plugin for prose styling
- NoteCard shows article preview (title, image, summary)
- PostDetailPage renders full markdown article with prose typography
- Both views support header images and hashtag links
2026-02-21 01:13:51 -06:00
Chad Curtis b0d10546ca Add /articles route using KindFeedPage for kind 30023 2026-02-21 01:09:57 -06:00
Chad Curtis 3511621132 Move articles to Notes section with both sidebar and feed toggles 2026-02-21 01:07:55 -06:00
Chad Curtis 9074c9b763 Move articles to Media section with sidebar/page toggle
Articles (kind 30023) now have both a showArticles sidebar toggle and
feedIncludeArticles feed toggle, matching the pattern of other content
types like Vines and Streams. Added FileText icon to all icon maps.
2026-02-21 01:06:41 -06:00
Chad Curtis 1e9bd782aa Add Notes section for feed content organization (posts, reposts, articles)
- Add 'feed' section to EXTRA_KINDS for Posts (kind 1), Reposts (kind 6),
  and Articles (kind 30023) as feed-only toggles
- Add feedIncludePosts, feedIncludeReposts, feedIncludeArticles to FeedSettings
  (posts and reposts default to enabled, articles disabled)
- Add 'Notes' collapsible section in ContentSettings above 'Other Stuff'
- Feed kinds are now fully dynamic from settings instead of hardcoded
- Repost unwrapping accepts any event kind, not just kind 1
- Add article (kind 30023) card rendering with title, image, and summary
- Update all feed hooks for consistency (useFeed, useProfileFeed,
  useStreamPosts, DomainFeedPage, HashtagPage)
2026-02-21 00:36:58 -06:00
Chad Curtis 017b568100 Reorder EXTRA_KINDS by section and align onboarding to match
Array order is now media (Vines, Streams), social (Polls, Packs),
whimsy (Colors, Treasures). Sidebar and mobile drawer inherit this
order automatically. Onboarding CONTENT_KINDS updated to match.
2026-02-21 00:21:09 -06:00
Chad Curtis c95fbf1da5 Restore border separators on content type rows 2026-02-21 00:20:10 -06:00
Chad Curtis 5c89dc8ea0 Organize Other Stuff into Media / Social / Whimsy sections
Add a 'section' field to ExtraKindDef and group content types in both
FeedSettingsForm and ContentSettings with labeled section headers.
2026-02-21 00:19:08 -06:00
Chad Curtis 151ea6ab75 Fix missing Radio icon for streams in feed settings
Both FeedSettingsForm and ContentSettings were missing the 'streams'
entry in their ICONS map, causing it to fall back to a Palette icon
instead of the Radio icon used everywhere else.
2026-02-21 00:15:31 -06:00
Chad Curtis 99e864f815 Hide auth-only UI elements when logged out
Hide bookmarks, profile nav links, profile settings tab, wallet,
and network settings (relays, blossom servers) from logged-out users.
2026-02-21 00:11:40 -06:00
Chad Curtis 4707b37ad7 Fix emoji picker flashing quick select after deeper picker selection
Close the popover via onClose callback when an emoji is selected from
the full picker. Add a 300ms cooldown (justClosedRef) to prevent the
popover from immediately reopening via hover when the picker disappears
and the cursor lands on the trigger button.
2026-02-20 23:49:02 -06:00
Chad Curtis 58d7511545 Apply theme immediately on selection during onboarding 2026-02-20 22:21:02 -06:00
Chad Curtis 1d967b98d0 Integrate signup into onboarding flow, replace SignupDialog
Sign up now triggers the full onboarding overlay instead of a dialog.
The flow starts with keygen -> download key (logs in) -> profile setup
(kind 0) -> then continues with the existing settings questionnaire
(theme, content, safety, follows, outro).

Added OnboardingContext with useOnboarding().startSignup() hook so any
component can trigger the signup flow. Updated LoginArea, Feed,
LeftSidebar, and FloatingComposeButton to use it instead of
SignupDialog. SignupDialog is no longer imported by any component.
2026-02-20 22:18:51 -06:00
Chad Curtis a88a44d59e Preload feed behind outro when user followed a pack
If the user followed at least one pack during onboarding, render the
app tree (invisible) behind the outro overlay so React Query starts
fetching the follows feed in the background. When they tap 'Let's go',
content is already loaded. Users who skipped follows don't preload
since they'll land on the global feed which loads fast enough.
2026-02-20 22:11:43 -06:00
Chad Curtis f64ef2aed7 Fix outro wording: weird -> fun 2026-02-20 22:08:19 -06:00
Chad Curtis d5772de200 Revise outro text to be warmer and more playful 2026-02-20 22:06:50 -06:00
Chad Curtis f279b456c0 Shorten outro text 2026-02-20 22:06:37 -06:00
Chad Curtis c17815ea0b Add follow packs step and sentimental outro to onboarding
After saving settings, onboarding now checks the user's kind 3 follow
list. If empty, it shows suggested follow packs (starting with
naddr1qvzqqqyckypzpyexz3t34l966ngh5xg7u2q788hthdqmj0av3lv8s2tz9t43zt6d)
with member avatars and a Follow All button. Users with an existing
follow list skip this step.

The final step is now a warm outro thanking the user and inviting them
into the app, replacing the old spinner-only done screen.
2026-02-20 22:06:04 -06:00
Chad Curtis 1c640e3202 Use ChestIcon for treasures in onboarding content picker 2026-02-20 21:56:20 -06:00
Chad Curtis 3bcbd53100 Use lucide icons in onboarding and disable follow packs by default
Replace emoji icons with lucide-react components (Clapperboard, BarChart3,
MapPin, Palette, Users, Radio) in the content type picker. Remove follow
packs from the default selection for new users.
2026-02-20 21:53:29 -06:00
Chad Curtis ff43177aa5 Decrypt and apply encrypted settings before releasing sync overlay
useInitialSync now fetches the kind 30078 event, decrypts it with the
user's NIP-44 signer, and applies theme/feedSettings/contentWarningPolicy
to the config before transitioning out of the sync screen. It also seeds
the TanStack query cache so useEncryptedSettings and NostrSync don't
redundantly re-fetch or re-decrypt the same event.
2026-02-20 21:49:44 -06:00
Chad Curtis 726af608a0 Add initial sync screen with setup questionnaire for new devices
When a user logs in, show a sync spinner while fetching their settings
from Nostr relays. If existing settings are found (returning user on a
new device), apply them and proceed. If no settings exist (fresh account),
walk through a brief questionnaire for theme, content preferences, and
content safety policy. Uses sessionStorage to avoid re-showing on refresh.
2026-02-20 21:47:25 -06:00
Chad Curtis 7bd397a68b Fix theme not applying to feed/tabs until refresh
The body had an inline background style from the preloader that
overrode CSS variables. Now main.tsx removes the inline style
after React mounts, letting Tailwind's bg-background class take
over so theme changes apply instantly.
2026-02-20 21:31:13 -06:00
Chad Curtis 190a0dba9b Optimize feed load performance: caching, code splitting, and instant rendering
- Cache follow list and author profiles in localStorage for instant feed on return visits
- Prefetch profile images via <link rel=preload> for instant avatar rendering
- Replace Radix Avatar with lightweight component that renders <img> immediately
- Lazy-load route components (React.lazy) reducing main bundle from 1.7MB to 920KB
- Dynamic import hls.js (1.3MB) only when visiting stream pages
- Remove unused Buffer polyfill
- Add pre-React loading spinner in index.html outside #root for seamless handoff
- Fix 3-5s feed rerender caused by NostrSync relay/settings sync
- Fix Zod 4 strict mode rejecting feedSettings with extra/missing keys from localStorage
- Fix useLocalStorage skip update when value reference unchanged
- Reduce eoseTimeout from default to 600ms
2026-02-20 21:23:07 -06:00
Chad Curtis 87173f8432 Truncate long display names on profile page header 2026-02-20 18:28:29 -06:00
Chad Curtis 60a23aa0b3 Fix reply context overflow breaking sticky nav on mobile
Long unbroken names (raw npubs used as display names) in 'Replying to'
overflowed the container, pushing the page wider than the viewport and
making sticky/fixed nav elements scroll off-screen horizontally.

Truncate author names at 200px, add overflow-hidden on the container,
and shrink-0 on static text to prevent wrapping issues.
2026-02-20 17:59:06 -06:00
Chad Curtis 6066b5ec49 Replace polling notification system with persistent WebSocket subscription
Replaces the AlarmManager-based polling (wake every 60s, open/close WebSocket
connections) with a persistent WebSocket subscription to a Nostr relay. Events
are delivered in real-time via the open connection. A partial WakeLock keeps
the connection alive through Doze mode (same approach as Signal without FCM).

- Add NotificationRelayService: foreground service with persistent WebSocket,
  exponential backoff reconnection, network-aware reconnect, relay rotation
  on failure, and SharedPreferences config listener
- Refactor NostrPoller into notification dispatcher with metadata caching
  (SharedPreferences-based, avoids redundant WebSocket connections for
  repeat authors)
- Remove NotificationService (AlarmManager scheduler) and PollAlarmReceiver
  (WakeLock-based polling)
- Remove SCHEDULE_EXACT_ALARM permission (no longer needed)
- Disable web-layer background refetch on native (persistent connection
  handles it)
2026-02-20 17:43:26 -06:00
Chad Curtis afa7ede1c9 Respect content warning policy in profile media grid
CW media squares show a blurred placeholder with a shield icon instead
of loading the actual image/video. When policy is 'hide', CW media is
excluded from the grid entirely.
2026-02-20 17:01:26 -06:00
Chad Curtis 9b0313f83f Match CW settings layout to other settings rows: label left, radio right 2026-02-20 16:57:42 -06:00
Chad Curtis 469dd406d7 Contain blur within rounded box, don't let it bleed outside 2026-02-20 16:55:18 -06:00
Chad Curtis e41ce2e10b Fix content warning overlay: move overlay outside overflow-hidden container and check NIP-32 l tags for CW reason 2026-02-20 16:54:09 -06:00
Chad Curtis eb483469bb Add NIP-36 content warning support with blur/hide/show settings
Respect content-warning tags on events. Users can choose how to handle
them in Settings > Content > Sensitive Content:

- Blur (default): Shows a grey placeholder overlay with the warning
  reason in quotes. Media is not loaded until the user clicks reveal.
- Hide: Removes content-warned posts from the feed entirely.
- Show: Ignores content warnings and displays everything normally.

The setting syncs across devices via encrypted NIP-78 settings.
2026-02-20 16:45:33 -06:00
Chad Curtis 3314f01b5e Fix failing tests and ESLint config
- Update NoteContent tests to use inline URLs (mid-sentence) instead of
  end-of-line URLs that render as LinkPreview cards without accessible links
- Update mention styling assertion from text-gray-500 to text-muted-foreground
  to match the actual NostrMention component classes
- Add android/ to ESLint ignores to prevent linting build output artifacts
2026-02-20 15:20:18 -06:00
Chad Curtis 5bbbc5022a Fix mobile live stream layout: viewport-locked chat above bottom nav
Lock main element to viewport height on mobile with body scroll disabled,
collapsible description with author under details, zap button inline with
title, and chat input sitting directly above the bottom nav with no gaps.
2026-02-20 15:14:05 -06:00
Chad Curtis 4882a90403 Add NIP-53 live stream support with HLS player and live chat
Implement kind 30311 live streaming events: HLS video player (hls.js),
kind 1311 live chat with real-time subscriptions, stream detail page with
collapsible description on mobile, desktop sidebar chat, streams feed page,
and full extra-kinds integration with sidebar nav and settings.
2026-02-20 14:52:48 -06:00
Alex Gleason ee9a1e34c3 Always route search queries to relay.ditto.pub (for now) 2026-02-20 00:11:03 -06:00
Alex Gleason 77ff88c2f6 Render NIP-30 custom emoji in all remaining display name locations
Apply EmojifiedText to mentions inside embedded/quote posts,
embedded note author names, and all other secondary UI locations
where display names were still rendered as plain text.
2026-02-19 21:43:56 -06:00