Commit Graph
1170 Commits
Author SHA1 Message Date
Barry DeenandGitHub 80e54a14e7 Merge pull request #530 from barrydeen/refactor/google-deterministic-accounts
refactor(auth): derive Google-linked nsecs deterministically (replaces Drive backup)
2026-05-14 12:08:59 -04:00
Barry Deen 3f3d277ef1 feat(onboarding): always include relay.wisp.talk in new user's NIP-65
When a new user finishes profile setup, OnboardingViewModel.finishProfile
saves the discovered+tested relays locally and publishes a kind 10002
relay list event. Insert wss://relay.wisp.talk into that list before
saving and publishing, so every new Wisp account advertises Wisp's relay
to the network from day one.

Read+write so the new user both publishes to and reads from it. Dedup
on URL (case-insensitive) so we don't duplicate if probing already
discovered the relay independently.

Reverts the earlier change to RelayConfig.DEFAULTS — that was the wrong
place. The DEFAULTS list is a fallback for users without an onboarding
flow; new-account injection belongs in onboarding where the relay list
event is actually constructed and published.
2026-05-14 12:05:05 -04:00
Barry Deen 86b7b885bb feat(relays): add relay.wisp.talk to default read+write relays
Adds Wisp's own relay to RelayConfig.DEFAULTS so every new account
picks it up automatically. Covers all signup paths via the existing
loadRelays() fallback — Sign Up, Google auto-create-at-0, Google
"Create another account", and read-only npub login.

Existing users with an explicitly-saved relay list are unaffected
(their stored list takes precedence). Existing users who never saved
a custom relay list will pick up wisp.talk on next load.

Read+write so new accounts both publish to and read from it.
2026-05-14 12:02:05 -04:00
Barry Deen de88bdd89a fix(auth): skip chooser screen when no accounts found
Previously a first-time user with no Nostr activity yet would land on a
chooser screen with an empty list and a "Create your account" prompt
explaining the deterministic-derivation model. Cuts an extra tap and
some explanatory copy.

Now: if the probe returns zero accounts, auto-derive the keypair at
index 0 and transition directly to ONBOARDING_PROFILE. The chooser
screen only appears for users who actually have existing accounts to
pick from.

