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.
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.
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.
Read the user's theme from localStorage before first paint so the
preloader background and spinner colors match dark/light/black/pink.
Uses a small blocking script (public/theme.js) that runs before
the preloader is visible.
- 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
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.
Android 12+ crashes the app if startForeground() isn't called within 5
seconds of startForegroundService(). Moved it from onStartCommand() to
the top of onCreate() to ensure the foreground notification is posted
before any other initialization.
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)
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.
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.
- 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
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.
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.
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.
Add emojify/EmojifiedText utilities that replace :shortcode: patterns with
inline images using emoji tags from kind 0 and kind 1 events. Applied to
display names across NoteCard, PostDetailPage, ProfilePage, ProfileHoverCard,
InteractionsModal, NotificationsPage, and NoteContent @mentions. Also
emojifies about/bio text in ProfilePage and ProfileHoverCard.
Remove string-level truncation from getDisplayName to avoid breaking
shortcodes mid-pattern (CSS truncate handles visual overflow). Use
em-relative sizing and align-text-bottom for proper inline alignment.
Custom emoji reactions from kind 7 events with emoji tags were displaying
the raw shortcode text (e.g., ':soapbox:') instead of rendering the image.
Add custom emoji resolution throughout the reaction pipeline: data hooks
now extract emoji URLs from event tags, and all rendering locations
(ReactionButton, PostDetailPage stats, InteractionsModal, Notifications)
now display custom emojis as inline images.
Detect 64-char hex strings in the search bar and NIP19Page route.
Query relays to check if the hex is an event ID first, then fall back
to treating it as a pubkey if a kind-0 profile is found.
Detect NIP-19 and NIP-05 identifiers in the search bar and profile
search dropdown, navigating directly to the appropriate route instead
of performing a text search. Refactor DomainFeedPage header to use
back navigation, show user count inline, and match border styling.
- Add blossomServers (array), defaultZapComment, faviconProvider, corsProxy to AppConfig
- Blossom servers: multi-server support with add/remove UI matching relay list pattern
- Default zap comment changed from 'Zapped with MKStack!' to 'Zapped with Mew!'
- NIP-46 nostrconnect now uses the user's write relay list instead of hardcoded relay
- CORS proxy and favicon provider use {href} URI template pattern
- New 'System' section in Advanced Settings for zap comment, favicon provider, CORS proxy
- Fix ghost button hover text color on light/pink themes across settings panels
- Fix pre-existing TSC errors: appRelays array typing, muteHelpers dead code, ProfilePage FeedItem type, EditProfileForm useFieldArray, FeedSettingsForm/ContentSettings feedKey narrowing, LeftSidebar icon type
- Show link preview cards for URLs that end a line, not just standalone
URLs; only suppress previews for URLs followed by more text on the
same line
- Show skeleton placeholders on notifications page until the query has
completed at least once instead of flashing 'No notifications yet'
- Add skeleton loading for author rows in notification cards while
profile data loads
- Replace inline image <a> tags with ImageGallery component so clicking
images opens the lightbox with navigation
- Match notification tab height/weight to feed tabs on mobile while
preserving original desktop styling
The relay needs time to index newly published events. Immediate
invalidation caused a refetch that returned stale counts, overwriting
the optimistic +1 and making the count briefly appear then vanish.
- Add true optimistic updates for reactions and reposts via setQueryData
instead of only invalidating queries after publish
- Track user's own reaction per event and display their emoji in place of
the Heart icon across feed, post details, and notifications
- Strip whitespace around block embeds and skipped media URLs to eliminate
blank lines caused by whitespace-pre-wrap
- Only render link preview cards for standalone URLs; inline URLs in
sentences render as plain clickable links
- Remove border-b separators and use fixed-height object-cover on preview
images to eliminate gaps
- Tighten padding on EmbeddedNote and EmbeddedNaddr cards
- Use fixed-height object-cover for ImageGallery grid images
- Unify RepostMenu to use Popover on all screen sizes instead of Drawer
on mobile
- Domain feed page at /timeline/:domain fetches .well-known/nostr.json and queries events from domain users
- NIP-05 domains are clickable in usernames throughout the app (Nip05Badge, NoteCard, PostDetailPage, etc.)
- @_@domain.com renders as @domain.com (underscore prefix hidden)
- Profile hover cards on avatars, usernames, and mentions across all components
- Clicking avatar/banner on profile page opens image in gallery lightbox with safe-area support
- NIP-05 verified users get clean profile URLs (e.g. /user@domain.com) instead of /npub1...
- NIP19Page unified dispatcher handles NIP-19, NIP-05, and profile routing at /:param
- Removed /u/:npub and /d/:domain routes in favor of root-level routing
- CORS proxy fallback for NIP-05 resolution on non-CORS-compliant servers
- Mobile top bar logo links to home
- Safe area fixes for gallery lightbox, mobile drawer, and sheet close button