Commit Graph
100 Commits
Author SHA1 Message Date
Barry DeenandGitHub c7ac6730ba Merge pull request #24 from Letdown2491/feat/staging-build-type
feat: add staging build type for release-performance on-device testing
2026-06-12 12:12:28 -04:00
Barry DeenandGitHub 0bcceffae2 Merge pull request #8 from dmnyc/feat/zap-sheet-fiat-image
feat(zap): fiat currency input + inline images in zap messages
2026-06-12 10:32:57 -04:00
Barry DeenandGitHub 2e0a5b9e8e Merge pull request #23 from Letdown2491/fix/notification-spam-score-anr
fix: move spam scoring out of NotificationRepository lock
2026-06-12 10:29:06 -04:00
Barry DeenandGitHub 21ce7ee2a8 Merge pull request #5 from roguehashrate/feature/payment-targets-zec-dash-bch-ltc
feat: add ZEC, DASH, BCH, LTC payment targets
2026-06-11 06:32:53 -04:00
Barry DeenandGitHub f3f8c0b199 Merge pull request #4 from barrydeen/feat/nip-a3-payment-targets
feat: add NIP-A3 payment targets (kind 10133)
2026-06-10 13:43:45 -04:00
Barry Deen edbb5ab549 feat: add NIP-A3 payment targets (kind 10133)
Users can publish payment addresses for other cryptocurrencies and
payment apps (payto tags per draft NIP-A3) and pay other users
through them.

- NipA3.kt: parse/build payto tags, type validation, payto:// and
  native wallet URIs, recognized-type stylization
- PaymentTargetRepository: LRU + SharedPrefs cache per pubkey;
  stores empty lists so cleared targets propagate
- ZapDialog: "Other ways to pay" chip section; targets-only dialog
  when lightning zapping isn't possible but targets exist
- PaymentTargetSheet: QR, copy, open-in-wallet bottom sheet
- Wallet settings: manage and publish own targets with network
  read-back before editing to avoid clobbering the replaceable event
- Profile screen: targets shown under the lightning address
- On-demand fetch only (zap dialog / profile open), ingested via
  EventRouter with created_at freshness guard
2026-06-10 13:41:36 -04:00
Barry DeenandGitHub dc35c0f49e Merge pull request #3 from barrydeen/feat/tor-support
feat(tor): embedded Tor with fail-closed routing for all connections
2026-06-10 13:04:23 -04:00
Barry Deen 28cb2dad42 feat(tor): embedded Tor with fail-closed routing for all connections
Add an embedded Tor daemon (kmp-tor) with an onion toggle in the sidebar
drawer and on the pre-login Splash/Auth screens. When enabled, every
connection — relay WebSockets, Coil images, ExoPlayer media, NIP-05/NIP-11,
LNURL/zaps, Blossom uploads, link previews, exchange rates, DM media, live
metrics — routes through Tor's SOCKS proxy.

Routing is fail-closed: from the moment the toggle flips, new clients point
at a dead loopback port until bootstrap completes, and existing clients have
their in-flight requests cancelled and keep-alive pools evicted so no
pre-toggle socket survives. OkHttp passes unresolved hostnames to SOCKS
proxies, so DNS also resolves inside Tor.

- TorManager: kmp-tor runtime in a foreground service; state machine
  (Off/Starting %/On/Stopping/Error) observed by all toggles; start retries
  around kmp-tor's fixed 1s service-bind timeout, which a busy main thread
  misses during cold start
- TorPreferences: device-level pref so the toggle works pre-login and
  survives logout
- HttpClientFactory: single proxy choke point; client registry +
  setTorSocks() invalidation; new getRelayClient/getLoopbackClient/
  getDmMediaClient
- Relay/RelayPool: clients resolved through providers on every (re)connect;
  suspendForTorSwitch/resumeAfterTorSwitch tear down sockets and suppress
  reconnects during bootstrap so the 5-min relay cooldown can't trip; local
  relay stays direct (Tor refuses loopback/LAN targets)
- Captured-client fixes so a mid-session toggle can't leak: Coil delegating
  Call.Factory (it caches its lambda forever), ZapSender, DmBubble,
  NwcRepository, SplashViewModel raw client, ExchangeRateRepository refetch
