Commit Graph
1217 Commits
Author SHA1 Message Date
Barry DeenandGitHub bd71a211a1 Merge pull request #44 from dmnyc/fix/linebreaks-before-mentions
fix(richcontent): preserve blank lines before inline profile/hashtag segments
2026-06-30 17:36:56 -04:00
The Daniel 0e348ad95f fix(richcontent): preserve blank lines before inline profile/hashtag segments
The trimBlankLines pass was stripping \n\n before NostrProfileSegment and
HashtagSegment, treating them as block-level elements. They are inline —
they share a Text() composable with surrounding text and get no extra Column
spacing — so blank lines before them must be preserved.

Notes whose paragraphs start with a nostr: mention lost all paragraph
spacing on Android as a result.
2026-06-27 00:06:31 -04:00
Barry DeenandGitHub 8083521e04 Merge pull request #38 from dmnyc/fix/compose-uploading-pill
fix(compose): move uploading indicator to its own line as a capsule pill
2026-06-26 14:11:35 -04:00
Barry DeenandGitHub 2ae8a3a1af Merge pull request #39 from dmnyc/user-profile-bio-update
feat(profile): clean up payment targets, CLINK offer & zap interface
2026-06-26 14:11:16 -04:00
Barry DeenandGitHub a91df75554 Merge pull request #42 from Letdown2491/feat/strip-link-trackers
feat: strip tracking params from links in note content
2026-06-26 14:10:59 -04:00
Letdown2491 70704cda75 feat: strip tracking params from links in note content
Removes share/analytics tracking params from URLs as they're rendered:
- utm_* and well-known click-ids (fbclid, gclid, msclkid, igshid, etc.)
  on every host
- YouTube share params (si/feature/pp) scoped to YouTube hosts, covering
  video, playlist, and channel links

Meaningful params (v, list, t, q, fragments) are preserved, and the URL
is returned untouched when nothing is stripped.
2026-06-25 22:05:42 -06:00
Barry DeenandGitHub ae03b930dc Merge pull request #37 from Letdown2491/perf/hotpath-and-startup
Perf/hotpath and startup
2026-06-18 12:35:54 -04:00
The Daniel 474f956a5f feat(profile): clean up payment targets, CLINK offer & zap interface
Consolidates the profile's payment surfaces so each method appears once and
the layout is consistent.

- Action row: uniform 40dp circle buttons (send, zap, follow, mute); the zap
  button routes straight to the zap composer
- Bio: long bios clip to ~5 lines with Read more / Show less; a CLINK noffer
  in the bio renders as a compact " CLINK offer" label instead of the full
  Pay-offer card (new plainNoffer flag on RichContent, scoped to the bio)
- Payment methods list: shared row layout with a fixed-width leading icon
  column for alignment and 10dp tap padding for separation; raw addresses
  (long Monero / noffer strings) dropped from the rows — the copyable value
  lives in the sheet that opens on tap
- Lightning address is now tappable: zaps when a wallet is connected,
  otherwise opens a QR + copy pay sheet (reuses PaymentTargetSheet)
- CLINK offer gets one explicit "Pay CLINK Offer" row (styled like the other
  payment rows) instead of being duplicated across a button chooser, an
  inline bio card, and a NIP-A3 target row
2026-06-17 14:07:46 -04:00
The Daniel ba13d53279 fix(compose): move uploading indicator to its own line as a capsule pill
The "Uploading…" label was crammed into the compose toolbar's icon row
alongside six icon buttons, leaving no horizontal space — so the text
wrapped to three lines ("Uplo / ading / …"). Pull the spinner + label out
of the icon row into a dedicated line below it, wrapped in a rounded
surfaceVariant capsule with maxLines = 1 so it can never wrap. Animates
in/out with the upload lifecycle.
2026-06-17 12:19:21 -04:00
Letdown2491 4d3e62af86 perf: defer exchange-rate init off the main thread
ExchangeRateRepository.init does disk I/O (loadCached) plus kicks off a network
refresh; move it off the main thread in WispApp.onCreate so it no longer gates
first frame. Rates populate asynchronously regardless and consumers already
handle the empty initial map. Startup-path win; matters more under GrapheneOS
exec-based spawning.
2026-06-16 09:47:33 -06:00
Letdown2491 5c0002b76b perf: verify worker pool + single-pass event decode + thread hygiene
- RelayPool: replace per-event coroutine-per-signature-verify with a small
  fixed worker pool fed by a bounded Channel. Reuses the RelayEvent already
  built for _relayEvents so there's no extra per-event allocation; falls back
  to an inline launch if the buffer is momentarily full so verification is
  never skipped. Cuts thousands of coroutine+closure allocations/sec on the
  hottest path (extra costly under GrapheneOS hardened_malloc).
