Show ic_spec_lock_open while idle or handshaking, then Crossfade to the
closed lock on success or failure — timed with the existing tint wash so
the shackle settling reads as one smooth transition.
Co-authored-by: Cursor <cursoragent@cursor.com>
Match private-chat Noise status to the Tor globe treatment: one lock
glyph with an orange pulse while handshaking, then green or red with
smooth tint cross-fades — no sync/recycle swap.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align conversation exit controls with bottom-sheet close chrome so the X reads as primary green rather than muted grey.
Co-authored-by: Cursor <cursoragent@cursor.com>
* first pass
* pass 2
* cleanup
* capitalization
* strings
* input bar fixes
* fixes
* notes
* nice
* nicer
* lists
* cleanup
* button
* fixes
* animations
* Fix layout jumpiness in chat and geohash people list
Three separate causes of things moving when they should not:
- Chat lurched whenever a bottom sheet closed. Placement animation is meant
to soften insertions and removals, but any relayout moves every item --
a sheet's text field opening the keyboard changes the chat's IME inset,
and closing it changes it back. Placement animation is now armed only
briefly around a real change to the message list, so items otherwise
track the viewport exactly.
- Anon list changed height as participants churned. Rows sized to their
content, so any reorder could change the card's height; and the card
sized to the live anon count, which moves constantly in a busy geohash.
Rows now have an exact height, and a trimmed anon card reserves the full
capped height regardless of how many are present beyond the cap.
- Anons are now their own trailing section rather than a tail on each of
"on location" and "teleported in", which had pushed the few recognisable
names out of view twice over. Self is never grouped as an anon.
Adds 7 tests covering the sectioning and the fixed-length behaviour.
* Group geohash people as People and Anon
Replaces the "on location" / "teleported in" / "anonymous" split with two
sections: peers who announced a nickname, then the anons.
Teleport state was never worth a section of its own -- every row already
carries it as a distinct glyph -- and splitting on it fragmented the short
list people actually read, in a channel where most participants are
anonymous anyway.
Self stays in the People section even when unnamed.
* Key message list state per conversation
Switching channels reused every piece of state in MessagesList, because none
of it was keyed on which conversation was being shown:
- The LazyListState carried the previous channel's scroll offset, so the new
channel opened at a stale position and then corrected itself.
- hasScrolledToInitialPosition and followIncomingMessages carried over, so a
channel entered after scrolling up in another one did not land on its
newest message at all.
- The arrival tracker had never seen the incoming channel's ids, so a
backlog of six or fewer messages was treated as six simultaneous arrivals
and each one slid in.
- previousMessageCount carried over, arming placement animation for the
relayout that the switch itself caused.
All of it is now keyed on a conversationKey derived the same way
displayMessages is. The tracker also detects a list sharing no ids with the
previous one and adopts it silently, which covers /clear and any caller that
does not supply a distinct key.
Adds 4 tests for wholesale replacement, including the case that the burst
cap cannot catch on its own.
* fix location channel layout
* icon
* location sheet
* move location error
* fix location channel lifecycle bug
* remove empty lable
* geist mono
* timestamp no seconds
* new icons
* icons
* cleanup
* mentions
* fix mentions
* grouping of geohash channel list
* colors
* fix mention colors
* Bring private and group chat headers up to the main header's layout
Both conversation headers were built on TopAppBar with a centred title, a
back arrow on the left and everything else crowded into the title slot, at
14sp with 14dp icons. Moving between the timeline and a conversation visibly
shifted the bar's height, insets and type.
Introduces ConversationHeader, built from the main header's own tokens rather
than TopAppBar: same ChatHeaderHeight, same 12/8dp edge insets, leading glyph
in a 44dp slot so it lands exactly where the brand mark does, same -6dp
optical nudge pulling the title toward it, same 17sp label.
- Drops the back button; the close action on the right is the way out.
Leaving a channel outright already lives on its row in the network sheet,
so it does not need a second home beside the exit.
- Leading glyph is the transport: globe over the internet, wifi/bluetooth/
routed on the mesh, matching the main header's channel button.
- Actions are right-aligned and unweighted -- favourite, encryption state,
close -- so a long title yields space to them instead of pushing them off
screen.
- Private chat titles use the primary green like every other header label,
rather than orange for Nostr-reachable peers.
Height and edge insets now belong to each header variant instead of the
ChatFloatingHeader wrapper, which was applying them a second time to the
channel header.
Adds nine spec icons in the existing 20x20 / 1.25-stroke language -- bluetooth,
wifi, routed, close, check, warning, sync, lock_open, envelope -- so the
headers and peer rows no longer mix Material glyphs into the set.
* color
Switching channels reused every piece of state in MessagesList, because none
of it was keyed on which conversation was being shown:
- The LazyListState carried the previous channel's scroll offset, so the new
channel opened at a stale position and then corrected itself.
- hasScrolledToInitialPosition and followIncomingMessages carried over, so a
channel entered after scrolling up in another one did not land on its
newest message at all.
- The arrival tracker had never seen the incoming channel's ids, so a
backlog of six or fewer messages was treated as six simultaneous arrivals
and each one slid in.
- previousMessageCount carried over, arming placement animation for the
relayout that the switch itself caused.
All of it is now keyed on a conversationKey derived the same way
displayMessages is. The tracker also detects a list sharing no ids with the
previous one and adopts it silently, which covers /clear and any caller that
does not supply a distinct key.
Adds 4 tests for wholesale replacement, including the case that the burst
cap cannot catch on its own.
C1 from security review: SymmetricState/HandshakeState logged raw X25519
shared secrets, chaining keys, and handshake hashes in hex to logcat on
every handshake, in release builds. A logcat transcript of a handshake
allowed full session decryption. Both classes no longer log at all.
Also reduces excessive logging across the app (~50% fewer log calls in
the noisiest files):
- NoiseSession emits one line per completed handshake; per-message
encrypt/decrypt and per-handshake-step debug logs removed
- Removes all content/key logging: decrypted DM content, file names,
payload hex dumps, pubkeys, event IDs, lat/lon, peer IPs, arti log
forwarding
- Collapses multi-line banner/emoji log sequences into single factual
lifecycle lines (connect/disconnect, relay/Tor state transitions)
- Keeps security-relevant warnings (signature failures, replay
detection, key mismatches, panic wipe) in compact form
No logic changes. Includes the full security review report in
docs/security-review-jul-27.md.
Replaces the "on location" / "teleported in" / "anonymous" split with two
sections: peers who announced a nickname, then the anons.
Teleport state was never worth a section of its own -- every row already
carries it as a distinct glyph -- and splitting on it fragmented the short
list people actually read, in a channel where most participants are
anonymous anyway.
Self stays in the People section even when unnamed.
Three separate causes of things moving when they should not:
- Chat lurched whenever a bottom sheet closed. Placement animation is meant
to soften insertions and removals, but any relayout moves every item --
a sheet's text field opening the keyboard changes the chat's IME inset,
and closing it changes it back. Placement animation is now armed only
briefly around a real change to the message list, so items otherwise
track the viewport exactly.
- Anon list changed height as participants churned. Rows sized to their
content, so any reorder could change the card's height; and the card
sized to the live anon count, which moves constantly in a busy geohash.
Rows now have an exact height, and a trimmed anon card reserves the full
capped height regardless of how many are present beyond the cap.
- Anons are now their own trailing section rather than a tail on each of
"on location" and "teleported in", which had pushed the few recognisable
names out of view twice over. Self is never grouped as an anon.
Adds 7 tests covering the sectioning and the fixed-length behaviour.