Commit Graph
168 Commits
Author SHA1 Message Date
IslamandGitHub d1682db79b Refactor 1/n: ChatViewModel's Message Sending section (#613)
* Extract BitchatMessage into a separate file

* Convert `fromBinaryPayload` to `convenience init?`

* Extract message dedup into an extension

* Remove dead `formatMessageContent`

* Minor refactor of timestamp and username formatting

* Remove dead `getSenderColor`

* Extract MessagePadding into a separate file

* Extract BitchatPacket into a separate file

* Extract ReadReceipt into a separate file

* Extract NoisePayload into a separate file

* Remove unnecessary import

* Extract peer-seed color calculation out

* Extract `handleDeliveredReadReceipt` to a new function

* Extract `handlePrivateMessage` to a new function

* Separate `delivered` and `readReceipt` functions

* Extract `handleGiftWrap` into a function

* Extract `subscribeToGeoChat` into a function

* Minor cleanup

* Extract `handleNostrEvent` into a function

* Minor cleanup

* Create `sendGeohash` function + minor cleanup

* Extract sending geohash dm into a function

* Check for blocks before trying to send a DM
2025-09-15 15:29:47 +02:00
IslamandGitHub ea8d51a36b Refactor: BitchatMessage (#610)
* Extract BitchatMessage into a separate file

* Convert `fromBinaryPayload` to `convenience init?`

* Extract message dedup into an extension

* Remove dead `formatMessageContent`

* Minor refactor of timestamp and username formatting

* Remove dead `getSenderColor`
2025-09-15 15:00:12 +02:00
IslamandGitHub 347ce5ece4 Modularization: Extract SecureLogger into a separate module (#600)
* Extract SecureLogger into a separate module

* Add BitLogger package as a dependency for iOS & macOS targets
2025-09-15 14:45:58 +02:00
2870acdcc7 Location Notes (kind 1) at Building Precision + Live Updates (#598)
* Location notes: Matrix loader with 1–3s minimum display; allow multiple notes; wire notes UI from mesh toolbar

* Location notes: prevent duplicate loading/subscriptions (StateObject manager, geohash captured at open, guard subscribe)

* Location notes: fix duplicate  state redeclaration in ContentView

* Location notes: render note bodies with monospaced font in notes list

* Location notes: add close (x) button in sheet header (upper-right) using @Environment(\.dismiss)

* Location notes: acquire geohash on open (force refresh) and show Matrix loader until block-level geohash resolves; add LocationNotesSheet wrapper

* Location notes: inline sheet wrapper to avoid missing file in target; force location refresh and show Matrix loader until block geohash resolves

* Location notes: remove Matrix animation; use simple spinner when acquiring location; simplify notes view layout

* Location notes: remove per-note relative timestamp from sheet (no seconds display)

* Location notes: fix intermittent first load by ensuring resubscribe after geohash change, removing over-eager subscribe guard, and widening note fetch window (no since filter)

* Location notes: add background counter service and show count next to notes icon on mesh; auto-subscribe to current block geohash

* Location notes: move notes icon to the right of #mesh badge in toolbar

* Location notes: restore timestamps in notes list; remove loading state from manager and sheet (no spinner/animation)

* Location notes: drop 'teleport' tag from kind-1 events; simplify note builder API and usage

* Location notes: show timestamp as relative within 7 days, else absolute date (MMM d or MMM d, y); keep monospaced styling

* Location notes: append 'ago' to relative timestamps (within 7 days)

* Switch location notes and UI helpers to building-level geohash (precision 8): add GeohashChannelLevel.building, map length 8, use building for notes selection and counter, add building name mapping

* Location notes: change notes toolbar icon to SF Symbol 'long.text.page.and.pencil'

* Revert notes geohash selection back to block-level (precision 7); keep building level available but unused; update counter and sheet accordingly

* Location notes: show block name (from reverse geocode) in header instead of 'street-level notes'

* Nostr: add EOSE handling with callback support for subscriptions; wire to LocationNotesManager/Counter (initialLoadComplete). Remove 'building' level from channel enum and geocoder mapping; geohash list shows block/neighborhood/city/province/region only

* Fix Swift 6 isolation: hop to @MainActor inside Timer callback for EOSE tracker mutations

* Notes counter: show 0 by default; subscribe at building-level (precision 8) for notes and counter; keep building hidden in channel list

* Notes header: show building name when available (fallback to block name)

* Notes: subscribe counter to building + parent block (merge results); hide notes icon unless location is authorized; replace 10s timer with live location updates while sheet open

* Notes counter: subscribe only to building geohash (precision 8) so count reflects current 8-cell; auto-begin live location refresh on mesh (and permission) to update as you walk

* Notes header: show count in parentheses; icon shows blue if any notes, grey if none; only start live location updates while sheet is open; reduce nickname width; set live distance filter to 10m

* Notes header: show count as '(N note/notes)' with non-bold, secondary styling next to title

* Notes header: move count before title as '<N> note(s) @ #gh'; remove parentheses; keep count non-bold

* Notes header: bold count text; ensure sheet updates when building geohash changes by calling manager.setGeohash on geohash change

* Notes sheet: add light haptic feedback when building geohash changes (iOS only)

* Notes icon: force a one-shot location refresh before (re)subscribing the counter so icon turns blue immediately on current 8-cell

* Notes subscribe: fallback to default relays when GeoRelayDirectory has no entries (pass nil relayUrls) so counter/icon turn blue and sheet loads even before georelay fetch

* Notes icon: turn blue immediately when sheet shows notes by passing count up from sheet (closure) and OR-ing with counter; reset on sheet close

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-13 14:55:04 +02:00
920dc31795 Refactor: Testable Keychain and Identity Manager (#584)
* Make static functions instance functions to be testable

* Injectable KeychainManager + Mock + updated tests

* Remove `pendingActions` from identity manager (dead code)

* Remove `getHandshakeState` from identity manager (dead code)

* Remove `getAllSocialIdentities` from identity manager (dead code)

* Remove `getCryptographicIdentity` from identity manager (dead code)

* Remove `resolveIdentity` from identity manager (dead code)

* Identity Manager: minor clean up

* Put Identity Manager behind a protocol

* Remove Keychain and Identity Manager singletons

* Tests: include MockKeychain/MockIdentityManager in project; init identityManager in CommandProcessorTests

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-12 14:37:34 +02:00
bb3d99bdca Fix repeated favorite notifications; route system messages to mesh; simplify favorites (#588)
* Favorites: mesh-only system message; stop reconnect resends; gate system on state change

* Favorites: remove npub resend tracking and nickname-based key migration; rely on Noise key as identity

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-12 14:36:32 +02:00
4b0634d1d0 Fix/general queue (#580)
* Fix emote targeting and grammar; add tests

Prevent 'system' mis-target via peerID-derived display name in actions sheet. Correct /hug and /slap usage/error grammar by passing base command name. Improve geohash nickname resolution to match displayName with #suffix. Add CommandProcessor tests.

* Geohash ordering: strict in-order inserts and timestamp clamp

Use channel-aware late-insert threshold with 0s for geohash to keep strict chronological order. Clamp future Nostr event timestamps to 'now' to avoid future-dated items skewing order in geohash timelines.

* Trim trailing/leading spaces in geohash nicknames and tag emission

Sanitize Nostr 'n' tag values on ingest and emit by trimming whitespace/newlines to prevent trailing spaces in displayed usernames. Local nickname is already trimmed on focus loss and submit.

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-11 21:02:48 +02:00
islam 5d6aecfc83 Replace .log w/ explicit .debug/.error functions
This would make the intention more explicit so we can overload different logging types as well like keychain, security events, etc…

Search/Replace Strategies:

1.
Search regex: `SecureLogger\.log\(\s*(.*?),\s*category:\s*(.*?),\s*level:\s*\.(\w+)\s*\)`
Replace regex: `SecureLogger.$3($1, category: $2)`

Sample input:
```
SecureLogger.log(
    "🔄 Found favorite for '\(peerInfo.nickname)' by nickname, updating noise key",
    category: .session,
    level: .debug
)
```

Sample output:
`SecureLogger.debug("🔄 Found favorite for '\(peerInfo.nickname)' by nickname, updating noise key", category: .session)`

---

2.
Search regex: `SecureLogger\.log\((.*?)\)`
Replace regex: `SecureLogger.debug($1)` (as it’s the default level)

Sample input:
`SecureLogger.log("some text")`

Sample output:
`SecureLogger.debug("some text")`

---

3
Manual changes:
ChatViewModel line 5393 (commented code)
NostrRelayManager line 196 (commented code)
NostrRelayManager lines 346-350 (if/else logic)
NostrRelayManager line 371 (commented code)
2025-09-11 19:03:08 +01:00
islam 5ca9222fc2 Make logging categories static properties of OSLog
So we can use `.<category name>` to simplify the code.

Search/Replace Strategy:
Search text: `category: SecureLogger.`
Replace text: `category: .`
2025-09-11 19:02:32 +01:00
IslamandGitHub e72fe50ffa Perf: Add final to classes that are not inherited (#574) 2025-09-11 19:17:04 +02:00
5f44af19da tor by default, small (#564)
* feat(tor): Tor-by-default scaffold and integration

- Add TorManager with static/dlopen start, torrc generation, SOCKS probe
- Add TorURLSession; route Nostr/Web fetches via SOCKS proxy
- Add chat system messages for Tor status; show progress (macOS) and ready
- Disable ControlPort bootstrap monitor on iOS; keep it on macOS
- Make Tor waits non-blocking; avoid main-actor stalls on startup
- Queue & flush Nostr subscriptions on relay connect; skip duplicates
- Always rewrite torrc at launch to fix iOS container path mismatches
- Link libz; add project wiring for tor-nolzma.xcframework
- Minor fixes: SOCKS probe resumeOnce guard, entitlement for network.server (macOS)

* iOS: deterministic Tor recovery + 100% gating; BLE-first; session rebuild

- Restart/wake Tor on foreground via ControlPort (ACTIVE/SHUTDOWN),
  avoid restarts during bootstrap; add NWPathMonitor to trigger checks
- Use NWConnection control polling for GETINFO; remove blocking CFStream
  readers to avoid QoS inversions; compute readiness from SOCKS + 100%
- Rebuild TorURLSession on resume; reset Nostr connections to rebind
- Gate all internet after full bootstrap; keep BLE mesh startup fast
- Fix Swift 6 capture issues; hop UI updates to @MainActor
- Remove Tor progress spam; persist initial "starting tor..." system message

* UI: show Tor system messages only in geohash channels (not mesh)

- Gate "starting tor..." and readiness/timeout messages to geohash view
- Add helper addGeohashOnlySystemMessage() to avoid posting to mesh timeline
- Persist system messages in geohash backing store via addPublicSystemMessage()

* Relays: treat repeated -1011 handshake failures as permanent; skip reconnects

- Classify NSURLErrorBadServerResponse as permanent and stop retrying
- Filter permanently-failed relays from subscribe/connect attempts
- Avoid reconnect scheduling for permanently failed relays

* Embed Tor via tor_api; deterministic restart + Nostr gating; add Tor notifications

- Run Tor via tor_api in a dedicated thread with OwningControllerFD
- Cleanly stop Tor on background; restart on .active (single instance)
- Avoid fallback to tor_main/dlopen; add is-running check to prevent duplicates
- Fix argv lifetime in C glue to avoid strcmp crash on start
- Gate Nostr connect/subscribe/send until Tor is fully ready
- Rebuild URLSession + reset relays after Tor readiness (scene-based)
- Remove TorDidBecomeReady double-reset and appDidBecomeActive resubscribe
- Add TorWillRestart/TorDidBecomeReady notifications and chat system messages
- Debounce path-change restarts; ACTIVE poke first; coalesce subs; cancel stale reconnect timers
- Project: add CTorHost.c and TorNotifications.swift to targets; fix libz.tbd path

* Defer Nostr setup logs until Tor is ready; fix subscribe coalescing and reconnect generation

- Move "Connecting to Nostr relays" log after awaitReady()
- Log "Queuing subscription" when Tor not ready; only coalesce when handler exists
- Clear coalescer on unsubscribe
- Cancel stale reconnect timers using connectionGeneration
- Remove app-level TorDidBecomeReady reset to avoid duplicate reconnects
- Debounce path-change restarts

* Gate Nostr init/subscription logs until Tor is ready

- ChatViewModel: await Tor readiness before initializing Nostr and logging
- Only log GeoDM subscription when Tor is ready to avoid early noise

* Make Nostr connect single-sourced; defer DM subscription until connected

- Remove duplicate connect call from ChatViewModel; let scene-based flow connect
- Setup DM subscription once on first connection via  sink
- Reduce early subscription send/cancel noise after Tor restarts

* On launch, queue Nostr subscriptions without initiating connects; let centralized connect handle it

- In subscribe(), if no connections exist, just list relays and queue subs
- Avoids early send/cancel churn before connect() runs post-Tor-ready

* Always queue subscriptions and flush on connection; avoid immediate sends

- Prevents early send/cancel churn at launch and during reconnects
- If relays are already connected, flush immediately; otherwise pending until connected

* UI: scope Tor restart messages to geohash channels; skip initial foreground restart on cold launch to avoid confusing system message in #mesh

* geo: disable background sampling + notifications\n- Gate sampling to foreground only (beginGeohashSampling, watchers)\n- Suppress geohash activity notifications unless app is active\n- Stop sampling explicitly on background scene phase

* Update BitchatApp.swift

Co-authored-by: asmo <asmogo@protonmail.com>

* Update BitchatApp.swift

Co-authored-by: asmo <asmogo@protonmail.com>

* Update BitchatApp.swift

Co-authored-by: asmo <asmogo@protonmail.com>

* Update bitchat/BitchatApp.swift

Co-authored-by: asmo <asmogo@protonmail.com>

* Update bitchat/BitchatApp.swift

Co-authored-by: asmo <asmogo@protonmail.com>

* fix(iOS App): resolve merge artifacts in scenePhase handler\n- Remove duplicate didEnterBackground state\n- Fix switch/if braces and logic for foreground restart gating

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: asmo <asmogo@protonmail.com>
2025-09-11 19:08:43 +02:00
5273f13512 Fix critical routing and buffer issues\n\n- Preserve unsent items in MessageRouter outbox (no data loss)\n- Drain and bound Nostr send queue; flush on relay connect\n- Cap BLE pending write buffers per peripheral to avoid OOM\n- Enable Nostr fallback for short peer IDs via favorites mapping\n- Route favorite notifications via MessageRouter (mesh/Nostr) (#559)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-04 21:26:58 +02:00
e79bcf531b Feature/screenshot privacy warning (#541)
* Screenshot privacy: warn on location sheet; gate screenshot broadcasts to chat only\n\n- Track sheet presentation in ChatViewModel\n- Show privacy alert when screenshot taken on location channels\n- Ignore screenshots for App Info and Location sheet for broadcast\n- ContentView wires sheet onAppear/onDisappear and presents alert\n- Fix location sheet subtitle wrapping: render as single Text to avoid orphaned bullets

* Fix duplicate messages after channel switch\n\n- Dedup on per-geohash append (skip if ID exists)\n- Dedup and sort timeline by timestamp when switching into a geohash\n- Keep content/layout unchanged; add lightweight debug logs for empty rows

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-28 14:37:17 +02:00
jack 3e5043f875 Harden text formatting against Unicode range mismatches
- Use NSString.length for all NSRange regex scans
- Guard string slicing when lastEnd > match.lowerBound
- Advance lastEnd safely to avoid backtracking overlaps
- Apply to formatMessageContent, formatMessage, formatMessageAsText, parseMentions
2025-08-28 09:22:34 +01:00
jack 11cd5527ed Fix crash in formatMessageAsText: use NSString length for NSRanges and guard slicing before matches to avoid invalid ranges with multi-byte content 2025-08-28 09:18:11 +01:00
cf1bfdac6b Geohash Bookmarks + Rising‑Edge Notifications + Friendly Names (#538)
* Add geohash bookmarks: persistence, sampling integration, and UI\n\n- Add GeohashBookmarksStore with UserDefaults persistence and toggle API\n- Sample union of regional + bookmarked geohashes for activity notifications\n- LocationChannelsSheet: bookmark icons on nearby rows and a Bookmarked section\n- Header toolbar: toggle bookmark for current geohash\n- Tests: GeohashBookmarksStoreTests for normalization and persistence\n\nRationale: Bookmarked geohashes are always sampled for new activity notifications and quickly selectable from the sheet.

* Notifications: remove background 'new chats!' nudge; geohash activity only on zero→alive; mesh 'nearby' only on zero→alive\n\n- Geohash sampling: notify only when previous participant count in last 5m was zero, with 30s freshness gate\n- Suppress old sampled events after (re)subscribe\n- Remove channel inactivity nudge\n- Mesh: reset rising-edge gate immediately when peer list goes empty (strict zero→alive)

* Geohash notifications: ensure triggering message appears\n\n- On zero→alive sampling, pre-populate geoTimelines[gh] with the triggering message\n- Dedup on per-geohash timeline and UI messages by message ID to avoid duplicates after subscribe\n- Keeps participant update, block/self checks, and cooldown intact

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-28 09:10:15 +01:00
b63a595b04 Fix geohash UX: rising-edge notify, correct teleport, sort timeline (#537)
- Remove "new chats!" background nudge; notify only when a regional geohash goes from 0 participants to activity, with content preview and cooldown.\n- Do not mark teleported when selected geohash is in regional channel list; clear persisted teleport when in-region; avoid self teleported state from historical tags; deep links don’t mark teleported for regional geohashes or during cold start.\n- Sort per-geohash timelines chronologically on re-entry and trim whitespace-only content to prevent blank lines.\n- Trim inbound public content and ignore whitespace-only sends.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-28 00:08:01 +01:00
d7b7f1f673 UI: diversify peer colors; smarter geo notifications; 21m live location (#531)
- Use minimal-distance hue palette for mesh and geohash lists; align chat sender colors with list palette.
- Add foreground geo notifications for different channels; deep-link to bitchat://geohash/<gh>; per-geohash 60s cooldown; respect self/blocks.
- Global geohash sampling runs outside the sheet; delegate handles deeplinks and suppresses when already in-channel.
- Switch location channel sheet to continuous CoreLocation with 21m distance filter; add config knob.
- Only link geohash hashtags when standalone (no @name#abcd or word#abcd).
- Include mesh-reachable peers in mesh counts and in "bitchatters nearby" notification.
- Add TransportConfig knobs for palette and geo notifications.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-26 18:43:22 +02:00
96c6fc0c0d Improve mesh relaying, presence, and DM robustness; signed public msgs; reachability UI; quicker announces; store-and-forward (#527)
* Sign public broadcasts; verify relayed messages via persisted signing keys; keep scheduled relays in sparse graphs and speed their jitter; persist announce signing key for offline auth; add short backoff after disconnect errors to reduce reconnect thrash

* Add connected vs reachable model: retain peers after link drop, expire after reachability window; expose all peers in snapshots; compute isReachable in UI; add meshReachable state and sorting; avoid removing peers on link events; notify UI on stale removals

* ContentView: handle new .meshReachable connection state in header icon switch (exhaustive switch fix)

* Logs: tag relayed announces as 'Reachable via mesh' and annotate public message logs with (direct|mesh) path for easier field analysis

* Fix syntax error: remove stray else/log inserted into writeOrEnqueue; keep logs clean

* UI: use 'point.3.connected.trianglepath.dotted' for mesh-reachable; change people count to include connected+reachable (exclude Nostr-only)

* UI: switch to 'point.3.filled.connected.trianglepath.dotted' for mesh-reachable icons in list and header

* Reachability: reduce retention to 21s for all peers (verified and unverified) to minimize stale presence

* mesh DMs/acks: route to reachable peers; queue READ/DELIVERED until handshake; add Transport.isPeerReachable; UI: hide offline non-mutuals; DM header: better name fallback + show transport + encryption icons; fix NostrTransport conformance

* Verification sheet: compute encryption status and fingerprint using short mesh ID mapping (fix 'not encrypted/handshake' for DMs with stable key)

* Announce cadence: faster discovery (4s), sparse 15±4s, dense 30±8s; initial 0.6s; post-subscribe 50ms; min-force 150ms; maintenance 5s; proactive announces on handshake + recent-traffic nudge

* Relay: increase broadcast TTL cap in sparse graphs to 6; tighten jitter for handshake (10–35ms) and directed (20–60ms) relays

* Range/robustness: store-and-forward for directed packets (15s) with flush on new links + periodic; announces: no subset + afterglow re-announce on first-seen; adaptive scanning: force ON when <=2 neighbors or recent traffic

* Fix warnings: remove unused msgID and unused mutable var in directed spool flush

* Announces: TTL 7 (sparse only) via RelayController; no fanout subset for announces; neighbor-change rebroadcast of last 2–3 announces. Fragments: faster pacing (5ms global, 4ms directed).

* Peer list: real-time icon updates by publishing snapshots on connectivity checks; add unread message indicator (envelope) next to peers with unread DMs

* UI: unread envelope uses orange; hasUnreadMessages checks Nostr conv key for peers with known Nostr pubkeys (geohash DM consistency)

* Logs/robustness: debounce disconnect notifications (1.5s), debounce 'reconnected' logs (2s), add weak-link cooldown after timeouts on very weak RSSI (<= -90)

* Peer icons: faster, accurate reachability\n\n- Run connectivity checks every maintenance tick (5s)\n- Publish peer snapshots on central unsubscribe for instant UI refresh\n- Lower inactivity timeout to 8s and disconnect debounce to 0.9s\n- Gate reachability on mesh-attached (>=1 direct link); no links => no reachable peers\n- Keep 21s retention for verified/unverified, but only when attached to mesh\n\nImproves list responsiveness when walking out of range and prevents stale 'reachable' states when isolated.

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-26 17:28:08 +02:00
a7d5b2d7d9 Centralize remaining magic numbers; finalize logging hygiene across UI/BLE/Nostr (#522)
* docs(plans): add refactor plan; chore(config): introduce TransportConfig and use in BLEService/ChatViewModel/PrivateChatManager; feat: add PeerDisplayNameResolver and apply in BLEService; chore: make TransportPeerSnapshot Equatable/Hashable; perf: simplify read-receipt persistence (no synchronize)

* project: add TransportConfig.swift and PeerDisplayNameResolver.swift to iOS/macOS targets (no xcodegen)

* chore: remove unnecessary UserDefaults.synchronize() calls (extension/app/VM)

* project: fix TransportConfig reference path; remove recovered reference; hook correct fileRef in iOS/macOS sources

* chore(BLE): move connect/duty/announce constants to TransportConfig and reference them

* chore(NostrTransport): factor recipient npub resolution into helper; reduce duplication

* chore(BLE): trim raw hex dump on central decode failure to length+prefix

* project: remove duplicate TransportConfig.swift entries from Sources build phases

* chore: centralize more constants in TransportConfig (BLE thresholds, Nostr read-ack, UI caps) and adopt in BLEService/ChatViewModel/NostrTransport

* chore: centralize location + geohash constants (filters, lookback, relay count) and adopt in LocationChannelManager/ChatViewModel

* chore: centralize compression, dedup, verification QR, relay backoff, georelay fetch constants; adopt across modules

* chore: centralize more BLE/Nostr delays; tighten NostrRelayManager logs to concise summaries; adopt config for location/geohash/relays

* refactor(config): centralize remaining magic numbers and finalize log hygiene

Add comprehensive TransportConfig constants for UI, Nostr, and BLE; adopt across ChatViewModel, ContentView, BLEService, ShareViewController, and BitchatApp to remove scattered literals. Standardize Nostr lookbacks/limits, UI delays/animations, and BLE announce/duty-cycle/candidate caps. Preserve behavior while making tuning explicit and safe.

Highlights:\n- UI: animations, scroll throttle, long-message thresholds, batch stagger, color hue tuning, rate-limit buckets, read-receipt debounce, startup delays, share accept/dismiss windows, migration cutoff.\n- Nostr: short display length (8), conv-key prefix length (16), DM lookback (24h), geohash sample lookback/limits; consistent use throughout ChatViewModel.\n- BLE: dynamic RSSI defaults, announce intervals/base+jitter, duty cycles (dense/sparse), fragment/ingress lifetimes, expected write timings/spacing, recent packet window (30s/100), peer inactivity timeout; unified candidate caps (100).\n- Share: use constant dismiss delay; App: use constant share accept window.\n\nRisk/impact: behavior-equivalent with centralized knobs; easier to tune without code edits.

* project: add TransportConfig.swift to Share Extension target to fix build

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-25 21:18:51 +02:00
60b0deee7b Cleanup: remove dead code, normalize fingerprints, modernize share extension, trim test noise, and drop ‘preparing to share…’ message (#520)
* Remove dead code and artifacts: drop PeerManager, unused views/types; delete LegacyTestProtocolTypes; update .gitignore; purge TestResult.xcresult and build.log

* Tests: gate verbose prints under DEBUG; ChatViewModel: remove legacy fingerprint helper and rely on UnifiedPeerService

* Share Extension: migrate to UIKit + UTTypes; drop Social/SLComposeServiceViewController

* Remove 'preparing to share …' system message; send shared content immediately

* Inline comment cleanup: drop legacy 'removed' breadcrumbs across protocols, services, view model, and views

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-25 18:01:19 +02:00
7c4c3f1391 macOS geohash parity: shared LocationChannelsSheet with permission CTA, enable CoreLocation on macOS, unify geohash participants/DMs, update ContentView (unread + QR on macOS), commands: hide/block /fav & /unfav in geohash, remove /help, make /who show geohash participants, fix ViewBuilder mutation+sheet toolbar, entitlements for mac location (#519)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-25 16:29:52 +02:00
jack acc11101ad Fix geohash linking: avoid links in @mentions
Only link #geohash tokens when not directly attached to an @mention (e.g., @name#gh stays plain). Keeps standalone #geohash tappable.
2025-08-25 09:31:41 +02:00
5fd9140ffa QR verification: live challenge/response over Noise; persistence, offline badges, and UX/perf polish (#510)
* QR verification scaffold: add Noise verify payload types, VerificationService with QR schema/signing, placeholder MyQR/Scan views, and UI entry points in header

* QR: fix VerificationQR mutability (sigHex var) and remove duplicate Data hex helpers to resolve redeclaration; wire signed payload assembly

* QR: render actual QR images with CoreImage; add copy button; keep scanner placeholder for now

* QR: fix SwiftUI modifiers — apply .interpolation(.none) and .resizable() to platform Image inside ImageWrapper; remove from wrapper usage

* QR: add iOS camera scanner using AVFoundation; integrate into Scan view; add NSCameraUsageDescription to Info.plist

* QR: make NoisePayloadType exhaustive in ChatViewModel switches by ignoring verifyChallenge/verifyResponse for now (placeholder)

* QR verification: speed + persistence + UX

- Inject live Noise into VerificationService; prewarm QR on app start
- Keep camera active; remove intermediate responder toast
- One-shot/dupe guards and deferred send on handshake
- Persist verified status immediately; standardize fingerprint (SHA-256)
- Show verified badge for offline favorites; mutual verification toast
- VERIFY sheet styling to match peer sheet; UI polish
- Logs to diagnose verified load + favorites mapping

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-24 23:19:58 +02:00
e6903456ab NIP‑17/44: XChaCha20 v2 gift wraps, DM kind=14, receipts + UI fixes (#506)
* NIP-17/44: adopt NIP-44 v2 (XChaCha20-Poly1305, v2: base64url), switch rumor kind to 14, randomize BIP-340 aux; add XChaCha20Poly1305Compat and wire-up

* Nostr DMs: ensure delivered/read acks are sent even without Noise key mapping by falling back to direct Nostr (geohash-style) acks to sender pubkey

* NIP-44 v2 decrypt: try both Y parities for x-only sender pubkeys (even then odd) to fix unwrap authentication failures

* Tests: add NIP-44 v2 ACK round-trip tests (delivered/read) using bitchat1 embedding and gift-wrap decrypt path

* UI: fix DM autoscroll IDs by using dm:<peer>|<id> for private chat item IDs and preserve anchors, ensuring scrollTo targets exist when opening/switching/new messages

* UI: fix string interpolation in DM/geohash context keys (remove escaped interpolation) to resolve 'unused ch' warnings and ensure proper IDs

* UI: MeshPeerList shows transport state icon: radio for mesh-connected, purple globe for mutual favorite/Nostr; fallback dim person for others

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-24 17:56:43 +02:00
85f99984c4 Unify DM notifications and fix geohash self-echo hydration (#505)
* feat(notifications): unify DM notifications across transports; notify for unread+recent even in foreground; delegate suppresses if chat open

* fix(geohash): include own past geohash messages on channel load; skip only very recent self-echo (<15s) to avoid duplicates

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-24 14:40:06 +02:00
75c8933091 Feat/georelay (#504)
* feat(georelay): route geohash kind 20000 via nearest relays; add GeoRelayDirectory; target geohash subscriptions; avoid duplicate connections

* feat(georelay): fetch daily from remote CSV with fallback to bundled; cache to app support; prefetch on app load

* fix(georelay): make CSV parser nonisolated and call as Self.parseCSV to satisfy Swift 6 actor isolation

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-24 12:49:52 +02:00
d2bfbbcfd4 Fix/chat perf (#502)
* perf(chat): batch public inserts, sort batch by ts; add per-sender + per-content token buckets; content-based near-dup suppression; reuse compiled regexes; single token scans per row; disable list animations during batches

* perf(chat): conditional animations via isBatchingPublic; late-arrival binary insert; flush public buffer on channel switch; prewarm formatting on flush

* perf(chat): batching, spam rate-limits, near-dup LRU, adaptive flush, faster trims, regex/detector reuse, conditional animations, late-insert, current-mode prewarm, Swift 6-safe timer/closures

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-24 11:11:49 +02:00
0260798712 Fix/UI (#500)
* Remove link previews: link URLs inline\n\n- Delete LinkPreviewView and all references\n- Strip preview usage from ContentView\n- Make detected URLs tappable via .link attribute\n- Add MessageTextHelpers for tokens/length checks\n- Wire helpers into iOS/macOS targets

* Peer list UX: match message colors, use map pin icon, stable ordering\n\n- Color list names and icons with same peer color as messages\n- Default icon mappin; teleported uses face.dashed (iOS)\n- Geohash: move teleported peers to bottom\n- Maintain stable order: append new peers, remove missing\n- Mesh list: replace state icons with colored mappin/person

* Fix peer list build errors: remove ambiguous Set type, avoid guard in ViewBuilder, remove duplicate bindings, and simplify person lookup

* Peer list: remove onChange expressions to avoid type-check issues; rely on ObservedObject updates

* Peer lists: simplify view structure to satisfy SwiftUI type checker (replace Group with VStack, flatten body)

* Peer lists: move ordering mutations to onAppear/onChange, keep body pure; fix result builder errors

* Fix: remove trailing modifiers outside if/else to avoid ViewBuilder type issues

* Color parity: fix seed cache to include color scheme; normalize Nostr seeds; switch to mappin.circle icon

* Peer colors: ensure exact match with message colors\n\n- Mesh list uses same seed logic as messages (getNoiseKeyForShortID fallback)\n- Color cache keyed by theme; normalize nostr seeds\n- Icons: use mappin.and.ellipse (default) and face.dashed for teleported\n- Teleport tag: accept "teleported" in addition to "teleport" and presence events

* Fix geohash color mismatch: populate nostrKeyMapping before building messages in resubscribe; also honor t,teleport tag for teleported state

* Message actions: username tap opens sheet via clickable AttributedString; message tap inserts @mention; rename to 'direct message'

* Cashu UX: suppress 'Show more' when message contains a Cashu token (chips handle rendering)

* Cashu detection: broaden regex (allow '.') + shorter min length; avoid long-text fallback when Cashu present so chips render and no full blob

* Geohash levels: rename region->province, country->region; update labels, precision, UI, manager mapping, tests; add Codable backward-compat for renamed cases

* Fix braces in LocationChannel.swift: close enum before extension to satisfy file-scope declarations

* Geohash links: make #geohash tappable and open that channel (bitchat://geohash/<gh>); handle in ContentView.onOpenURL with validation and selection

* Underline tappable #geohash mentions for clarity

* Geochat: sanitize timeline on channel switch to remove any blank-content entries (prevents blank rows)

* Xcode project: sync sources after UI changes (remove LinkPreview, add helpers, update build refs)

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-24 02:05:02 +02:00
dadc896ed8 Fix/geohash blocks (#483)
* fix(geo-block): enable block/unblock for geohash users and enforce blocks\n\n- Add persisted set of blocked Nostr pubkeys in SecureIdentityStateManager\n- Check Nostr blocks for incoming messages (public + geo DMs)\n- Prevent sending geo DMs to blocked users\n- Extend /block and /unblock to resolve geohash display names to pubkeys and act accordingly\n- Improve /block list to show geohash blocks (visible names or #suffix)

* fix(geo-block): enforce blocks on geohash public and DM receive; add block/unblock actions to geohash people list

* fix: mark handlePublicMessage as @MainActor to call isMessageBlocked safely

* ui(block): show blocked indicator (nosign icon) next to blocked peers in mesh and geohash lists

* fix(geo-block): early-drop blocked pubkeys on geohash receive; filter blocked users from participants list

* fix(geo-block): purge existing geohash messages/DMs on block; block directly from chat using Nostr sender ID when available

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-22 18:44:11 +02:00
b2504a7ff5 Feat/b links (#481)
* feat(cashu): auto-link cashu tokens in chat

- Detect cashuA/cashuB tokens via regex and render as tappable links
- Style like URLs (underline; blue for others, orange for self)
- Add openURL handler for cashu: scheme to delegate to wallet on iOS
- Respect existing heavy-content gating and long-message collapsing

* feat(ln): auto-link Lightning invoices and LNURL + lightning: scheme\n\n- Detect BOLT11 (lnbc/lntb/lnbcrt...), LNURL bech32, and lightning: URIs\n- Render as tappable links with lightning: scheme; consistent styling\n- Handle lightning: in openURL alongside cashu:

* feat(links): replace raw Cashu/Lightning tokens with compact chips (🥜 pay via cashu /  pay via lightning) while keeping them tappable

* style(links): add subtle background highlight to cashu/lightning chips

* ui(links): render Lightning/Cashu as rounded chips under message with padding; remove inline chip text

* ui(links): increase chip padding and corner radius; add extra top padding for chip row

* scroll: auto-scroll to bottom when sending a new message (public + private), regardless of current scroll position

* scroll(geo): when switching geohashes, scroll to top of chat (first visible message)

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-22 17:20:26 +02:00
7f2cbd6621 chat: fix blanking and gaps when switching geohashes; channel-aware row IDs + deferred autoscroll\n\n- Add channel-aware UI IDs (mesh|id, geo:<gh>|id, dm:<peer>|id) to prevent SwiftUI reuse gaps\n- Defer scrollTo to next runloop for stability; auto-scroll on appear/switch\n- Collapse very long messages with Show more/less; skip heavy parsing for huge content\n- Simplify LazyLinkPreviewView (remove GeometryReader in list) (#480)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-22 16:38:40 +02:00
jack 4bfe9ac80d Geohash count: use the same pruned/sorted list for toolbar and peer list (visibleGeohashPeople) to ensure consistency 2025-08-22 12:51:40 +02:00
jack ff5fd3f3fe Logs: add GeoTeleport diagnostics (incoming tags, self/peer marking, counts) to debug peer list dashed icon behavior 2025-08-22 12:29:23 +02:00
jack f8a955214b Geo teleported: robust tag detection (accept 't', 'teleport', and boolean-like values); mark self on send when tagging; should fix peer list icons 2025-08-22 12:24:17 +02:00
jack 5650e1f2c2 Peer lists: apply same per-peer colors as chat (self orange); suffix uses lighter variant; geohash uses Nostr pubkey, mesh uses Noise key 2025-08-22 11:49:44 +02:00
jack c4b422ad3f Fix: re-import CryptoKit for SHA256 usage in ChatViewModel 2025-08-22 11:47:21 +02:00
jack e82fda1093 Fix compile warnings: remove unused primaryColor in formatMessageAsText; remove unused CryptoKit import; mark peerColor/formatMessageAsText @MainActor to call main-actor helpers 2025-08-22 11:45:49 +02:00
jack 806d451135 Feature: assign stable per-peer colors (non-self) across mesh/geohash/DM; keep self orange and mentions-to-me orange; cache colors 2025-08-22 11:43:44 +02:00
13f8b0c636 Fix/geohash work (#475)
* Scroll UX: auto-scroll only when last item visible; preserve user position when scrolled up for mesh/geohash/DM; reduce blanking after very long messages

* iOS: re-enable keyboard autocomplete and default capitalization for message input

* Styling: stop blue/underline styling for #hashtags; render as normal text color (self=orange, others=green)

* Geo UI: ensure self shows teleported (face.dashed) if either per-session tag or manager flag is true

* Geo teleported: publish UI updates by assigning @Published Set instead of in-place insert; update on tag receipt and channel switch

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-22 11:38:44 +02:00
bc27e16899 Fix/visuals (#469)
* Geohash peers: show face.dashed for self when channel selected via teleport; face.smiling otherwise

* Geo presence: broadcast 'teleport' tag on geochat join; track teleported participants and show face.dashed for them in peer list

* Teleport tag: attach to actual geohash chat events (sendMessage, emotes, screenshots) instead of separate presence; remove presence emit

* Fix: show face.dashed for any teleported peer (not just self) in geohash list

* Geo list: show self immediately on channel switch and mark teleported state; clear teleported flags on leaving geochat

* Teleport persistence: recompute teleported based on current location vs selected geohash; add face.dashed icon to Teleport button label

* Styling: use lighter green/orange for #abcd suffix after nicknames in all chats (senders and @mentions)

* Peer lists: render #abcd suffix as lighter green/orange (self orange) in geohash and mesh lists

* Toolbar: move unread icon next to #channel badge and allow dynamic width; Peer lists: increase top spacing before first item

* Toolbar: prevent geohash channel badge from truncating; give it layout priority and fixed width

* Toolbar: make unread envelope independent from channel button (sits left of badge); fix accidental taps opening channel selector

* Toolbar: make unread envelope open most recent unread/private chat directly

* Geohash peer list: render self row fully orange (icon, base, suffix, '(you)')

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-21 11:15:18 +02:00
jack d3d9a22757 Mentions: color @mentions orange only when directed at me; otherwise use normal color (respect geohash suffix) 2025-08-21 03:26:46 +02:00
jack 33fbca67d6 UI: render self-authored messages in orange (sender and content); keep links/hashtags orange for self 2025-08-21 03:01:14 +02:00
jack c63350a4d3 UI: bold entire message text for self in mesh, DM, and geohash; adjust caching to include self flag 2025-08-21 02:51:14 +02:00
jack 916f535503 Fix: send screenshot notice to active public channel (geohash when selected), not always mesh 2025-08-21 01:52:54 +02:00
b09710a7aa Feature/location channels (#459)
* Require signed announces; add Ed25519 key/signature/timestamp TLVs and verify

- Protocol: AnnouncementPacket now requires ed25519PublicKey (0x03), announceSignature (0x04), and announceTimestamp (0x05). Encoder emits, decoder requires; unknown TLVs still tolerated.
- NoiseEncryptionService: add canonical announce sign/verify helpers using context 'bitchat-announce-v1'.
- BLEService: sign Announce, include TLVs; on receive verify (±5 min skew) and ignore unverified; store ed25519 key and isVerifiedNickname in PeerInfo.
- Preserve 8-byte on-wire IDs to keep BLE headers small; no per-message bloat.

* Fix Data.append usage for timestamp bytes in Packets and NoiseEncryptionService (use append(contentsOf:) on UnsafeRawBufferPointer)

* BLEService: fix non-optional announce fields and unwrap signature result; enforce required verification path

* Enforce verified-only public messages; append peerID suffix on nickname collisions in handleMessage

* Suffix duplicate nicknames with peerID prefix in snapshots and getPeerNicknames; ensures lists and messages disambiguate 'jack' vs 'jack'

* Include self nickname in collision detection: suffix remote 'jack' when it matches our nickname in lists and public chat

* UI labels: remove space before '#abcd' suffix in names (public chat, peer lists, snapshots)

* Style '#abcd' suffix light gray:
- Public chat: color suffix in sender via AttributedString segments
- People list: split name and color suffix segment; add helper splitNameSuffix()

* Debounce 'bitchatters nearby' notification: add 60s grace before reset when mesh goes empty; cancel reset when peers return

* Lighten '#abcd' suffix: use Color.secondary.opacity(0.6) in public chat sender and People list

* Toolbar peers indicator: use blue when Bluetooth peers present (was default text color)

* Toolbar peers indicator: use grey when zero peers (was red)

* Toolbar peers indicator: use system grey (Color.secondary) when zero peers, not green

* Fix crash: mutate peripherals dict on BLE queue (not main). Move scan restart to BLE queue as well.

* Reduce connect timeout churn: back off peripherals that time out (15s), increase timeout to 8s, skip non-connectable adverts, and demote timeout log to debug; clean backoff map periodically

* Mentions: support '@name#abcd' disambiguation; filter hashtag/URL styling inside mentions; deliver notifications only to the specified suffixed target when collisions exist

* Fix string interpolation in mention log (escape sequence)

* Mentions: parse '@name#abcd' in sender/receiver; render mention suffix grey (not orange/blue/underlined); ensure receiver notifications trigger for suffixed tokens

* Mentions: include own suffixed token 'name#<myIDprefix>' in valid tokens so '@name#abcd' to self is recognized and notified

* Public actions: show own system message by processing own public messages (remove early-return). Private /hug: add local system message for sender's chat.

* Grammar: change local '/hug' message to past tense ('you hugged/slapped'). Notifications: only fire 'bitchatters nearby' on rising-edge; remove 5-min re-fire and increase empty reset grace to 10m.

* Public /hug echo: add local system message so sender sees action immediately (no reliance on echo)

* Fix visibility: expose addPublicSystemMessage on ChatViewModel and use it from CommandProcessor

* Implement iOS Location Channels (geohash public chats)

- Domain: add ChannelID, GeohashChannel(Level), lightweight geohash encoder
- Identity: derive per-geohash Nostr keys (HMAC-SHA256 device seed)
- Nostr: kind 20000 + #g tag, nickname tag (n), filter helper
- iOS: LocationChannelManager (permissions, one-shot location), Info.plist string
- UI: toolbar # badge (#mesh or #<level>), sheet (irc style, full-row taps, settings link)
- VM: subscribe/send for active channel, clear timeline on switch, nickname cache
- Emotes: route public via Nostr/mesh without echo, local system confirmation, emojis restored
- Haptics: detect hugs/slaps for nickname#abcd and trigger on receiver
- Rendering: remove hashtag/mention formatting (plain monospaced)

Note: iOS-only; macOS unaffected.

* Lifecycle and persistence: resubscribe on foreground/relay connect, cap dedup set, persist mesh timeline.

- ChatViewModel: resubscribe geohash on app foreground and relay reconnect
- Add processed Nostr events cap (2k) with eviction
- Persist mesh public messages in meshTimeline; hydrate on switching to #mesh
- Local geochat messages use nostr: senderPeerID for classification
- Tests: fix JSON contains assertion for #g tag

* Fix Swift 6 concurrency warnings: remove closure-based NC observer, wrap relay reconnect resubscribe in Task @MainActor, resubscribe in appDidBecomeActive selector.

* Location Channels polish: live geohash refresh while sheet open; keep selection stable; toolbar shows #<geohash>; sheet labels show human level + #geohash.

* Add per-geohash in-memory timelines and cap private chats to 1337.

- ChatViewModel: persist geohash messages in geoTimelines[geohash] with cap; hydrate timeline on channel switch
- PrivateChatManager: trim per-peer chats to 1337 on send/receive
- Toolbar badge: prevent wrapping with lineLimit(1)/truncation

* Toolbar badge layout + brand color; Teleport custom geohash; Live refresh uses startUpdatingLocation with significant distance.

- Toolbar: right-justify geohash, head truncation, use brand green
- Sheet: add #custom geohash textfield + join; keep minimal IRClike style
- Manager: startUpdatingLocation()/stopUpdatingLocation() with distanceFilter=250 while sheet is open

* Location sheet live updates: reduce distanceFilter to 21m for more frequent geohash refreshes while open.

* Toolbar badge spacing/width; Sheet: rename to #geohash and button 'teleport'.

- Move # label closer to peer count and widen to avoid truncation
- Change custom field placeholder to '#geohash' and action button to 'teleport' with monospaced font

* Sheet polish: style 'teleport' button with subtle background, disable until valid; prefix '#' label and placeholder 'geohash'; center + style 'remove location permission' and hide separators for these rows.  Toolbar: adjust spacing/width for # label.

* Sheet UX: restrict custom geohash input to valid base32 (lowercase, max 12), reduce spacing so placeholder sits closer to '#', add divider under country row, style 'remove permission' and hide row separators as before. Toolbar: minor spacing/width already adjusted.

* Sheet behavior: show channel list even without permission; add green 'get location and my geohash' button; ensure only one separator between country and teleport by removing manual Divider and showing default row separator; refine teleport input filtering and spacing.

* Channel activity nudges: notify after 9 minutes inactivity only in background; triple-tap clear also clears persistent timelines.

- ChatViewModel: track last activity per channel; send local notification when new activity resumes while app in background (with small cooldown)
- CommandProcessor: /clear clears current public channel persistence via viewModel helper

* Fix compile: add activeChannelDisplayName() and clearCurrentPublicTimeline() helpers in ChatViewModel.

* Fix concurrent dictionary access in BLEService.broadcastPacket: snapshot shared collections under collectionsQueue to avoid CocoaDictionary iterator crashes.

* People: channel-aware list and counts

- Sidebar: NETWORK → PEOPLE; removed PEOPLE subheader/icon
- Toolbar: blue #mesh badge; green #<geohash> badge; count color by channel
- Geohash participants: track per-geohash unique senders with 5m decay; publish list
- Update on geohash send/receive; 30s prune timer; channel switch hooks

No changes to mesh peer UX; mesh list retained as-is.

* BLE: snapshot collections for thread-safe access; stopServices uses snapshot; safe characteristic snapshot in broadcast path

LocationChannelsSheet: rename button label to 'remove location access'

* Channel sheet: show current peer counts

- Mesh row shows connected mesh peers count
- Geohash rows show 5m-active participant counts via ViewModel
- Live-updates while sheet is open

* Channel sheet: pluralize counts as 'person/people' in titles

* Geohash sampling: subscribe to all available channels while sheet open

- ViewModel: add multi-channel sampling subscriptions and per-geohash participant updates
- Sheet: start sampling on appear, sync on list changes, stop on disappear

* Channel sheet: render counts '(N person/people)' in smaller font next to label

* Channel sheet: use square brackets for counts and adjust parsing; fix mesh title to '#mesh'

* Geohash DMs: implement send/receive via NIP-17 with per-geohash identity

- NostrEmbeddedBitChat: add no-recipient encoder for geohash DMs
- NostrTransport: add sendPrivateMessageGeohash(using provided identity)
- ChatViewModel:
  • startGeohashDM + mapping helpers
  • subscribe to per-geohash gift wraps; store DMs in conversations keyed by 'nostr_<prefix>'
  • route sendPrivateMessage() for 'nostr_' to Nostr geohash send; local echo and status
  • map pubkeys from geohash public events for DM initiation
  • resubscribe DM feed on reconnect and channel switch; unsubscribe on leave
- ContentView: long-press 'private message' starts geohash DM when sender is nostr

* Geohash DMs UX: tap participants to DM; support /msg nickname in geohash; DM notifications

- People (geohash) list: bold 'you', sort to top, tap to open DM
- ChatViewModel.getPeerIDForNickname resolves geohash names to nostr_ conv keys
- Geohash DM receive: send local notification when not viewing

* Geohash DMs polish: header title, star hidden, self-DM blocked, message icon, delivered/read ACKs

- Header shows '#<geohash>/@name#abcd' for geohash DMs; hide favorite star
- Geohash people row: add small message icon; prevent self tap
- Prevent sending geohash DM to self
- Send delivery/read ACKs on receive; handle delivered/read to update status

* Geohash DMs: hide encryption status icon in DM header

* BLE: fix data race in broadcast path

- Snapshot peripherals via Array(values) and filter outside sync
- Snapshot subscribedCentrals and centralToPeerID under collectionsQueue
- Mutate subscribedCentrals under collectionsQueue barriers in didSubscribe/unsubscribe

* LocationChannelsSheet: open fully by default (large detent only)

* BLE: remove unused centralMapSnapshot variable

* Geohash DMs: only notify on incoming PM when app is backgrounded; avoid foreground noise

* GeoDM: reliable delivered/read receipts, background-only notifications, and UI tweak

- Implement delivered + read receipts for geohash DMs; send READ on open\n- Handle relay OK acks for gift-wrap sends; resubscribe processes PM/DELIVERED/READ\n- Prevent mesh Noise handshakes for virtual geohash peers (nostr_*)\n- Notify GeoDMs only in background; suppress alerts for already-read msgs\n- Logging: promote key GeoDM receive logs; demote/remove verbose noise\n- UI: remove trailing envelope button from geohash People list

* Fix: remove duplicate variable declarations in AnnouncementPacket.decode()

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-20 01:10:23 +02:00
1c33a92765 Feature/signed public identity (#456)
* Require signed announces; add Ed25519 key/signature/timestamp TLVs and verify

- Protocol: AnnouncementPacket now requires ed25519PublicKey (0x03), announceSignature (0x04), and announceTimestamp (0x05). Encoder emits, decoder requires; unknown TLVs still tolerated.
- NoiseEncryptionService: add canonical announce sign/verify helpers using context 'bitchat-announce-v1'.
- BLEService: sign Announce, include TLVs; on receive verify (±5 min skew) and ignore unverified; store ed25519 key and isVerifiedNickname in PeerInfo.
- Preserve 8-byte on-wire IDs to keep BLE headers small; no per-message bloat.

* Fix Data.append usage for timestamp bytes in Packets and NoiseEncryptionService (use append(contentsOf:) on UnsafeRawBufferPointer)

* BLEService: fix non-optional announce fields and unwrap signature result; enforce required verification path

* Enforce verified-only public messages; append peerID suffix on nickname collisions in handleMessage

* Suffix duplicate nicknames with peerID prefix in snapshots and getPeerNicknames; ensures lists and messages disambiguate 'jack' vs 'jack'

* Include self nickname in collision detection: suffix remote 'jack' when it matches our nickname in lists and public chat

* UI labels: remove space before '#abcd' suffix in names (public chat, peer lists, snapshots)

* Style '#abcd' suffix light gray:
- Public chat: color suffix in sender via AttributedString segments
- People list: split name and color suffix segment; add helper splitNameSuffix()

* Debounce 'bitchatters nearby' notification: add 60s grace before reset when mesh goes empty; cancel reset when peers return

* Lighten '#abcd' suffix: use Color.secondary.opacity(0.6) in public chat sender and People list

* Toolbar peers indicator: use blue when Bluetooth peers present (was default text color)

* Toolbar peers indicator: use grey when zero peers (was red)

* Toolbar peers indicator: use system grey (Color.secondary) when zero peers, not green

* Fix crash: mutate peripherals dict on BLE queue (not main). Move scan restart to BLE queue as well.

* Reduce connect timeout churn: back off peripherals that time out (15s), increase timeout to 8s, skip non-connectable adverts, and demote timeout log to debug; clean backoff map periodically

* Mentions: support '@name#abcd' disambiguation; filter hashtag/URL styling inside mentions; deliver notifications only to the specified suffixed target when collisions exist

* Fix string interpolation in mention log (escape sequence)

* Mentions: parse '@name#abcd' in sender/receiver; render mention suffix grey (not orange/blue/underlined); ensure receiver notifications trigger for suffixed tokens

* Mentions: include own suffixed token 'name#<myIDprefix>' in valid tokens so '@name#abcd' to self is recognized and notified

* Public actions: show own system message by processing own public messages (remove early-return). Private /hug: add local system message for sender's chat.

* Grammar: change local '/hug' message to past tense ('you hugged/slapped'). Notifications: only fire 'bitchatters nearby' on rising-edge; remove 5-min re-fire and increase empty reset grace to 10m.

* Public /hug echo: add local system message so sender sees action immediately (no reliance on echo)

* Fix visibility: expose addPublicSystemMessage on ChatViewModel and use it from CommandProcessor

* use noise pubkey wip

* ttl=0 for signatures

* verification works

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-19 23:37:15 +02:00
Mateusz MatoszkoandGitHub 7836daa6d3 Adds O(1) method for peer nickname retrieval (#450)
* Adds O(1) method for peerNickname retrieval

* Uses peerNickname method where possible
2025-08-17 21:29:15 +02:00
4f1ac30f12 Feat/mesh robustness efficiency (#451)
* chat: de-dup private chats across ephemeral/stable IDs; prefer most advanced delivery status\n\n- Fixes LazyVStack duplicate ID warnings and blank row in PM\n- Merges messages by id from ephemeral and Noise-key stores\n- Chooses read > delivered > partiallyDelivered > sent > sending > failed (newer wins on tie)\n- Ensures status icon updates immediately without waiting for another send\n- Adds exhaustive handling for DeliveryStatus in ranking

* logging: reduce noisy info logs to debug; keep errors/warnings\n\n- Downgrade routing/ACK/subscription/connect logs to debug\n- Retain security/fingerprint/keychain info logs\n- Keep errors and warnings intact\n\ndocs: add docs/privacy-assessment.md covering BLE privacy, routing TTL/jitter, Nostr E2E gift wraps, ACK throttling, and logging posture

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-17 21:25:25 +02:00
6fbf7eee25 Refactor/ble nostr boundaries (#449)
* Refactor: move Nostr embedding and TLVs out of BLE; add NostrEmbeddedBitChat, Packets, PeerIDUtils; centralize MessageDeduplicator; update ChatViewModel to use new helpers; remove AI_CONTEXT.md and CLAUDE.md

* Rename class to BLEService with compatibility alias; move mention parsing out of BLE; emit low-level BLE events to delegate; unify hex helpers; accept 64-hex in isPeerConnected; add PeerIDResolver

* Project: rename file to BLEService.swift and update Xcode project; keep typealias SimplifiedBluetoothService = BLEService for compatibility

* Remove SimplifiedBluetoothService alias; update app code to use BLEService explicitly

* Tests: rename MockSimplifiedBluetoothService to MockBLEService; update typealiases and Xcode project

* Docs: update comments to refer to BLEService (tests, protocol, noise service)

* Tests: rename SimplifiedBluetoothServiceTests to BLEServiceTests; update project references and class names

* Introduce Transport protocol; BLEService conforms; document delegate-only event pattern in BLEService; keep publishers internal for UnifiedPeerService

* Adopt Transport end-to-end: add TransportPeerSnapshot + publishers; BLEService maps to Transport snapshots; UnifiedPeerService consumes Transport; ChatViewModel holds Transport

* Fix Transport integration: replace getPeerFingerprint with getFingerprint(for:); update PrivateChatManager and CommandProcessor to use Transport; add BLEService.getFingerprint(for:); update PeerManager to use Transport

* Refactor transport and BLE/Nostr layers; unify UI events; fix MainActor isolation

- Rename SimplifiedBluetoothService to BLEService and slim responsibilities
- Introduce Transport protocol and peerEventsDelegate for UI updates
- Add NostrTransport and MessageRouter to route PM/read/favorite via BLE or Nostr
- Centralize TLVs, PeerID utils, and MessageDeduplicator outside BLE
- Update UnifiedPeerService and ChatViewModel to use Transport and delegate events
- Fix MainActor isolation: route delegate calls via Task on MainActor; update notifyUI helper
- Adjust related files and tests accordingly

* BLEService: remove internal publishers; switch to delegate-only events

- Drop legacy messages/peers/fullPeers publishers
- Provide lightweight peerSnapshotSubject only to satisfy Transport
- Rework publishFullPeerData to build snapshots from internal state and notify delegate + subject
- Remove all peersPublisher.send call sites
- Keep UnifiedPeerService on delegate updates exclusively

* Remove inlined Nostr send helpers from ChatViewModel; route via MessageRouter

- Replace direct Nostr sends (PM, ACKs, favorites) with MessageRouter
- Add router method for delivery ACKs and implement NostrTransport.sendDeliveryAck
- Simplify ChatViewModel favorite notification path to use router
- Keep Nostr receive handling intact; reduce duplication

* Fix ReadReceipt initializer usage in ChatViewModel (readerID + readerNickname)

* Fix unused variable warning: replace shadowed 'nostrPubkey' bind with boolean check in ChatViewModel

* Fix queued PM format: use TLV for pending messages after Noise handshake

- Pending messages (including first-time favorite notifications) now use the same TLV encoding as normal sends
- Ensures ChatViewModel can decode on first send, even if handshake completes after queuing

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-08-17 15:17:04 +02:00