Deterministic derivation means auto-creating at index 0 is safe: if a
later sign-in (e.g. after a flaky probe) discovers existing activity
at index 0, the same nsec is regenerated, so no identity is lost.
2026-05-14 11:45:46 -04:00
Barry Deen 3d3c6e12ef refactor(auth): derive google-linked nsecs deterministically
Replaces the Google Drive backup flow (#528) with deterministic key
derivation. The user's Nostr identity IS their Google account — no
encrypted blobs to store, no backup events to publish, nothing for
Google or any third party to retain.

   privkey = SHA-256("wisp-account-v1:" || sub || ":" || accountIndex)

Properties:
- Same Google account always derives the same nsec on any device
- No backup to lose: signing in regenerates the keys
- No `drive.appdata` OAuth scope, no scary Drive consent dialog
- Anyone with access to the Google account can derive every nsec.
  Bounded by Google account security — same trade-off as #528, with
  a much simpler attack surface and no third-party storage layer

Discovery on sign-in:
- Derive candidate keypairs for indices 0..15 from the user's `sub`
- One REQ to relay.damus.io, relay.primal.net, nos.lol, nostr.wine,
  relay.wisp.talk, relay.ditto.pub asking for kind 0/3/10002 events
  from those pubkeys
- Pubkeys with any activity = "in use" accounts that go in the chooser;
  avatar + display name come from the same kind-0 events
- "Create another account" derives the next-unused index

Code shrinkage: DriveBackupService is gone, BackupCrypto's encryption
helpers are gone, the play-services-auth dependency is gone, and the
Drive-related ProGuard rules are gone. The whole flow is ~200 fewer
lines than #528 and easier to audit — the derivation is one line of
SHA-256.

Splash button switches to Google's dark-mode brand variant (#131314
container, full-color G, #8E918F stroke) per Sign in with Google spec.

No migration needed: nobody is on the #528 flow yet.
2026-05-14 11:40:37 -04:00
Barry DeenandGitHub 403b25ac90 Merge pull request #528 from barrydeen/feat/google-drive-backup
feat(auth): Continue with Google + encrypted nsec backup to Drive
2026-05-13 22:06:41 -04:00
Barry Deen 270425aa77 feat(auth): continue with google with encrypted nsec backup to drive
Adds a third splash entry point alongside Sign Up / Log In:

- Credential Manager + Google Identity for sign-in; AuthorizationClient
  scopes drive.appdata so Wisp can read/write a hidden per-app folder
- Per-account backup files named wisp_nsec_<npub>.bin in appDataFolder.
  One Google account can back up many Nostr identities; sign-in lists
  every backup found and the chooser shows each with avatar + display
  name fetched from relay.damus.io / relay.primal.net (kind 0)
- Encryption key derived via HMAC-SHA256 from the Google sub claim,
  reusing Nip44's XChaCha20 + HMAC building blocks for the payload.
  Plaintext nsec never leaves the device
- "Create another account" is always available, even when prior backups
  exist, so users can keep adding identities without overwriting
- Google-branded splash button (white, full-color G logo) per Sign in
  with Google brand spec
2026-05-13 22:04:45 -04:00
Barry DeenandGitHub 517af030ba Merge pull request #527 from dmnyc/feat/media-layout-gallery
feat(media): horizontal swipe gallery for multi-image posts
2026-05-13 19:50:19 -04:00
The Daniel c3d9d4d319 fix(media): velocity-aware swipe-down dismiss
Previously the dismiss only fired once raw drag distance cleared 120px,
so a quick downward flick that released early stayed put. Match the
iOS counterpart in barrydeen/wisp-ios#117 — feed positions into a
VelocityTracker during the drag and on release project the end
translation by 0.3s of velocity, committing the dismiss when that
projection clears 120px. A slow drag still needs to cross the raw
threshold.

Applies to both ZoomableAsyncImage (image pages in the pager and the
standalone single-image viewer) and VideoPagerPage (video pages in
the pager). Horizontal page flicks already worked via Compose's
default PagerDefaults.flingBehavior — no change needed for those.
2026-05-13 17:23:06 -04:00
The Daniel 447b3f34a6 feat(media): horizontal swipe gallery for multi-image posts
Adds a "Multi-image layout" setting (Gallery / Stack), default Gallery,
under Settings → Interface → Media.

Gallery mode collapses 2+ consecutive image/video segments inside a post
into a horizontal carousel — tiles are 4:5 with the next one peeking
from the right and an overlaid "n / N" counter at the bottom. The final
tile snaps flush to the right edge. Whitespace-only text between media
URLs is treated as a joiner so two images on separate lines still group.

Tapping any tile (gallery mode) or any inline image (stack mode) opens
a fullscreen pager:
- Horizontal swipe between images
- Pinch-zoom 1-4x with pan clamped to image bounds
- Double-tap toggles 1x ↔ 2x with the tap point pinned
- Swipe-down at 1x dismisses with a background fade
- Existing Download / Copy URL / Close action row

Single-image posts also route through the pager (one-page case), so the
viewer is consistent regardless of layout style.

Gestures are implemented in a single unified awaitEachGesture loop so
horizontal single-finger drags fall through to the parent HorizontalPager
for paging, while vertical drags drive swipe-to-dismiss.

8dp vertical spacing between every block in RichContent for breathing
room in stack mode and between text/media boundaries.

Test plan:
- Gallery mode: multi-image post shows carousel with peek-right and
  bottom counter; last tile snaps flush right; tap opens pager
- Stack mode: multi-image post stacks vertically with 8dp gaps; tap any
  image opens the swipeable pager at that image's index
- Fullscreen pager: swipe horizontal pages, pinch-zoom, double-tap,
  swipe-down dismiss with bg fade
- Single-image post: tap opens swipeable pager (one page)
2026-05-13 16:18:28 -04:00
Barry DeenandGitHub e5b379a08f Merge pull request #526 from barrydeen/feat/mention-relay-search-fallback
feat(mention): query search relay when local results are insufficient
2026-05-13 11:52:02 -04:00
Barry Deen 28bdd4a7c0 feat(mention): query search relay when local results are insufficient
When typing @mentions, first check local follows and cached profiles.
If fewer than 5 matches are found, query wss://search.nostrarchives.com
(NIP-50) and stream results into the suggestion list as they arrive.
The relay subscription is cancelled on each new keystroke and cleaned
up on EOSE or after a 3-second timeout.

Also adds @mention autocomplete to the livestream chat input, which
previously had no mention support.
2026-05-13 11:51:03 -04:00
Barry DeenandGitHub 0335e5cf2e Merge pull request #525 from barrydeen/fix/panning-speed
Fix panning speed slow down when zoomed in
2026-05-13 11:18:10 -04:00
Barry Deen 4c3ff50e49 Fix panning speed slow down when zoomed in 2026-05-13 11:17:35 -04:00
Barry DeenandGitHub e2ea4c728b Merge pull request #521 from dmnyc/feat/keys-remote-signer-details
feat(keys): show remote signer details on the Keys screen
2026-05-12 22:59:12 -04:00
Barry DeenandGitHub d8e29c5cf7 Merge pull request #523 from greenart7c3/claude/add-auto-translate-notes-1BCVd
Add auto-translate feature for notes
2026-05-12 22:58:52 -04:00
Barry DeenandGitHub 968e89d18d Merge pull request #522 from greenart7c3/claude/fix-relay-background-disconnect-wbnMC
Pause/resume local relay on app lifecycle events
2026-05-12 22:55:37 -04:00
Claude 8eb0841645 feat(translation): add auto-translate notes setting
Adds an opt-in Interface setting that auto-triggers the existing on-device
ML Kit translation pipeline as notes render, so foreign-language notes are
translated without tapping each one. The toggle is off by default and the
manual Translate menu item still works.
2026-05-11 13:18:34 +00:00
Claude bdd5c192eb fix(relay): disconnect local relay when app is backgrounded
The local relay socket was managed in a separate code path from
regular relays and was excluded from the lifecycle manager's
reconnect iterations. As a result it stayed connected indefinitely
across app backgrounding and only ever closed on account switch or
config change.

Add pauseLocalRelay/resumeLocalRelay helpers on RelayPool that drop
the local socket on pause and bring it back on resume while keeping
the localRelay / localRelayConfig references intact, and wire them
into RelayLifecycleManager.onAppPause / onAppResume. Also re-enable
reconnectEnabled in updateLocalRelay's same-URL branch so toggling
the relay off and back on while paused recovers cleanly.

https://claude.ai/code/session_01YHsgf89CpMXtj2wETzX588
2026-05-11 11:56:45 +00:00
The Daniel 3e3af77b70 feat(keys): show remote signer details on the Keys screen
For accounts using a remote signer the Keys screen now replaces the
"Reveal Private Key" affordance with a Remote Signer section, matching
the layout shipped on iOS:

- Section heading + subtitle explaining the private key never leaves
  the signer app
- Status pill (Checking… / Available / Signer app not installed) with
  a pulsing dot while checking
- Refresh button to re-probe
- "Signer App" row showing the recorded signer package id

Adapted to the NIP-55 protocol Android uses (Intent / ContentResolver
to a local signer app, no relay-mediated RPC): the health probe is a
PackageManager `getPackageInfo` lookup on the package recorded at
sign-in. iOS's relay-list and transport-pubkey fields don't apply
under NIP-55 and are intentionally omitted.

The local-keypair path (`SigningMode.LOCAL`) is unchanged — same
Reveal Private Key button + nsec card + warning text as before.
2026-05-11 00:07:25 -04:00
Barry DeenandGitHub b4e361a82c Merge pull request #519 from dmnyc/feat/zap-fiat-input
feat: register-style fiat input on the zap dialog
2026-05-06 23:30:39 -04:00
The Daniel 839f92cc59 feat(zap): register-style fiat input + drop trailing zeros + sending wording
Mirrors the iOS work (barrydeen/wisp-ios): the zap dialog's custom
amount input is fiat-aware and uses register-style entry — the user
types digits, they fill from the cents place ('21' -> $0.21,
'2100' -> $21.00), and the field renders the formatted dollar string
with the configured currency's symbol.

Implementation notes:

* Field is bound to the raw digit string and a [VisualTransformation]
  renders the formatted dollar view. Compose's cursor lives in raw-
  string coordinates and the OffsetMapping pegs the cursor to the end
  of the formatted view, so backspace removes the rightmost digit
  cleanly. The earlier String-only attempt — binding the field to the
  formatted string and re-formatting in onValueChange — broke
  backspace on Android: when Compose's internal text changed from
  '$0.2' to our re-formatted '$0.02', it couldn't map the old cursor
  position into the new string and snapped it to the start, so
  subsequent backspaces just moved the cursor instead of deleting.

* New ExchangeRateRepository.fiatToSats(majorAmount, currency) — the
  inverse of satsToFiat — converts the typed (cents / 100) dollar
  amount through the cached BTC-to-fiat rate.

* AmountFormatter.renderCurrency drops trailing zeros for sub-dollar
  amounts ('$0.84' instead of '$0.840', '$0.8' instead of
  '$0.800') by switching the DecimalFormat patterns to '#' optional
  digits past the decimal. Whole-dollar amounts still pad to two
  places ('$1.00') matching retail convention.

* zap_x_amount string flips from 'Zap %1$s' to 'Send %1$s' so the
  Send button reads 'Send $X.XX' in fiat mode (was 'Zap $X.XX').

* LiveStreamScreen's zap button reads 'Send' / 'Sending...' in fiat
  mode, 'Zap' / 'Zapping...' otherwise — matches the surrounding
  wallet UX.

Companion to the iOS PR for the same UX (cross-linked at filing time).
2026-05-06 19:39:09 -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
fiatjaf e0c2c7c9e9 use a different id and name for the app when doing a debug build. 2026-04-29 16:18:33 -03:00