- Cold start with Tor on: relay init gated on bootstrap
- kmp-tor pinned to 2.4.x: 2.5+ needs Kotlin 2.2 metadata, unreadable by
  this project's Kotlin 2.0.21

Verified on-device by socket-inode audit: app process held 43 sockets, all
to 127.0.0.1:<socks>; only the tor child process held external connections.

Known gaps: Breez/Spark SDK (own Rust networking) and ML Kit model
downloads bypass OkHttp and are not routed.
2026-06-10 13:00:23 -04:00
Barry DeenandGitHub 7e733098e4 Merge pull request #2 from barrydeen/feat/private-replies-reactions-zaps
feat: private replies, private reactions, and private zaps
2026-06-10 12:12:35 -04:00
Barry Deen 8177cfc115 feat(private-replies): gift-wrapped reactions + DIP-03 zaps (port wisp#543)
Generalizes the private-event marker (markPrivateReply/isPrivateReply →
markPrivate/isPrivate) and re-enables React and Zap on private replies.
Reactions on a private reply are kind 7 rumors gift-wrapped to every
thread participant (the k tag distinguishes k=1 reply reactions from
k=14 DM reactions); PrivateReactionPublisher mirrors the reply publisher
with per-recipient relay resolution and a self-copy. Zapping a private
reply locks the ZapDialog to DIP-03 private mode (forcePrivate) so a
public zap can never attach an e-tag to the rumor id, and sendZap forces
the flag server-side as a defensive guard. Repost/Quote stay hidden.

Dark Wisp addition over the upstream PR: PrivateRumorHandler gains
handlePrivateReaction and the remote-signer pending-decrypt paths route
k=1 reaction rumors through it, matching the EventRouter behavior.
2026-06-10 12:04:58 -04:00
Barry Deen 4a87103944 feat(zaps): private zaps via DIP-03 + DM-relay routing (port wisp#541)
Replaces the relays-tag heuristic with DIP-03: the real sender signs an
inner kind 9733 event, NIP-04-encrypted into the outer kind 9734's anon
tag using an ephemeral key derived as sha256(privkey + eventId +
createdAt). Relays and the LNURL provider only see the unlinkable
ephemeral pubkey; receipts route through both parties' DM relays.
EventRepository.resolveZapSender() decrypts incoming private zaps (as
recipient) and re-derives the ephemeral key to self-attribute our own
outgoing ones, so notifications, zap lists, and wallet history show the
real counterparty. Private zaps require a local keypair, so the UI gate
adds hasLocalKeypair; live-stream (addressable) zaps can't derive the
ephemeral key and stay public.
2026-06-10 12:01:23 -04:00
Barry Deen 0308bad87c feat(compose): private replies via NIP-17 gift wrap (port wisp#540)
Kind 1 rumors inside kind 1059 gift wraps, delivered to the recipient's
kind 10050 DM relays (NIP-65 read relays as fallback). PoW is mined on
the rumor so the badge survives unwrapping; a self-copy wrap keeps other
devices in sync; optimistic local insert updates reply counts at once.
Compose gains a private-reply toggle that auto-enables and locks when
replying to a private reply; thread and notifications show a lock icon
and hide repost/quote/react/zap on private replies.

Dark Wisp addition over the upstream PR: private reply rumor handling is
extracted into PrivateRumorHandler and wired into the remote-signer
pending-decrypt paths (DmListViewModel/DmConversationViewModel), which
upstream dropped along with NIP-55 support — without this, gift-wrapped
replies would be misfiled as DM messages for remote-signer accounts.
2026-06-10 12:00:20 -04:00
Barry DeenandGitHub aa607cc705 Merge pull request #1 from barrydeen/fix/grapheneos-perf-apk-build
fix: revert 16KB packaging changes behind GrapheneOS perf regression
2026-06-10 11:51:32 -04:00
Barry Deen e0a889d2be fix: revert 16KB packaging changes behind GrapheneOS perf regression
Fully revert f68b40f, which shipped in 1.0.2 alongside reports of severe
slowdown and freezes on GrapheneOS:

- jniLibs.useLegacyPackaging back to true: compressed native libs cut the
  download from 82MB to 37MB, and uncompressed 16KB-aligned packaging is
  only needed for Google Play, which this build no longer targets
- secp256k1-kmp pinned back to known-good 0.16.0 (0.19.0 = libsecp256k1
  0.7.0 rebuilt with NDK 28; runs once per relay event on the verify path)

Also fix adjacent issues found while auditing every shipped release APK
(all were clean: release-signed, R8-minified, correctly aligned — ruling
out a malformed artifact as the cause):

- baseline-prof.txt still targeted com/wisp/app after the rebrand; the
  next release would have shipped zero app AOT rules. Fixed paths now
  yield 12,888 app rules; verified on-device (cold start 2048ms -> 830ms
  once compiled, ProfileVerifier reports compiledWithProfile=true)
- release signing moved into Gradle (RELEASE_STORE_* via local.properties
  or env) so the published APK is exactly what assembleRelease emits
- tools/audit-apk.sh: pre-release gate checking debuggable flag, lib
  packaging consistency, alignment, R8, baseline profile, signing cert
- ProfileVerifier status logged at startup for field diagnostics
- <profileable android:shell="true"/> so testers can capture simpleperf/
  Perfetto traces from release builds
- docs/grapheneos-perf-investigation.md: full findings plus the A/B
  attribution matrix (incl. GrapheneOS MTE toggle test) for confirming
  which half of f68b40f was guilty
2026-06-10 11:48:57 -04:00
Barry Deen 80f9e42cc6 chore: rebrand to Dark Wisp (com.darkwisp.app)
- Rename package com.wisp.app -> com.darkwisp.app
- Set applicationId/namespace to com.darkwisp.app
- Reset version to 1.0.0 (versionCode 1) as a new app
- Update display name to Dark Wisp / Dark Wisp Debug
- Update NIP-89 client tag to "Dark Wisp"
- Rebrand user-facing strings across all locales and README
- rootProject.name -> dark-wisp-android
2026-06-10 10:36:58 -04:00
Barry DeenandGitHub 9a6b100e3a Merge pull request #517 from barrydeen/chore/bump-version-1.0.5
chore: bump version to 1.0.5 (79)
2026-05-04 09:28:08 -04:00
Barry Deen d5a6f729d2 chore: bump version to 1.0.5 (79) 2026-05-04 09:26:26 -04:00
Barry DeenandGitHub 00d70f20f0 Merge pull request #516 from barrydeen/fix/bottom-tab-state-restore
fix: stop restoring stale tab back stacks on bottom-nav switch
2026-05-04 09:25:29 -04:00
Barry Deen 5b745ddb90 fix: stop restoring stale tab back stacks on bottom-nav switch
PR #514 changed the bottom-nav handler to use popUpTo with the graph's
start destination plus saveState/restoreState. Two regressions resulted:

- Tapping a tab restored the tab's saved back stack, returning the user
  to a previously-open thread instead of the tab's main screen.
- The graph's start destination (LOADING) is popped inclusive on first
  successful load, so popUpTo never matched. The resulting back-stack
  state could surface the splash/auth screen on system back from
  Notifications.

Revert that block to popUpTo(FEED) { inclusive = false } + launchSingleTop,
which always lands the user on the tab's main screen with a shallow stack.
The refreshDmsAndNotifications() throttle introduced alongside the broken
nav block is preserved — that part addresses real REQ churn jank and is
independent of how the back stack is structured.
2026-05-04 09:23:09 -04:00
Barry DeenandGitHub 04501e41de Merge pull request #515 from barrydeen/fix/tab-switch-jank
perf: reduce startup and feed rendering work
2026-05-04 08:33:37 -04:00
Barry Deen a55ebdfee6 chore: bump version to 1.0.4 (78) 2026-05-04 08:33:22 -04:00
Barry Deen c4a8aacee3 perf: restore tight NIP-05 timeouts, isolate download client, harden notif rebuild
- give getNip05Client a dedicated 5/10s client (was aliasing the 10/15s
  general client); slow .well-known/nostr.json endpoints no longer tie
  up verification threads
- add getDownloadClient (30/60s) for MediaDownloader; the shared media
  client (10/30s) is fine for ExoPlayer streaming but its read timeout
  is too tight for full-file downloads on flaky networks
- tighten notification rebuild coalesce window from 50ms to one frame
  (16ms) so single-arrival updates feel responsive
- add NotificationRepository.shutdown() and call it from
  FeedViewModel.onCleared() so rebuildScope doesn't outlive the VM
2026-05-04 08:18:03 -04:00
Barry Deen e3373889e0 perf: reduce startup and feed rendering work 2026-05-04 08:08:44 -04:00
Barry DeenandGitHub de7af45149 Merge pull request #514 from barrydeen/fix/tab-switch-jank
fix: reduce bottom-tab switching jank
2026-05-04 07:46:53 -04:00
Barry Deen a0ec123e0c fix: preserve bottom-tab state during tab switching
Restore top-level tab destinations instead of recreating them on each tap, and avoid immediately resubscribing inbox streams when users bounce between tabs. This reduces navigation jank and repeated hangs on slower devices.
2026-05-04 07:44:39 -04:00
Barry DeenandGitHub b028007a7c Merge pull request #513 from barrydeen/chore/bump-version-0.3
bump
2026-05-03 21:59:32 -04:00
Barry Deen 55e3adfc74 bump 2026-05-03 21:58:58 -04:00
Barry DeenandGitHub 550b6d1997 Merge pull request #512 from barrydeen/feat/persist-decrypted-dms
Persist decrypted DMs to skip per-boot signer round-trips
2026-05-03 21:57:12 -04:00
Barry Deen 4894ccee40 feat: persist decrypted DMs to skip per-boot signer round-trips
NIP-17 gift wraps are re-fetched on every cold start (the kind-1059
subscription has no since cursor because gift-wrap timestamps are
randomized up to 2 days in the past). Without a decrypted-DM cache,
remote-signer mode (Amber) re-runs two NIP-44 IPC decrypts per wrap on
every launch.

Adds an ObjectBox-backed cache keyed by ownerPubkey|giftWrapId. On
construction, DmRepository hydrates conversations, seenGiftWraps,
rumorIdIndex and the DM notification list from disk off the main thread.
addPendingGiftWrap now short-circuits on seenGiftWraps before queuing,
so relay-redelivered wraps never reach the signer; only newly arrived
wraps are decrypted. addMessage / addReaction / addZap re-persist the
parent message so reactions and zaps survive restarts. clear() and
purgeUser() propagate to the persistence layer.
2026-05-03 21:54:41 -04:00
Barry DeenandGitHub 81aeaa05f8 Merge pull request #511 from barrydeen/refactor/remove-tor
refactor: remove Tor functionality
2026-05-03 21:16:01 -04:00
Barry Deen 0223e1b9e7 refactor: remove Tor functionality
Drops the embedded kmp-tor client, all .onion relay support, the Tor
toggle UI on splash/auth/drawer screens, the SOCKS proxy plumbing in
HttpClientFactory, all Tor-related strings across 11 locales, the
TorManager singleton, and the kmp-tor dependencies and ProGuard rules.

Relay URL validation now accepts only wss:// (with hostname, no port,
non-localhost, non-IP). The cleartextTrafficPermitted network security
flag stays — still needed for local relays on RFC1918 / loopback.
2026-05-03 21:14:51 -04:00
Barry DeenandGitHub 94b9d7b78c Merge pull request #510 from barrydeen/fix/relay-connect-anr
fix: dispatch Relay.connect() off the calling thread to avoid ANR
2026-05-03 20:51:25 -04:00
Barry Deen 955787eaad fix: dispatch Relay.connect() off the calling thread
OkHttpClient.newWebSocket() can block on its shared TaskRunner lock
for several seconds under contention. UI callbacks that invoke
RelayPool.sendToRelayOrEphemeral were calling Relay.connect() inline
on the main thread, producing 5s+ ANRs. Dispatch connect() through a
small dedicated thread pool so callers never wait on WebSocket setup.
2026-05-03 20:47:03 -04:00
Barry DeenandGitHub 683e3072b2 Merge pull request #509 from barrydeen/feat/inline-video-loop
feat: loop inline mp4 videos in note cards
2026-05-03 20:36:19 -04:00
Barry Deen 08d1340e9e feat: loop inline mp4 videos in note cards
Inline video players in RichContent now use REPEAT_MODE_ONE so MP4s
auto-repeat when rendered inside a note card. Applied to both freshly
created and PiP-reclaimed players. Fullscreen and audio paths are
unchanged.
2026-05-03 20:35:48 -04:00
Barry DeenandGitHub b6105eb134 Merge pull request #494 from dmnyc/feat/fiat-mode-coin-stack-icon
feat: coin stack icon for zaps in fiat mode
2026-05-03 20:01:28 -04:00
Barry DeenandGitHub 8d17d12a45 Merge pull request #502 from dmnyc/feat/post-undo-timer-settings
feat: post undo timer settings
2026-05-03 20:01:11 -04:00
Barry DeenandGitHub 0347c8f0e4 Merge pull request #503 from dmnyc/fix/drawer-item-spacing
fix: reduce drawer menu item spacing
2026-05-03 20:00:55 -04:00
Barry DeenandGitHub f857466455 Merge pull request #504 from dmnyc/fix/switch-unchecked-colors
fix: improve Switch visibility in unchecked state
2026-05-03 20:00:40 -04:00
Barry DeenandGitHub 1a10f6b84f Merge pull request #495 from fiatjaf/debug-build
use a different id and name for the app when doing a debug build
2026-05-03 20:00:13 -04:00
Barry DeenandGitHub 786710efbf Merge pull request #505 from fiatjaf/fix-spacing
add a spacing between "replying to" and the name
2026-05-03 19:59:14 -04:00
Barry DeenandGitHub 30bcaca875 Merge pull request #506 from fiatjaf/thumbhash
Thumbhash
2026-05-03 19:58:54 -04:00
Barry DeenandGitHub 413a06915e Merge pull request #500 from barrydeen/feat/qr-scan-drawer
feat: add QR scan tab to drawer QR sheet
2026-04-26 09:27:09 -04:00
Barry Deen fddae10109 chore: bump version to 1.0.2 (76) 2026-04-26 09:26:10 -04:00
Barry Deen c8d6e0d05f feat: add QR scan tab to drawer QR sheet
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.
2026-04-26 09:26:10 -04:00
Barry DeenandGitHub 0b0d30dec2 Merge pull request #493 from barrydeen/fix/16kb-page-size
fix: support 16 KB page size for Android 15+
2026-04-23 11:03:49 -04:00
Barry Deen f68b40f5a7 fix: support 16 KB page size for Android 15+
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.
2026-04-23 11:03:17 -04:00
Barry DeenandGitHub 0d8c856c29 Merge pull request #492 from barrydeen/feat/inline-video-download
feat: add download button to inline video controls
2026-04-23 10:36:06 -04:00
Barry Deen 3ab1041fce feat: add download button to inline video controls 2026-04-23 10:35:38 -04:00
Barry DeenandGitHub b609fc56c0 Merge pull request #491 from barrydeen/fix/notifications-freeze-indexof
perf: avoid O(N^2) indexOf in notifications list
2026-04-23 10:30:19 -04:00
Barry Deen 503c46b256 perf: avoid O(N^2) indexOf in notifications list
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).
2026-04-23 10:29:43 -04:00
Barry DeenandGitHub f4c716551b Merge pull request #490 from barrydeen/fix/quoted-note-emoji-picker
fix: use user's emoji picker for quoted/referenced notes
2026-04-23 10:18:43 -04:00
Barry Deen 69e49eb648 fix: use user's emoji picker for quoted/referenced notes
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.
2026-04-23 10:03:41 -04:00
Barry DeenandGitHub 642b68e854 Merge pull request #489 from barrydeen/feat/drawer-avatar-opens-profile-edit
feat: open edit profile when tapping drawer avatar
2026-04-23 09:50:07 -04:00
Barry Deen 0d8e34fe02 feat: open edit profile when tapping drawer avatar 2026-04-23 09:49:44 -04:00
Barry DeenandGitHub 05e14914b4 Merge pull request #488 from barrydeen/fix/media-downloader-oom
fix: stream media downloads to avoid OOM on large files
2026-04-23 09:44:22 -04:00
Barry Deen 7a29f976df fix: stream media downloads to avoid OOM on large files 2026-04-23 09:43:34 -04:00
Barry DeenandGitHub 5853db286b Merge pull request #487 from barrydeen/feat/persistent-audio-player
feat: persistent audio mini-player docked above bottom nav
2026-04-22 22:26:04 -04:00
Barry Deen 06ec5592d3 feat: persistent audio mini-player docked above bottom nav
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.
2026-04-22 22:21:41 -04:00
Barry DeenandGitHub ebeebccbe9 Merge pull request #486 from barrydeen/fix/persist-settings-across-account-switch
fix: preserve local relay and emoji frequency across account switches
2026-04-22 21:56:22 -04:00
Barry Deen e245b2fbde fix: preserve local relay and emoji frequency across account switches
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.
2026-04-22 21:54:40 -04:00
Barry DeenandGitHub b0455b1ea6 Merge pull request #485 from barrydeen/fix/compose-mention-ux
fix: stop mention insert from breaking compose cursor and keyboard
2026-04-22 21:04:50 -04:00
Barry Deen ad8c1c4d11 fix: stop mention insert from breaking compose cursor and keyboard
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.
2026-04-22 21:04:13 -04:00
Barry DeenandGitHub f0f728d2fc Merge pull request #484 from barrydeen/fix/trending-reconnect-race
fix: trending feed not reconnecting after app resume
2026-04-22 20:51:24 -04:00
Barry Deen 6582c16d88 fix: trending feed not reconnecting after app resume
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.
2026-04-22 20:50:52 -04:00
Barry DeenandGitHub f9cc586804 Merge pull request #483 from barrydeen/fix/pull-self-deletions-on-startup
fix: pull own NIP-09 deletions on startup so deleted sets stay gone
2026-04-22 20:14:00 -04:00
Barry Deen 359e64385b fix: fetch and apply own deletion events on startup
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.
2026-04-22 20:12:12 -04:00
Barry DeenandGitHub fbc18d385b Merge pull request #482 from barrydeen/feat/delete-note-confirmation
feat: confirm before deleting a note
2026-04-22 20:01:36 -04:00
Barry Deen 984ef213e0 feat: confirm before deleting a note from the 3-dot menu 2026-04-22 20:01:10 -04:00
Barry DeenandGitHub 499af910f4 Merge pull request #481 from barrydeen/feat/broadcast-mentions-to-inbox
feat: broadcast notes to inbox relays of mentioned pubkeys
2026-04-21 09:18:53 -04:00
Barry Deen ea80c46d2c feat: broadcast notes to inbox relays of all mentioned pubkeys
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.
2026-04-21 09:18:19 -04:00
Barry DeenandGitHub 38c8323eca Merge pull request #480 from barrydeen/docs/readme-rewrite-v1
docs: rewrite README for v1.0.0 feature set
2026-04-19 14:26:29 -04:00
Barry Deen 6b7515804b docs: rewrite README for v1.0.0 feature set 2026-04-19 14:26:05 -04:00
Barry DeenandGitHub 58e4b5c14e Merge pull request #479 from barrydeen/chore/bump-version-1.0.0
chore: bump version to 1.0.0
2026-04-19 14:21:14 -04:00
Barry Deen b0851706a2 chore: bump version to 1.0.0 2026-04-19 14:20:54 -04:00
Barry DeenandGitHub 8740154b01 Merge pull request #478 from barrydeen/fix/group-join-auth-race
fix: wait for NIP-42 AUTH before group 9021/9007/9009 and surface admin failures
2026-04-19 14:18:48 -04:00
Barry Deen c20a2c6d20 fix: wait for NIP-42 AUTH before group 9021/9007/9009 and surface admin failures
Group joins and admin actions (create group / invite / edit metadata / put user /
remove user / leave / delete) previously fired their events as soon as the signer
returned, racing the relay's NIP-42 AUTH challenge. On an auth-gated relay the
unauthenticated event hits first and the relay either responds "auth-required:"
or silently downgrades the request — a 9021 with a valid invite code returns
"restricted: group is closed, you need an invite code", a 9007 can fail silently,
a 9009 then fires against a ghost group and gets "blocked: group doesn't exist",
and the admin never sees any of it because the publishes were fire-and-forget.

attemptJoin now waits up to 5s for authCompleted before sending the 9021, and
still retries once on an auth-required rejection as a safety net. A new
publishAdminEvent helper wraps the admin kinds with the same auth-wait, awaits
the relay's OK, and emits an AdminError on rejection or timeout. createGroup
additionally rolls back its local placeholder if the relay rejects 9007 so we
don't keep trying to issue invites for a group the relay doesn't know about.

A new adminErrors SharedFlow on GroupListViewModel is collected by DmListScreen
and rendered as an AlertDialog showing the step (e.g. "createGroup/9007") and
the relay's message, so silent admin failures become visible.
2026-04-19 14:15:58 -04:00
Barry DeenandGitHub 242b809916 Merge pull request #477 from barrydeen/fix/account-switch-social-graph-notifications
fix: preserve social graph and notifications across account switches
2026-04-19 12:43:35 -04:00
Barry Deen 7d5b988005 fix: preserve social graph and notifications across account switches
The social graph (ExtendedNetworkRepository + SocialGraphDb) was wiped
on every account switch because clear() deleted both the SharedPreferences
cache and SQLite tables, and SocialGraphDb used a single shared DB file.
Now SocialGraphDb uses per-account files with a one-time legacy migration,
and clear() no longer touches persistent state (only in-memory). Notification
events from account A were leaking into account B because the ObjectBox
seeding coroutine fired after the switch and NotificationRepository had no
reload(). Adds reload() to NotificationRepository/SafetyPreferences
with a stale-pubkey rejection guard and proper job cancellation.
2026-04-19 12:42:29 -04:00
Barry DeenandGitHub 3e27713078 Merge pull request #476 from barrydeen/fix/group-metadata-preserve-permissions
fix: preserve group permission flags on metadata edit
2026-04-19 11:57:26 -04:00
Barry Deen 2e0c193f04 fix: preserve group permission flags on metadata edit 2026-04-19 11:36:29 -04:00
Barry DeenandGitHub 92f99593d8 Merge pull request #473 from barrydeen/feat/increase-onboarding-timeout
feat: increase onboarding EOSE timeout to 8s and add relays
2026-04-19 10:42:10 -04:00
Barry Deen 91ac6d8f20 feat: increase onboarding EOSE timeout to 8s and add relay.wisp.talk, pyramid.fiatjaf.com
Bump collectUntilEose timeout from 3000ms to 8000ms across all suggestion
queries (active now, news, profile fetches) to handle slow connections.

Add relay.wisp.talk and pyramid.fiatjaf.com to ACTIVE_RELAYS so more
users appear in the 'Active right now' onboarding section.
2026-04-19 10:41:03 -04:00
Barry DeenandGitHub a5ae58d3f6 Merge pull request #472 from barrydeen/fix/persist-deletes-drafts-hashtag-sets
delete hashtags, drafts, thread drops
2026-04-18 15:22:32 -04:00
Barry Deen b802c124d9 delete hashtags, drafts, thread drops 2026-04-18 15:20:31 -04:00
Barry DeenandGitHub 5f6eb0ca7d Merge pull request #471 from barrydeen/feat/nip29-group-config
feat: NIP-29 group config — flags, invites, roles, AUTH prompts
2026-04-18 13:20:38 -04:00
Barry Deen c4900142b3 feat: NIP-29 group config — flags, invites, roles, AUTH prompts
Expands group support beyond open/unrestricted creation:

- Create dialog exposes the 4 NIP-29 config flags (private, closed,
  restricted, hidden) as plain-English toggles; the follow-up kind:9002
  is only sent when the user actually customizes posture, with a 1.5s
  delay after the 9007 so the relay can mark us admin first.
- Group Detail shows per-flag icon + label + description rows plus a
  header badge chip (defaults to "Public" when fully open), so every
  room's posture is legible at a glance.
- Admin-only invite code section generates kind:9009 events and
  surfaces copy-code / copy-invite-link actions; Join dialog parses
  `relay'groupId?code=...` and plumbs the code through kind:9021.
- Per-member "Assign role" action sends kind:9000 with free-form role
  tags; admins list parses role strings without breaking call sites.
- Join flow waits on the relay's OK response before adding the room
  locally — rejections surface as a dialog instead of a broken
  chat screen with no messages.
- Joined chat relays now get tier-2 AUTH prompts (prompt-and-remember)
  instead of silently discarding challenges; after AUTH completes we
  re-fire group REQs and clear stale "auth-required" banners.
- Raw 39000/39001/39002 events logged so relay-side behavior can be
  inspected directly from logcat.
- Default group relay switched from groups.0xchat.com to
  chat.wisp.talk.

Known: chat.wisp.talk omits non-admin pubkeys from kind:39002 on
closed groups, so the local member list stays sparse until the relay
behavior is fixed. To be addressed server-side.
2026-04-18 13:19:21 -04:00
Barry DeenandGitHub d3f511b7e2 Merge pull request #470 from barrydeen/feat/nspam-v2.2-lightgbm
feat: upgrade nspam model to v2.2 LightGBM
2026-04-18 11:32:33 -04:00
Barry Deen 799c52cd5b tune: raise nspam hide threshold from 0.5 to 0.7
Reduces false positives against real users that still show up with
the v2.2 model, at the cost of letting through some borderline
spam. Applied to both reply notifications and thread rendering.
2026-04-18 11:28:13 -04:00
Barry Deen c1a1c8dab1 feat: upgrade nspam model to v2.2 LightGBM
Replaces the v0.9 logistic-regression reply-spam classifier with
the v2.2 gradient-boosted tree model (500 trees, 63 leaves). Ships
model.txt + calibration.npz and adds a pure-Kotlin LightGBM tree
walker. Feature extraction is unchanged; tree-walk output matches
LightGBM's C++ predict bit-exactly against the published parity
fixtures.
2026-04-18 11:18:35 -04:00
Barry DeenandGitHub cf672a80c5 Merge pull request #469 from barrydeen/feat/for-you-feed
feat: add For You feed (extended/follows + trending + hashtags)
2026-04-16 22:55:28 -04:00
Barry Deen 764aac6322 feat: add For You feed blending extended/follows with trending and hashtags
New default feed type that combines three streams into one sorted list:
- Main author subscription (extended network if cached, follow feed otherwise)
- Top-reactions and top-replies trending notes (today timeframe)
- Notes for all followed hashtags, batched 10 at a time against the search
  relay over a 24h window

Supplementary fetches are one-shot per feed-generation and self-close on
EOSE + drain; events flow through EventRepository.addEvent() for dedup and
in-order insertion alongside the main feed. Pagination paginates only the
author subscription; trending and hashtag batches are initial-load only.
2026-04-16 22:06:53 -04:00
Barry DeenandGitHub 4c0e6b952b Merge pull request #468 from barrydeen/fix/zap-count-zero-display
fix: always show zap count on post cards, including 0
2026-04-16 21:35:54 -04:00
Barry Deen 4da5a087eb fix: always show zap count on post cards, including 0 2026-04-16 21:33:52 -04:00
Barry DeenandGitHub 7849c04965 Merge pull request #467 from barrydeen/fix/back-minimizes-app
fix: allow back button to minimize app from FEED
2026-04-16 21:29:24 -04:00
Barry Deen 221275e29d fix: allow back button to minimize app from FEED
Users expect pressing back from the home screen to minimize the app.
The BackHandler was swallowing the press on FEED; disable it there
so Android's default behavior handles it.
2026-04-16 21:28:59 -04:00
Barry DeenandGitHub 1b51a4b1fe Merge pull request #466 from barrydeen/fix/video-replay-autoplay-disabled
fix: replay inline video after it ends when autoplay is off
2026-04-16 21:24:40 -04:00
Barry Deen 8f90324c7a fix: seek to 0 when replaying ended video with autoplay off
ExoPlayer in STATE_ENDED stays at the end position, so calling play()
on the overlay tap had no effect after the video finished once. Seek
back to 0 before playing if the player has ended.
2026-04-16 21:23:33 -04:00
Barry DeenandGitHub 4b927b335a Merge pull request #465 from barrydeen/feat/media-compression-and-formats
feat: render HLS/HEIC inline + compress uploads + transcode GIFs
2026-04-16 21:19:00 -04:00
Barry Deen 17c17a6648 feat: compress images and transcode GIFs before Blossom upload
Every upload path (profile picture, compose attachment, DM attachment)
now runs through a shared preprocessing pipeline:

- image/gif -> transcoded to H.264 MP4 via MediaCodec + MediaMuxer
  through an EGL input surface (typical 5-20x size reduction)
- other image/* -> decoded with ImageDecoder (handles HEIC on API 28+),
  EXIF-rotated when needed, proportionally scaled, JPEG re-encoded
- videos and other types -> passed through unchanged

Profile pictures cap at 400px wide / JPEG quality 75. Compose and DM
uploads cap at 1920px / quality 82. imeta dim and Kind 15 size/dim
tags are recomputed from post-pipeline bytes so clients see accurate
metadata. Gallery-mode video/image mixing check treats GIFs as video
since they become MP4s.
2026-04-16 21:16:56 -04:00