- Event/RelayMessage: decode object-form events via fromJsonObject instead of
  the generated serializer, removing one walk over the parsed tree per message.
- WispApp: baseline-profile-check executor is now a daemon and shut down after
  its one-shot run instead of lingering for the app lifetime.
2026-06-16 08:55:20 -06:00
Barry DeenandGitHub 01490656f5 Merge pull request #36 from barrydeen/chore/fix-extra-translation-lint
chore: remove orphan wallet_spark translations
2026-06-16 00:56:58 -04:00
Barry DeenandClaude Opus 4.8 df802ec0b7 chore: remove orphan wallet_spark translations
The bare `wallet_spark` string existed in all 10 translated locales but
not in the default locale and is not referenced in code (only
wallet_spark_title/_subtitle_recommended/_sdk are used). It tripped
lint's ExtraTranslation check, failing lintVitalRelease/Staging.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 00:55:46 -04:00
Barry DeenandGitHub 3ec27958db Merge pull request #35 from barrydeen/chore/bump-v1.1.0
chore: bump version to v1.1.0
2026-06-16 00:47:36 -04:00
Barry DeenandClaude Opus 4.8 8800415851 chore: bump version to v1.1.0
Bump versionName 1.0.0 -> 1.1.0 and versionCode 1 -> 2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 00:46:25 -04:00
Barry DeenandGitHub 2fc8aecf8b Merge pull request #26 from dmnyc/feat/zap-sheet-overhaul
feat(zap): zap sheet overhaul — fiat input, iOS layout rewrite, hero composer, per-account presets
2026-06-16 00:35:38 -04:00
Barry Deen 71a37d597a Merge branch 'main' into pr-26-merge
# Conflicts:
#	app/src/main/kotlin/com/darkwisp/app/repo/InterfacePreferences.kt
#	app/src/main/kotlin/com/darkwisp/app/ui/component/ZapDialog.kt
2026-06-16 00:25:53 -04:00
Barry DeenandGitHub 707eed39bc Merge pull request #25 from dmnyc/feat/wallet-overhaul
feat(wallet): Spark-primary wallet overhaul — redesign, NWC backup, CLINK, Receive screen
2026-06-16 00:05:50 -04:00
Barry DeenandGitHub 07c497b782 Merge pull request #28 from dmnyc/feat/media-gallery
feat(media): horizontally scrolling image galleries for multi-image posts
2026-06-15 23:39:40 -04:00
Barry Deen 45f4d18789 Merge remote-tracking branch 'origin/main' into feat/media-gallery
# Conflicts:
#	app/src/main/kotlin/com/darkwisp/app/ui/component/MediaCarousel.kt
2026-06-15 23:36:16 -04:00
Barry DeenandGitHub 27b76b8502 Merge pull request #7 from roguehashrate/feat/anon-mode
feat: Anon Mode
2026-06-15 23:31:52 -04:00
Barry DeenandGitHub 2955dd4da1 Merge pull request #30 from dmnyc/feat/thread-ios-parity
feat(thread): iOS-parity depth connectors, reply order, sticky reply bar + publish gating (port wisp #567)
2026-06-15 23:16:15 -04:00
Barry DeenandGitHub 1ca9d9b58f Merge pull request #33 from Letdown2491/perf/strip-debug-logs
perf: strip Log.v/Log.d calls in minified builds
2026-06-15 23:15:46 -04:00
Barry DeenandGitHub f6276201a8 Merge pull request #34 from Letdown2491/perf/incremental-feed-filter
perf: maintain feed filter incrementally instead of re-filtering per update
2026-06-15 23:15:23 -04:00
Letdown2491 87d21e698c perf: maintain feed filter incrementally instead of re-filtering per update
The feed publish path re-filtered the entire master feedList (capped at
5,000 events) on every 50ms settle window — a full toList() copy plus a
.filter{} pass where each event paid a Set lookup and an isRepostedByAny
LRU lookup, then a fresh list allocation and StateFlow emit. During an
inbound burst this ran up to ~20x/sec.

Maintain a parallel filteredFeed list incrementally instead: membership is
decided once at insert time via a single passesFilter() predicate, and all
feedList mutation sites (binaryInsert, repost re-sort, removeEvent,
purgeUser, purgeThread, resetFeedDisplay) update both lists in lockstep
under the same monitor. The settle/immediate handlers now just snapshot the
maintained list; the full O(n) filter pass survives only in
rebuildFilteredFeed() for the rare author/kind filter change. Inserts that
don't change the filtered view no longer trigger a feed emission, avoiding
redundant Compose recompositions.

Measured on a Pixel 9a (staging/R8, one-build A/B timing both paths over the
identical feedList snapshot per publish): at ~350 notes the publish path
dropped from median 167us / p90 243us to median 5.7us / p90 18us — ~23-29x
faster, and the gap scales linearly with feed size since the per-update
re-filter is eliminated.
2026-06-13 12:36:55 -06:00
Letdown2491 ca3930ef15 perf: strip Log.v/Log.d calls in minified builds
Debug logging currently survives R8: RelayPool (51 Log.d calls) and
EventRepository (14) log in per-event paths, so release builds pay
string formatting and logd writes for every relay message, EOSE, and
poll/gallery event. -assumenosideeffects lets R8 remove the calls
along with their argument string building (verified: log strings
present in the dex before, gone after; Log.w/Log.e strings retained).
Debug builds are unminified and keep full logging.
2026-06-12 16:04:26 -06:00
Barry DeenandGitHub e48348eb28 Merge pull request #31 from Letdown2491/fix/inline-video-player-exhaustion
fix: lazily create inline video players to survive media-heavy notes
2026-06-12 14:52:27 -04:00
The Daniel 669cc87e35 feat(thread): iOS-parity depth connectors, reply order, sticky reply bar + publish gating (port wisp #567)
- Replace stacked vertical lines with single L-shaped connector per non-root
  reply: 12dp indent step capped at depth 5, 8dp corner radius, 1dp stroke,
  suppresses PostCard full-width divider so no line extends left of the curve
- Sort thread children purely by created_at oldest-first; removes own-replies-
  first bubbling to match iOS ThreadViewModel.buildNestedReplies
- Add ThreadReplyBar sticky bottom composable: 0.5dp divider + surfaceVariant
  pill with "Reply…" placeholder and orange edit icon; tap opens compose with
  the focal event as reply parent
- Gate Publish button on non-blank text or at least one uploaded attachment
2026-06-12 13:35:24 -04:00
Letdown2491 5a59341560 fix: lazily create inline video players to survive media-heavy notes
InlineVideoPlayer and InlineVideoPlayerWithFullscreen created a fully
prepared ExoPlayer per video URL the moment it entered composition,
with no cap or visibility gating. A note carrying many video URLs
(e.g. the circulating 155-video stress-test note) instantly spawned a
player per URL: 156 players / 487 player threads / 1800 process
threads measured on device, exhausting hardware codec instances
(~16-32 device-wide) and memory until input dispatch timed out (ANR)
in STACK media layout.

The player now only exists while its video is near the viewport:
created when >50% visible with autoplay on, or on tap (which starts
playback immediately); released with its position remembered once the
video scrolls fully off-screen. Until then the slot renders the
uploader-provided NIP-92 imeta "image" preview frame, falling back
to the existing thumbhash/blurhash painter. parseImetaTags now parses
the "image" entry, and MediaCarousel video tiles use it too instead
of a blank box when no thumbhash/blurhash is present.

Verified on device against the 155-video note: peak 7 ExoPlayer
threads (was 549), no ANR, playback/mute/fullscreen/PiP unaffected.
2026-06-12 11:32:22 -06:00
The Daniel e45d131ae2 feat(media): horizontally scrolling image galleries (port wisp #527)
- MediaCarousel: horizontal swipe gallery for multi-image posts with
  page indicators
- FullScreenMediaPager: swipe between images in full screen
- ZoomableAsyncImage: pinch-zoom, pan, double-tap and swipe-down dismiss
- Media layout setting (gallery/stacked) in Interface preferences
2026-06-12 13:17:11 -04:00
The Daniel b33c55c31c fix(settings): isolate instant-zap defaults per account (port wisp #576) 2026-06-12 13:16:49 -04:00
The Daniel 7fe8568292 feat(zap): hero-input composer + Edit Presets sheet + per-account presets + long-press wiring (port wisp #575) 2026-06-12 13:16:49 -04:00
The Daniel a0976e6e31 feat(zap-sheet): iOS-layout rewrite + instant-zap settings + post-card long-press + pin Send (port wisp #574)
NIP-A3 payment targets re-grafted into the new sheet layout: target
chips render in the scrollable column, the targets-only dialog still
covers the no-wallet/no-lud16 case, and all call sites pass both the
new profileLookup and the NIP-A3 params. LocalCanSign gate from wisp's
watch-only mode intentionally dropped (no watch-only in dark-wisp).
2026-06-12 13:16:49 -04:00
The Daniel 309e93426d feat(zap): fiat currency input + inline images in zap messages (port wisp #519, #559)
- Register-style fiat amount entry in the zap dialog with live sats
  conversion via ExchangeRateRepository
- Zap messages render image URLs inline in the engagement drawer,
  collapsed to [image] on the top banner (new ZapMessageImage util)
- Redesigned zap rows in reaction details as mini-posts via RichContent
- Adds String.toNpub() helper used by the new zap row fallbacks
2026-06-12 13:16:49 -04:00
The Daniel d612788ea7 refactor(wallet): remove nsec-derived default wallet feature (deferred)
Strips Keys.deriveSparkEntropy, SparkRepository.generateDefaultFromPrivkey/
isDefaultWallet/normalizeMnemonic, WalletViewModel isDefaultWallet state,
OnboardingViewModel auto-derive call, and all WalletScreen UI branches that
keyed on isDefaultWallet. Also adds String.toNpub() extension (Nip19) and
removes stale toNpub imports that were breaking the build.
2026-06-12 13:15:16 -04:00
The Daniel 624be33419 feat(theme): darker simplified header/footer chrome (port wisp #565)
- Darker default dark-mode backgrounds to match iOS
- TopAppBar chrome matches body color across all screens
- Compact chrome heights, iOS badge red, iOS-red error color
- Full-width post divider, zero-value action counters hidden
- Inset-aware chrome compaction (no first-frame snap)
- Profile tab strip darkened (kept dark-wisp's ScrollableTabRow +
  sort pill; wisp's pill-less tab row depends on unported #555)
2026-06-12 12:52:34 -04:00
The Daniel e8d7287636 fix(wallet): keep self-send legs distinct + deterministic tx order (port wisp #586) 2026-06-12 12:52:34 -04:00
The Daniel 4a536eb543 feat(wallet): iOS-parity Receive screen — text input + note + invoice/address tabs (port wisp #580) 2026-06-12 12:52:34 -04:00
The Daniel 396f101a96 feat(wallet-setup): primary Spark mode picker row with glow (port wisp #570)
Most of #570 (default-wallet primary row + More-options accordion)
already arrived via the #563 parity port; this adds the remaining
WalletPrimaryRow treatment for the Spark row on the mode picker.
2026-06-12 12:52:34 -04:00
The Daniel cefa6c5945 fix(clink): pay offers via signer abstraction so external signers work
Paying a noffer required a local private key (KeyRepository.getKeypair),
so sessions signed in with an external signer (e.g. Amber) were told to
sign in. NofferClient now takes a NostrSigner and routes NIP-44
encrypt/decrypt and event signing through it; both call sites build the
right signer for the active signing mode (local key, remote signer, or
read-only -> error).
2026-06-12 12:52:34 -04:00
The Daniel e2bb1a3f31 feat(clink): pay CLINK noffer offers from notes, profiles & wallet (port wisp PRs)
- NIP-69-style noffer parsing (Noffer.kt) + encrypted RPC client
  (NofferClient) for fetching invoices from CLINK services
- Full-height NofferPaySheet with payee recognition and amount entry
- noffer1 strings in notes, profiles, QR sheet and drawer resolve to a
  pay action; onPayInvoice plumbed through DM, group & search notes
- Offer field on profile edit; payee re-parse on profile cache update
- NofferClient routes through the relay client provider (Tor-aware)
2026-06-12 12:52:34 -04:00
The Daniel 5622e11c2e feat(wallet): NWC connection backup/restore via NIP-78 (port wisp #563)
- Publishes the NWC connection string as an encrypted NIP-78 app-data
  event on connect, replacing any prior backup
- Restores the saved connection when reconnecting on a new device
- Relay backup hidden for default Spark wallets (nsec is the canonical
  backup); kept for non-default seeds
2026-06-12 12:52:34 -04:00
The Daniel d8dd5f390f feat(wallet): dashboard polish — top-5 transactions, switch-wallet flow, drop relay backup status list (port wisp #571, #572)
- Dashboard footer lists the 5 most recent transactions instead of 1
- Delete wallet replaced by a switch-wallet flow: disconnect to use the
  default wallet or restore another; copy clarifies funds stay safe
- NWC dashboard parity + wallet settings cleanup
- Spark settings no longer render the per-relay backup status list
2026-06-12 12:52:34 -04:00
The Daniel c030accc29 feat(wallet): redesigned wallet front end + nsec-derived default wallet (port wisp #532, #539, #548, #549, #561, #562, #566)
- Default Spark wallet deterministically derived from the user's nsec
  (HKDF seed derivation), with onboarding auto-setup and default-wallet
  backup banner
- Two-tier connect screen: Spark vs NWC chooser with colored buttons
- Wallet dashboard parity for both Spark and NWC: expandable wallet
  info card, lightning address row, transaction history polish
- Balance display cycles sats / fiat / hidden
- NWC connect screen redesigned to match iOS: paste/scan split card,
  connection hint, close pill
- NsecPasteGuard blocks pasting an nsec into connection/seed fields
2026-06-12 12:52:34 -04:00
The Daniel 927ecdaae8 feat(ui): credit-card style wallet icon in bottom nav and drawer (port wisp #547) 2026-06-12 12:52:34 -04:00
Barry DeenandGitHub 86eb6424b8 Merge pull request #10 from dmnyc/feat/wallet-nav-icon
feat(ui): credit-card style wallet icon in bottom nav and drawer
2026-06-12 12:13:58 -04:00
Barry DeenandGitHub 91afed827e Merge pull request #9 from dmnyc/feat/media-gallery
feat(media): horizontally scrolling image galleries
2026-06-12 12:13:29 -04:00
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
The Daniel 8863eeb293 feat(ui): credit-card style wallet icon in bottom nav and drawer (port wisp #547) 2026-06-12 11:29:56 -04:00
The Daniel 9ae6fdd896 feat(media): horizontally scrolling image galleries (port wisp #527)
- MediaCarousel: horizontal swipe gallery for multi-image posts with
  page indicators
- FullScreenMediaPager: swipe between images in full screen
- ZoomableAsyncImage: pinch-zoom, pan, double-tap and swipe-down dismiss
- Media layout setting (gallery/stacked) in Interface preferences
2026-06-12 11:29:55 -04:00