987 Commits

Author SHA1 Message Date
ΔLΣX 🌸
62f63d031b
Merge c464d60bb156001fbe9c3f8f6c2b1b6f469c263d into 1f24973b06ee77492742ba522002e028f38f4381 2026-11-08 06:20:45 +00:00
GitHub Action
1f24973b06 Automated update of relay data - Sun Nov 8 06:10:18 UTC 2026 2026-11-08 06:10:18 +00:00
heyaim
2fc6f47472
Clear unsent chat text when panic wipe is confirmed (#1027)
The chat screen stored the unsent message separately from the state cleared
by panic wipe, leaving old text visible after completion. Mention
suggestions could also put text back into the cleared input.

Cleared the input and its command and mention suggestions when the user
confirms the wipe. Rejected mention selections when suggestions are hidden
or the nickname is no longer in the current list, including older click
handlers and rows still visible during their closing animation.
2026-11-06 15:54:31 -08:00
petitbonney
c464d60bb1 Leave the joined channel when opening a geohash link
A joined channel stayed on screen while messages went to the linked location channel. Clear only the joined channel, because switchToChannel(null) also resets the location and the open private chat.
2026-11-06 11:58:11 +01:00
petitbonney
b3e93d537e Open location channels from bitchat://geohash links
Match the iOS client, which already opens these links and includes them in channel invites. Links are validated the same way: lowercase, 2 to 12 characters, geohash base32 only.
2026-11-06 11:00:11 +01:00
GitHub Action
998f168487 Automated update of relay data - Sun Nov 1 06:11:17 UTC 2026 2026-11-01 06:11:17 +00:00
callebtc
20501fdfe0
Merge pull request #1020 from permissionlesstech/release/2.2.0
Prepare phone 2.2.0 and Wear 0.1.7 releases
v2.2.0
2026-10-27 22:16:12 +01:00
callebtc
86d6b415db Prepare 2.2.0 release 2026-10-27 18:27:44 +01:00
callebtc
638a5dcc6e
Merge pull request #1019 from permissionlesstech/claude/geohash-message-cap-eviction
Evict the oldest capped geohash message, not the first to arrive
2026-10-27 18:25:19 +01:00
callebtc
5653d6fc7d
Merge pull request #998 from permissionlesstech/claude/priceless-ishizaka-75044f
Make Mesh Lab receive hooks tolerate device clock skew
2026-10-27 17:57:22 +01:00
callebtc
d193192800 Evict the oldest capped geohash message, not the first to arrive
Relays return stored events newest first, and the globe backfills from
many relays at once, so the first message inserted into a channel is
often its newest. Dropping by arrival order at the 500-message cap kept
older messages instead, which could expire while discarded newer ones
were still inside the hour, undercounting or zeroing a channel's chat
activity. Drop the message with the oldest timestamp.
2026-10-27 17:56:20 +01:00
callebtc
078bba56f3
Merge pull request #1017 from permissionlesstech/claude/geohash-activity-markers
Show live geohash activity on the globe picker
2026-10-27 17:53:43 +01:00
callebtc
19a6474eae Keep geohash chat writers counted beyond the message cap
Chatters were derived from the retained messages, which are capped at
500 per channel, so in a busy channel someone who wrote ten minutes ago
dropped out of the headcount and roster once 500 newer messages arrived,
and prune then forgot their last-seen time. Track each writer's newest
chat time separately; the cap now bounds only the message count.
2026-10-27 17:44:49 +01:00
callebtc
53ea6a2e90 Let the globe's minus button pull back to the whole globe at level 1
The opening rotation lands at about 4x zoom while keeping level 1, and
minus was enabled only above level 1, so it was dead with most of the
globe off screen. Minus now also zooms out to the whole globe from a
zoomed-in level 1, and disabled rail icons are dimmed; their fixed tint
had hidden the disabled state.

Zooming out in place exposed a crash in the flight path: arc() returns
rounding noise rather than zero for an unmoved center, and for such a
tiny arc ln(sqrt(b^2 + 1) - b) cancels to ln(0), making the zoom NaN.
The whole-globe button made the same flight. Use the equivalent
-asinh(b) and d3's zoom-only threshold.
2026-10-27 17:39:47 +01:00
callebtc
e1eafeaf5a Normalize globe flight progress by the arc only once
The flight curve's position already divides by the arc length, so
dividing again was only right for one-radian flights: longer arcs
stopped short and snapped onto the target on the last frame, shorter
ones overshot. Cover non-unit arcs in the tests.
2026-10-27 17:29:33 +01:00
callebtc
c9cae5841f Zoom the opening rotation further, short of two-letter cells
The opening rotation now zooms from 72% of the whole globe to 80% of
the zoom at which autoPrecision switches to two-letter cells (~4.1x),
so it lands on a regional view while the selection stays at level 1.
The end zoom is derived from the same cell-size rule autoPrecision
uses (thresholdZoomForPrecision), so it cannot drift past the threshold.

The intro stays on the glide path so rotation and zoom move together,
and a fly-to that starts below the minimum zoom (an interrupted intro)
no longer snaps up on its first frame.
2026-10-27 15:35:27 +01:00
callebtc
e5d232004e Bring back a gentle zoom-in to the globe's opening rotation
The opening rotation had lost the zoom-in it used to have. It now
starts at 72% of the whole-globe size and grows into it while turning
to the location, as one 1.4 s eased move, so the selection stays at
level 1 and the entire globe stays in view.

Pinching during an interrupted intro continues from the current size
instead of snapping up to the minimum, and never pinches further out.
2026-10-27 15:32:10 +01:00
callebtc
a3d7c14276 Smooth deep globe flights and open the picker at level 1
Flying into a fine geohash was fast, rough and jittery:

- animateTo panned linearly while zooming exponentially over a fixed
  duration, so near the end the remaining fraction of a degree became
  a huge on-screen jump (the target was still ~130 screens away at 99%
  progress). Large zoom changes now follow GlobeFlight, the van Wijk
  and Nuij smooth pan-and-zoom path used by d3-zoom and Mapbox flyTo,
  along a great circle, with duration scaled to the distance.
- The target precision was forced for the whole flight, so a fine cell
  was re-selected nearly every frame, recomposing the card each time.
  Precision now follows the zoom and lands on the target at the end.
- Every selection change fired a haptic tick, buzzing through any
  programmatic flight. Ticks now fire only for user-driven changes.
- The view center was a Float; at deep zoom one Float step of latitude
  spans several pixels. It is now a Double.

Opening the picker on a selected geohash no longer dives into it: it
plays the same whole-globe rotation as the no-selection case, turning
to face that location at level 1 so the world's activity stays in view.
2026-10-27 15:04:29 +01:00
callebtc
12703a405b Track relay connectivity for the globe status chip
Addresses review feedback: connectivity was checked once, after the
initial grace period, and the publish loop could only ever set LIVE. A
connection lost while the picker was open stayed LIVE forever, and a
connection made after the grace period left a quiet globe OFFLINE until
traffic arrived.

The monitor now re-derives its status on every 500 ms tick from its own
relays' connection state, via activityStatus(): LIVE when connected and
either traffic has arrived or the grace period passed, OFFLINE when no
relay is connected after it, CONNECTING otherwise. Relay state is
polled because the relay manager mutates its relay objects in place, so
its relay list flow can drop connection changes as equal values.
2026-10-27 14:53:00 +01:00
callebtc
30351a6b35 Calm the globe marker animation to an in-place fade
The pop-in overshoot, staggered cascade and markers flying into and out
of clusters were too much motion. Markers now stay exactly where the
globe puts them: a new one fades in while growing from 60% to full size
over 200 ms with an ease-out and no overshoot, and a vanished one fades
out while shrinking slightly over 150 ms. Cluster size easing, the limb
fade and the system animator scale are unchanged.
2026-10-27 13:24:14 +01:00
callebtc
1494ba9634 Refresh globe online counts as the presence window lapses
Addresses review feedback: the monitor republished its snapshot only
when an event arrived or prune removed something. A chatter stays
listed for the hour, so prune keeps them after their five-minute online
window ends and returns false; the published online count then stayed
inflated for up to an hour.

The 15-second prune tick now always recomputes the snapshot (the
StateFlow drops unchanged ones). Only real traffic marks the sweep
LIVE, so a timed refresh cannot flip an offline globe to live.
2026-10-27 12:39:11 +01:00
callebtc
8dfc9454ac Fix inverted land fill and detail flicker on the globe
Land and sea swapped colors, and flickered, whenever a large landmass
crossed the globe's limb. drawLandFill decides whether to invert a
ring's fill by testing sample points with ringContainsLocation, which
cast its ray along a parallel using longitudes relative to the probe.
Edges near the probe's antimeridian wrapped from +180 to -180 and
landed near zero, so a Pacific probe saw Europe's coast as crossings:
Eurasia "contained" the Pacific and the middle of North America, its
fill inverted, and the result flipped as the view rotated. The test now
counts edges crossing the probe's meridian north of it, following each
edge the short way around, as the iOS globe does. GlobeLandFillTest
checks oceans and continents against the bundled Natural Earth land.

Moving frames also strobed between detail levels. Detail was re-chosen
every 8 frames and could step back up mid-gesture, but shedding detail
is what makes frames fast again, so slow devices oscillated and
neighbor cells, labels and borders flashed. GlobeMotionGovernor now only
steps detail down within a gesture, restores full detail at rest, and
starts the next gesture at the level the device sustains.

The geohash grid, neighbor cells and city dots now draw at every detail
level instead of vanishing on touch and popping back on release; only
borders, city labels, graticule density and coastline resolution vary.
2026-10-27 12:26:29 +01:00
callebtc
bf5e5dd6c8 Animate globe activity markers in and out
Markers used to pop on and off as data arrived, the globe turned, or
zoom merged and split clusters. GlobeMarkerAnimator tracks markers by
their busiest channel between frames:

- New markers pop in with a slight overshoot (~280 ms); a new batch is
  staggered over ~110 ms so it cascades rather than flashing at once.
- Vanished markers shrink and fade out quickly (~160 ms).
- On zoom, a marker merging into a cluster flies into it and markers
  splitting from a cluster fly out of it; cluster size changes ease.
- Markers fade and shrink toward the limb instead of being culled at
  a depth threshold.

Durations follow the system animator scale, so Remove animations makes
every change instant. The layer reads the frame clock only while a
marker is moving or pulsing, so a settled globe still stops redrawing.
2026-10-27 12:12:22 +01:00
callebtc
cd9af3a20d Verify every globe activity event before counting it
Addresses review feedback on the observe-only sweep, which bypasses the
relay manager's checks:

- Signature verification was skipped for events that only refreshed
  known activity, so a relay could replay a known chat id with new
  pubkeys or keep people online with unsigned heartbeats. A new
  VerifiedEventFilter verifies every distinct event once and drops
  repeats of an admitted id without reading their fields. Failed ids
  are not remembered, so a forged copy cannot block the genuine event.
- Proof-of-work ran before verification and throws on a non-hex id,
  which stopped the inbox consumer. Verification now runs first, and
  each event is ingested inside its own error boundary.

GeohashActivityAggregator.wouldGrow is removed with its only caller.
2026-10-27 12:03:37 +01:00
callebtc
af7ae15c11 Count channel people with the globe's activity rule
The channel sheet and the globe used different code to count people in
a geohash, so the numbers disagreed. GeohashActivityAggregator is now
the single rule for both: anyone with a heartbeat or message in the
last five minutes, plus anyone who wrote in the last hour.

- GeohashParticipantStore keeps its API but derives its participant
  set from the aggregator. Chat events pass kind, id and content
  through the new GeohashRepository.recordActivity so authors are kept
  for the hour; geohash DMs still count as presence.
- The aggregator works in milliseconds and clamps future timestamps to
  now, keeping the store's guard against skewed clocks.
- The globe adopts the sheet's filters: blocked users are excluded from
  every count, and the proof-of-work gate applies to chat events.
- The 5-minute cutoffs scattered through GeohashRepository are gone;
  counts and nickname disambiguation read the already-filtered set.
2026-10-27 11:33:36 +01:00
callebtc
b56ed5bb79 Show live geohash activity on the globe picker
The globe picker now shows where people are chatting, in the spirit of
bitmap.lat. While it is open, a monitor subscribes to kind 20000 (last
hour) and kind 20001 (last five minutes) with no geohash filter, on a
worldwide spread of relays plus the few nearest the resting view. Relays
it opened are released when the picker stops.

- Dots are sized by people and colored by chat volume; heartbeat-only
  channels draw as quiet rings, recent chat pulses, and overlapping dots
  merge into clusters that split as you zoom.
- Tapping a dot flies to that geohash and selects it; tapping a cluster
  zooms in and lists its busiest channels. The card shows people and
  messages per hour, a Join button, and the busiest chat inside the cell.
- Clients heartbeat into region, province and city channels under
  separate identities, so people are estimated from the busiest
  precision level instead of summed.
- Signatures are verified for any event that would add a person or a
  message, so forged events cannot inflate counts.
- Observe-only subscriptions bypass the shared deduplicator so the
  sweep never consumes events the open chat still needs.

The picker chrome is redesigned: top bar with a live status chip, a
zoom rail with a whole-globe button, gesture hints that fade after
first touch, and opaque surfaces that no longer show their own shadow.
With no starting geohash it opens on the whole globe.
2026-10-27 11:11:15 +01:00
callebtc
eed88190c0
Merge pull request #1013 from permissionlesstech/claude/wear-os-ui-audit-990f1a
Polish Wear OS UI across all screens
2026-10-26 18:07:20 +01:00
callebtc
0196844eac Refuse demo seeding on a person-chosen identity
ui_demo_seed copied the installation's nickname into the demo
conversation, so a debug build on a real watch could put a real name
in screenshots and mix demo rows into real history. Seeding now runs
only on an auto-generated nickname (or the demo name it set itself),
and switches the install to a fixed demo name.
2026-10-26 17:58:43 +01:00
callebtc
f9648c8dd4 Polish Wear OS UI across all screens
- Apply the bitchat mono font to every Wear Material type role; the
  tokens name the device font, so defaultFontFamily missed titles,
  buttons, dialogs and the clock.
- Swipe-to-dismiss navigation that reveals the previous screen, with
  push/back entrance motion instead of a double-exposed crossfade.
  Revealed screens stay passive so a cancelled swipe changes nothing.
- App-wide TimeText on list screens, hidden in chat, faded during swipes.
- Edge buttons for setup and verification actions; the Wear confirmation
  layout for the alerts prompt. No auto keyboard on first-run nickname.
- People: uniform rows with unread pills. Live push-to-talk moves to the
  menu as a SwitchButton; Quit confirm uses the error color.
- Push-to-talk overlay: edge budget ring, cancel hint, cleaner cancel
  morph. Text input names its destination and matches the action bar.
- Fix history rows replaying their entrance on scroll, edge-fade dimming
  text inside the display, a lit waveform bar at rest, and plural and
  capitalization slips in content descriptions.
- Aspect-aware full-screen image fit for round displays.
- Condense copy throughout for 192 dp screens.
2026-10-26 17:48:55 +01:00
callebtc
2375946030 Add debug Wear UI demo seed hook
Debug-only test hook commands that fill the watch UI with synthetic
peers, messages, an image and a voice note for screenshot reviews,
without sending packets: ui_demo_seed, ui_demo_peers and
ui_demo_clear_peers.
2026-10-26 17:48:55 +01:00
callebtc
b8016545e0
Merge pull request #1010 from permissionlesstech/release/2.1.0
Prepare phone 2.1.0 and Wear 0.1.6 release versions
v2.1.0
2026-10-26 14:43:02 +01:00
callebtc
a9400acb35 Merge branch 'main' into release/2.1.0 2026-10-26 12:55:32 +01:00
callebtc
97db392d2a
Merge pull request #996 from permissionlesstech/claude/ios-features-android-port-ebe483
Port iOS geohash DM, payload cap, and panic confirm fixes
2026-10-26 12:43:17 +01:00
callebtc
57bb99c170 Merge remote-tracking branch 'origin/main' into claude/ios-features-android-port-ebe483
# Conflicts:
#	app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt
2026-10-26 12:40:40 +01:00
callebtc
c8b10c5e71
Merge pull request #997 from permissionlesstech/claude/geohash-chat-autoscroll-bug-bae434
Keep following new messages in full chat timelines
2026-10-26 12:39:15 +01:00
callebtc
405297fd70
Merge pull request #1003 from swedishfrenchpress/fix/verification-sheet-stacking
fix(ui): stack verify sheet over the network sheet
2026-10-26 12:38:48 +01:00
callebtc
c7db79fa37
Merge pull request #1008 from permissionlesstech/claude/wear-os-swipe-menu-d7f87d
feat(wear): add swipe-right app menu with quit option
2026-10-26 12:37:46 +01:00
callebtc
822213b551 fix(wear): hide page indicator on chat home pager 2026-10-26 12:36:55 +01:00
callebtc
404fe4a588 feat(wear): add swipe-right app menu with quit option
Swiping the public mesh chat to the right reveals an app menu page
(Wear HorizontalPagerScaffold). The menu shows mesh status and a
"Quit bitchat" button that, after confirmation, stops the mesh
foreground service and closes the task.

Swiping from the menu's left edge still falls through to system
swipe-to-dismiss. Page swipes are disabled while push-to-talk or the
full-screen image viewer owns the gesture.
2026-10-26 12:33:12 +01:00
callebtc
e4310e9df9
Merge pull request #1006 from permissionlesstech/fix/mesh-relay-concurrency
Fix concurrent security-cache cleanup and relay snapshots
2026-10-26 11:42:25 +01:00
callebtc
972bf44ee0 Merge remote-tracking branch 'origin/main' into claude/geohash-chat-autoscroll-bug-bae434
# Conflicts:
#	app/gradle.lockfile
2026-10-26 11:41:26 +01:00
callebtc
ac5d719a96
Merge pull request #984 from permissionlesstech/fix/play-release-crashes
Fix minified release QR scanner initialization
2026-10-26 11:36:28 +01:00
callebtc
f5419f0e90 Fix concurrent security cache and relay snapshots 2026-10-26 09:51:58 +01:00
callebtc
ec1a92fcfe Fix minified release scanner initialization 2026-10-26 09:50:36 +01:00
callebtc
79897d36ac
Merge pull request #1005 from permissionlesstech/fix/translation-lint
Complete missing locale resources
2026-10-26 09:41:01 +01:00
callebtc
ab57e209a1 Complete missing locale resources 2026-10-25 23:51:59 +01:00
callebtc
6091aa6163 Prepare phone 2.1.0 and Wear 0.1.6 release versions 2026-10-25 23:06:52 +01:00
callebtc
de26ef336b
Merge pull request #1004 from permissionlesstech/claude/bubble-audio-contrast-light-de2b64
Improve voice note waveform contrast in light mode
2026-10-25 23:01:59 +01:00
callebtc
1e121fafad
Merge pull request #999 from permissionlesstech/claude/bitcohat-spam-detection-20c908
Add spam filter for public mesh and geohash chats
2026-10-25 22:34:50 +01:00
callebtc
844239d384 See through disguised copies in repeat checks
A copy with a zero-width space between letters, an accent on every
letter, or math-bold or fullwidth letters looks the same to a reader
but defeated both the per-sender and cross-user repeat checks.

Normalize text for comparison by applying NFKD (which folds fullwidth
and styled letters to plain ones and splits off accents), dropping
combining marks and invisible characters, collapsing whitespace and
lowercasing. Displayed and stored text is unchanged. Input is capped
before normalizing to bound the work per message.
2026-10-25 21:25:17 +01:00