Introduce ExternalPostData as a shared shape that both useBlueskyPost
and the new useMastodonPost hook normalize into. Both BlueskyEmbed and
MastodonEmbed are now thin wrappers that fetch data from their respective
APIs and render via the shared ExternalPostCard component.
MastodonEmbed now fetches from the instance's public API instead of
using an iframe, matching the native card treatment already applied to
Bluesky. Both embeds have built-in /i/ navigation so the DiscussBar
is no longer rendered for them.
Clicking the card body navigates to /i/{postUrl} instead of opening
bsky.app externally. Avatar and display name are separately clickable
buttons that navigate to /i/{profileUrl}.
Fetch post data from the public Bluesky API (getPostThread) and render
it natively with avatar, display name, handle, text content, images,
external link thumbnails, and interaction stats (replies, reposts, likes).
This eliminates the iframe dependency on embed.bsky.app and provides a
more consistent look matching the existing EmbeddedNote style.
Wall posts are filtered by the profile owner's follow list, so posts
from non-followed users wouldn't show up anyway. This hides the compose
box, FAB button, and shows a contextual message instead.
Resolve conflicts in AppRouter, NoteCard, sidebarItems, and PostDetailPage
by keeping both music/podcast features and main's additions (books,
calendar events).
The trending endpoint returned massive payloads with no field filtering support.
The search API with sort=readinglog and fields parameter returns slim responses
in under a second while still showing genuinely popular books.
- Add /books page displaying popular books fetched from OpenLibrary's trending API
- Add Books entry to sidebar registry so users can add it from the More menu
- Add /books route to AppRouter
- Show book title instead of raw 'isbn:...' in comment context by fetching book info via useBookInfo hook
Country suggestion only appears at the top of search results when
the query is an exact match on name or code. For prefix matches
(e.g. 'angol' -> Angola), the country appears below profile results.
If there are no profile results, the country is shown at the top.
Typing a country name in the search bar now shows matching countries
with their flag emoji, navigating to /i/iso3166:<CODE> on click.
Works on both desktop (ProfileSearchDropdown) and mobile
(MobileSearchSheet) with full keyboard navigation support.
Browse all countries in a searchable grid. Clicking a flag navigates
to the existing /i/iso3166:<CODE> external content page. The World
item is also registered in the sidebar so users can add it via the
More menu.
- Remove CustomEmojiPicker.tsx and useUserEmojiPacks.ts (replaced by
Derek's useCustomEmojis + emoji-mart custom categories)
- Update EmojiPackContent to use local kind 10030 query
- Update EmojiShortcodeAutocomplete to use useCustomEmojis
- Fix quick reaction buttons to render custom emojis as images
- Fix quick reaction buttons to include emoji tag when reacting
- Filter stale custom emojis from quick reaction row
- Add emoji tags to compose box mock event for preview rendering
Two fixes:
1. useCustomEmojis now resolves ['a', '30030:...'] references in the
user's kind 10030 emoji list, fetching the referenced kind 30030
emoji pack events and merging their emoji tags. Previously only
inline emoji tags were extracted, missing subscribed packs.
2. EmojiPicker theme detection now reads the --background CSS variable
lightness instead of checking for .dark class on <html>. Custom
themes set class='custom' (not 'dark'), so the dark class check
always returned false. Now works for all theme modes: light, dark,
system, custom, and profile themes.
Add collapseAfter prop to AncestorThread. When set, ancestors beyond that
depth are hidden behind a 'Show earlier posts' button with a dotted thread
indicator. Reactions pass collapseAfter=1 so only the reacted-to post is
immediately visible.
The parentEventId was only computed for text notes (isTextNote), so kind 7
reactions never resolved their e-tag parent. Include isReaction in the
condition so the AncestorThread renders the reacted-to post above.