Add wispSwitchColors() helper in Theme.kt that sets unchecked thumb,
track, and border colors so off-state toggles are distinguishable from
the background across all color schemes. Applied to all Switch instances
app-wide (10 files). Consolidates duplicate inline colors from
NotificationsScreen into the shared helper.
Adds configurable undo countdown for publishing:
- "Undo countdown" toggle (default on)
- Duration picker (5/10/15/20/30s, default 10)
- "Include replies" toggle (default off — replies send immediately)
ComposeViewModel reads settings to skip the countdown when disabled
or for replies unless opted in. Countdown duration is now configurable
instead of hardcoded to 10s.
Dedicated to Séimí Mac Síomón and Corndalorian.
Ports barrydeen/wisp-ios#34 to Android.
Adds a Scan tab alongside Nostr/Lightning in the drawer's QR sheet that
opens the camera and decodes Nostr entities (npub, note, nprofile, nevent,
naddr) — with or without the nostr: prefix — and navigates directly to
the matching profile, thread, or article.
Extracts the camera scanner from WalletScreen into a reusable QrScanner
component, and centralizes NostrUriData→route mapping as toRoute() in
Navigation.kt so the deep-link handler and scanner share it.
Add quoteDepth parameter to RichContent, QuotedNote, PostCard, and
GalleryCard. At depth >= 1 (already inside a quote), force compact
preview rendering (avatar + name + snippet) instead of full PostCard
with action bar. Prevents recursive nesting from crushing layouts.
Show coin stack icon in fiat mode for notifications (filter header,
per-zap icon, DM zap, summary stat), reaction details zap row, and
embedded invoices. Add maxLines=1 to all ActionBar count/amount text
to prevent vertical wrapping in tight layouts.
Replace the bolt/bitcoin icon with a coin stack icon on note zap
displays and the action bar zap button when fiat mode is active.
Also swap the drawer wallet menu item to the wallet icon in fiat mode.
Fixes corrupted Box import in WispDrawerContent.
Disable legacy jniLibs packaging so native libraries are stored
uncompressed and page-aligned in the APK. Bump secp256k1-kmp to 0.19.0
and kmp-tor-resource to 408.16.4 since the prior versions shipped
ELF binaries with 4 KB LOAD alignment. All 13 .so files in the release
APK now report 2**14 alignment.
Replace items(...) + notifications.indexOf(item) with itemsIndexed(...)
in NotificationsScreen. The indexOf call ran during composition for
every visible item, causing a linear scan per item and quadratic total
work as the list grew. Reported as a UI freeze when tapping the
notifications tab on slower devices (GrapheneOS).
Quoted notes nested inside another note were using the default unicode
emoji fallback instead of the user's sorted-by-frequency picker with
their custom emojis. Plumb resolvedEmojis, unicodeEmojis, and the
onOpenEmojiLibrary callback through NoteActions so nested PostCard /
GalleryCard renders in RichContent.QuotedNote pick up the same emoji
state as their parent.
Replaces the basic inline audio widget with a global mini-player that persists
across feed scrolling and tab navigation. Playback continues with system
notification / lock-screen transport controls via MediaSessionService.
The dock has two states: a collapsed row showing the author's avatar and name
with rewind-15 / play-pause / forward-15; swipe up to expand for a scrub slider,
speed cycling, and close. Single ExoPlayer is owned by AudioPlayerController
and shared with the inline tap-to-play widget, which now reflects global
playback state.
Two repos called clear() during resetForAccountSwitch() were doing
`prefs.edit().clear().apply()` on their SharedPreferences files while
those files were still pointing at the outgoing account:
- CustomEmojiRepository wipes its own per-pubkey file, erasing the
"most used" emoji frequency map. No relay mirror, so the data was
lost for good.
- BlossomRepository shares `wisp_prefs_{pubkey}` with KeyRepository, so
its full-file wipe also took out local_relay, relays, dm_relays, etc.
Most of those keys came back via replaceable-event refetches, but
local_relay has no network source and stayed null.
Drop the disk wipe from both clear() methods. The subsequent
reload(newPubkey) already repoints prefs to the incoming account's file.
Mentions used to be stored in the text field as raw nostr:nprofile URIs
rendered as @Name via an OutputTransformation. The visual/underlying
offset mismatch made the cursor jump, the IME desync on rapid typing,
forced a trailing space after every mention (so '@name's' was impossible),
and turned a single backspace into a silent delete of the whole mention.
Store '@Name' directly in the text field and track mention ranges
out-of-band in the ViewModel; splice nostr:nprofile URIs back in at
publish time. Editing is now plain text — identity offset mapping, no
forced space, and breaking a mention drops only that mention, not the
surrounding text.
Remove the preConnectEphemeral optimization from the reconnect path. It
created a race where the ephemeral was mid-connect when subscribeTrendingFeed
fired its REQ, leaving the subscription stuck on Connecting. The direct
metric-switch path (no preConnect) worked fine, so match it.
Relays sometimes keep serving hashtag sets (and other addressable
events) after the user publishes a NIP-09 kind 5 deletion, so sets
reappeared on app reload — especially after a fresh install or on a
second device where the local DeletedEventsRepository was empty.
Self-data subscription now also requests our own kind 5 events, and
EventRouter routes them through EventRepository (marking deletion
coords/ids) and sweeps any matching interest sets out of
InterestRepository so a late-arriving deletion still wins.
When publishing a kind-1 note (root or reply) that mentions pubkeys via
nostr: references, also deliver the event to each mentioned pubkey's
read (inbox) relays per NIP-65. Inbox URLs are deduplicated across
targets and against our own write relays, so shared inboxes only
receive the event once.