mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 23:45:18 +00:00
* Make peer lists accessible and actionable; block by stable identity Who you can reach — the app's most important fact — was encoded in unlabeled 10pt icons with macOS-only tooltips, and the mesh list had no actions (block/favorite/verify were slash-command-only). - Both peer lists become real accessibility citizens: each row is one element announcing name, reachability, and favorite/unread/blocked state, with a button trait and custom actions for the gesture-only interactions. Neither file previously had a single accessibility modifier. Reachability icons gain tooltips reusing existing strings; teleported vs in-area pins are explained. - Mesh rows gain the context menu the geohash list already had: direct message, favorite, show fingerprint, block/unblock. The fingerprint double-tap, previously shadowed by the single tap, is reordered so it fires. - The DM header's offline state (previously EmptyView — absence of a glyph as the only signal) becomes a dimmed "offline" tag, and a geohash DM — always Nostr-routed — no longer mislabels itself "offline". - App Info gains a SYMBOLS legend defining every glyph the lists and headers use; nothing defined them before. - Mesh block/unblock now resolve by the peer's stable Noise identity instead of a `/block <displayName>` string, so the exact tapped row is affected and offline peers can be unblocked (with covering tests). New strings are added source-language (en) only. * Surface block/unblock feedback in the conversation where it was triggered setMeshPeerBlocked silently returned when the peer's identity could not be resolved (e.g. long-press-blocking an old public message from a sender who left and was never a favorite), where the /block command printed "cannot block X: not found or unable to verify identity" — post that same message from the guard branch. Both the failure and confirmation messages now route through addCommandOutput instead of addSystemMessage, so blocking from inside a private chat prints into that chat rather than invisibly into the public timeline (same routing #1363 applied to command output). The confirmation also reuses the /block wording ("blocked X. you will no longer receive messages from them") for parity with the command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Remove dead accessibility label and unreachable /unblock fallback The favorite button's .accessibilityLabel in MeshPeerList is unreachable: the row-level .accessibilityElement(children: .ignore) swallows child elements, and the row's custom accessibility action already covers favoriting. ConversationUIModel.unblock is only called from the mesh peer list with a non-optional mesh peerID, so the "/unblock <name>" fallback branch could never run — take PeerID directly and drop the branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: jack <212554440+jackjackbits@users.noreply.github.com> Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Test Harness Guide
This test suite uses an in-memory networking harness to make end-to-end and integration tests deterministic, fast, and race-free without touching production code.
In-Memory Bus
- File:
bitchatTests/Mocks/MockBLEService.swift - Registry/Adjacency: Global
registrymapspeerIDto aMockBLEServiceinstance;adjacencyrecords simulated links between peers. - Setup: Call
MockBLEService.resetTestBus()insetUp()to clear state between tests. - Topology: Use
simulateConnectedPeer(_:)andsimulateDisconnectedPeer(_:)to add/remove links.connectFullMesh()helpers in tests build larger topologies. - Handlers: Tests can observe data via
messageDeliveryHandler(decodedBitchatMessage) andpacketDeliveryHandler(rawBitchatPacket). - De‑duplication: A thread-safe
seenMessageIDsprevents duplicate deliveries during flooding/relays.
Broadcast Flooding
- Flag:
MockBLEService.autoFloodEnabled - Intent: When
true, public broadcasts propagate across the entire connected component (ignores TTL for reach) while still de‑duping to prevent loops. - Usage: Enabled in Integration tests (
setUp) to simulate large-network broadcast; disabled in E2E tests to keep routing explicit and verify TTL behavior (seePublicChatE2ETests.testZeroTTLNotRelayed).
Rehandshake Flow (Noise)
- Why: The legacy NACK recovery path was removed; recovery now relies on Noise session rehandshake after decrypt failure or desync.
- Manager:
NoiseSessionManagermanages per-peer sessions. - Pattern: On decrypt failure, proactively clear the local session and re-initiate a handshake. The peer accepts and replaces their session.
- Test:
IntegrationTests.testRehandshakeAfterDecryptionFailure- Corrupts ciphertext to induce a decrypt error.
- Calls
removeSession(for:)on the initiator’s manager beforeinitiateHandshake(with:)to avoidalreadyEstablished. - Verifies encrypt/decrypt succeeds post-rehandshake.
Tips
- Determinism: Add small async delays only where handler installation/topology changes could race the first send.
- Scoping: Keep
autoFloodEnabledtoggled only within Integration tests; always reset intearDown()to avoid cross-test contamination. - Direct vs Relay: Private messages target a specific peer when adjacent; otherwise they are surfaced to neighbors for relay and, if known, also delivered to the target.
Quick Start
- Create nodes and connect them:
let svc = MockBLEService(); svc.myPeerID = "PEER1"svc.simulateConnectedPeer("PEER2")
- Observe messages:
svc.messageDeliveryHandler = { msg in /* asserts */ }
- Enable broadcast flooding for Integration suites only:
MockBLEService.autoFloodEnabled = true