Replace the '?' avatar fallback and 'Anonymous' name placeholder with skeleton
loaders in ComposeBox and LeftSidebar during initial profile load. Expose
isLoading from useCurrentUser and useLoggedInAccounts to support this.
Bypass @emoji-mart/react wrapper which creates new Picker() in useEffect,
causing 'Failed to construct HTMLElement: Illegal constructor' when React
remounts the component (e.g. opening popover in reply or compose box).
Instead, manage the emoji-mart Picker Web Component imperatively via refs.
Type a colon followed by at least 2 characters (e.g. :fi) to trigger
an inline emoji autocomplete dropdown. Navigate with arrow keys, select
with Enter/Tab, or dismiss with Escape. Uses the existing emoji-mart
data for search by shortcode, name, and keywords.
The popover's mouse-leave timeout was firing during the DOM transition
from the quick-select bar to the full emoji-mart picker, causing it to
close immediately. Added an expanded state flag that disables the
hover-to-close behavior when the full picker is shown.
Encrypted settings synced via NIP-78 were parsed with a bare JSON.parse()
and cast with 'as EncryptedSettings', allowing invalid data (e.g. theme as
an object instead of a string) to pass through unchecked and crash the UI
when applied to classList.add(). Now both useEncryptedSettings and
useInitialSync validate decrypted JSON with a shared EncryptedSettingsSchema,
gracefully dropping invalid fields instead of propagating them.
Instead of discarding events from out-of-sync relays, keep all events
for display and compute the pagination cursor from the main timestamp
cluster. Outlier events are still shown but don't drag the cursor
backward, preventing pagination gaps without losing data.
- Add blossom.ditto.pub and blossom.dreamith.to as primary servers
- Mirror uploads to all configured servers via BUD-04 (fire-and-forget)
- Prefer first configured server for upload URLs
- Fall back to alternative Blossom servers when images/videos fail to load
The useLayoutOptions cleanup effect was unconditionally resetting the
layout store on unmount. During page transitions, React runs the new
page's render (which sets the store) before the old page's cleanup
effect fires. The unconditional reset would clobber the new page's
options, causing showFAB to be wiped.
Now the cleanup only resets if the store still holds this component's
options, preventing it from interfering with the incoming page.
- Extract zap amount from kind 9735 events in NostrPoller.java (checks
amount tag then falls back to description tag's zap request)
- Format amounts compactly (e.g. 1.5K sats, 2M sats)
- Set data URI on notification PendingIntent so tapping opens /notifications
- Handle deep link intent in MainActivity (both onCreate and onNewIntent)
- Remove dead localNotificationActionPerformed listener from TypeScript
(only fired for Capacitor-API notifications, not natively posted ones)
Render dropdown inline with absolute positioning instead of portaling
to document.body, which placed it behind the modal overlay. Also set
overflow-visible on DialogContent so the dropdown isn't clipped.
- Make detectMention accept explicit text/cursor params to avoid relying
on DOM textarea state that can be stale during React render cycles
- Re-attach textarea listeners when content changes to handle element
remounts (e.g. preview mode toggle)
- Prevent feed from going empty during background refetches by keeping
previous data as placeholder (placeholderData)
- Extend feed gcTime to 30 minutes so cache survives navigation
- Export DITTO_RELAY from appRelays.ts, replacing hardcoded relay URLs
- Normalize all relay URLs to use trailing slashes, preventing NPool
from opening duplicate connections to the same relay
- Add cache-based fallback to mention search: when relay search returns
nothing, scan already-fetched author profiles in the query cache
The profile feed query was sending a kind 0 (metadata) filter alongside
the feed filter in a single query. Since kind 0 resolves almost instantly,
relays would EOSE early, triggering the 100ms pool eoseTimeout before
slower relays could deliver the actual feed events. The profile page
already fetches kind 0 via useAuthor/NostrBatcher, so it was redundant.