Commit Graph
100 Commits
Author SHA1 Message Date
jackandislam 1352484491 Fix remaining compilation issues after rebase
- Fix PhotosUI import order (must be after platform imports)
- Fix Data.WritingOptions.atomic reference
- Add identity derivation caching to NostrIdentityBridge
- Fix all remaining PeerID type conversions in ChatViewModel
- Fix ContentView body structure to use main's VStack layout
- Fix PaymentChipView API usage (now uses PaymentType enum)

Build and tests now passing.
2025-10-15 17:37:19 +01:00
jackandislam ef396936a3 Fix post-rebase compilation errors
- Remove duplicate NostrIdentityBridge and Bech32 from NostrIdentity.swift (now in separate files)
- Add caching to NostrIdentityBridge.deriveIdentity() for performance
- Remove duplicate NotificationStreamAssembler from BLEService.swift
- Remove duplicate function declarations in BLEService.swift
- Remove duplicate DeliveryStatusView and PaymentChipView from ContentView.swift
- Fix PeerID type conversions throughout (use .id for String, PeerID(str:) for wrapping)
- Update ContentView body to use main's simple VStack structure
- Fix NostrIdentityBridge instance method calls
- Remove privateChatView (replaced with sheet-based UI in main)

Build and tests passing (137/139 tests pass).
2025-10-15 17:37:19 +01:00
jackandislam c3ce32ad07 Fix critical security issues in fragment reassembly and file cleanup
Fragment Reassembly Race Condition (CRITICAL):
- Wrap all incomingFragments/fragmentMetadata access in collectionsQueue.sync
- Prevents concurrent modification crashes from multi-threaded access
- Minimizes lock contention by doing heavy work (reassembly/decode) outside locks
- Add upper bound check: reject fragments with total > 10,000 (DoS prevention)
- Add cumulative size validation before storing fragments (memory DoS prevention)

File Cleanup Path Traversal (CRITICAL):
- Use NSString.lastPathComponent to extract filename safely
- Prevents directory traversal attacks via malicious filenames
- Add path prefix validation before file deletion
- Now checks both incoming and outgoing directories (fixes disk leak)

Additional Protections:
- Fragment assemblies now limited by both count (128) and cumulative bytes (1MB)
- Explicit checks for "." and ".." filenames in cleanup
- Defense-in-depth: multiple validation layers
2025-10-15 17:37:19 +01:00
jackandislam d7ca99ae5d Optimize voice note codec to 16 kHz / 20 kbps for smaller file sizes
- Reduce sample rate from 44.1 kHz to 16 kHz (telephony standard)
- Lower bitrate from 32 kbps to 20 kbps
- Results in ~37% file size reduction (~150 KB/min vs 240 KB/min)
- Increases max voice note length from 4.4 to 7 minutes over 1 MiB BLE limit
- Maintains excellent voice quality using native AAC-LC codec
2025-10-15 17:37:19 +01:00
jackandislam b25f0d9f63 Remove debug print statements from sendMessage 2025-10-15 17:37:19 +01:00
jackandislam abb998d809 macOS: Focus message input on launch instead of nickname field 2025-10-15 17:37:19 +01:00
jackandislam d71befd8b7 Complete all translations to 100% and fix auto-extraction
- Mark non-localizable strings with Text(verbatim:) to prevent extraction
- Update UI strings to lowercase per style guide (open, save, close, recording)
- Add complete translations for all 29 languages (194/194 strings at 100%)
- Remove empty/duplicate entries (@, bitchat/, Open, Recording %@)
- Add proper localization comments for all user-facing strings
2025-10-15 17:37:19 +01:00
jackandislam 78c4bed1ad Fix infinite render loop and apply all security fixes
CRITICAL BUG FIX - Infinite Render Loop:

Root Cause: Duplicate view identity in ContentView.swift:368
  ForEach(messageItems) { item in  // Already uses item.id via Identifiable
      messageRow(...)
          .id(item.id)  //  REDUNDANT modifier caused identity re-evaluation loop
  }

When @Published properties updated, SwiftUI re-evaluated .id() → appeared as
'new' identity → triggered re-render → infinite loop. Caused UI freezes,
keyboard failures, and 100% CPU usage.

Fix: Remove redundant .id() modifier - ForEach already has stable identity.

PERFORMANCE FIXES:

1. Waveform Cache Deadlock (Waveform.swift)
   - Removed nested queue.async(barrier) on cache hits
   - Was causing task saturation and potential deadlocks

2. Async Send Pattern (ContentView.swift)
   - Clear input immediately, defer actual send to next runloop
   - Prevents blocking current event handler

3. Proper Swift Concurrency (VoiceNoteView.swift)
   - Switch from .onAppear + DispatchQueue to .task
   - Cleaner async/await pattern for loading

4. Remove Redundant objectWillChange (ChatViewModel.swift)
   - @Published already triggers updates automatically
   - Explicit send() was causing double update cycles

SECURITY FIXES (C1-C5, H1-H2):

C1. Path Traversal Protection (BLEService.swift)
    - Unicode normalization, null byte removal
    - Replace ALL path separators, reject dotfiles
    - Validate paths don't escape directory

C2. Integer Overflow (BitchatFilePacket.swift)
    - Use UInt64 for TLV parsing, safe Int conversion

C3. MIME Validation (BLEService.swift)
    - Whitelist: JPEG, PNG, GIF, WebP, M4A, MP3, WAV, OGG, PDF
    - Magic byte validation for all types
    - Lenient on M4A (platform variations)

C4. Compression Bomb (BinaryProtocol.swift)
    - Ratio validation <= 50,000:1
    - Defense-in-depth with 1MB size cap

C5. TOCTOU Race (ChatViewModel.swift)
    - Direct removeItem without fileExists check

H1. File Size Validation (ChatViewModel, ImageUtils)
    - Check attributes BEFORE Data(contentsOf:)
    - Prevents memory exhaustion

H2. Metadata Stripping (ImageUtils.swift)
    - Remove ALL metadata keys from JPEG encoding
    - Only compression quality set
    - Protects GPS/EXIF/device info privacy

RESULT:
 No render loops
 Works with Xcode debugger
 Voice notes display properly
 All security vulnerabilities fixed
 164 tests passing

Production ready.
2025-10-15 17:37:19 +01:00
jackandislam c52a2a7772 Ensure /clear and panic triple-tap delete media files
Fix: /clear command and panicClearAllData() now properly delete media files

1. /clear (triple-tap on chat):
   - Deletes outgoing media (voice notes, images, files)
   - Conservative: only our sent media, preserves received media
   - Runs in background to avoid UI freeze

2. panicClearAllData() (triple-tap on bitchat/ header):
   - Deletes ALL media files (incoming + outgoing)
   - Removes entire files directory and recreates structure
   - Ensures complete data wipe for emergency scenarios

Both operations run async on .utility queue to prevent blocking UI.
2025-10-15 17:37:19 +01:00
jackandislam 3df0aa0cc4 Make voice note loading completely lazy with deferred initialization
Aggressive performance optimization to prevent UI freezes:

Problem: Even with async loading, creating 10+ VoiceNotePlaybackController
instances simultaneously (when scrolling past multiple voice notes) spawned
20+ concurrent background tasks, potentially starving main thread.

Solution - Ultra-lazy loading:
1. VoiceNotePlaybackController.init() now does ZERO work
   - No duration loading
   - No player creation
   - Instant initialization

2. Duration loaded on-demand via public loadDuration() method
   - Called from VoiceNoteView.onAppear after 150ms delay
   - Reduced priority: .utility instead of .userInitiated
   - Guard prevents duplicate loading

3. Waveform loading also deferred 150ms
   - Gives UI time to settle after message appears
   - Prevents task storms when multiple voice notes appear

This spreads the work over time instead of all at once.
2025-10-15 17:37:19 +01:00
jackandislam 46caded099 Cache geohash identity in ChatViewModel to prevent crypto during rendering
Additional optimization for location channels (voice notes are mesh-only,
but this helps with text message rendering in geohash channels):

- Add cachedGeohashIdentity to avoid deriveIdentity calls during rendering
- Check cache before falling back to crypto derivation
- Reduces main thread crypto work in location channels
2025-10-15 17:37:19 +01:00
jackandislam a04ac9dabd Cache Nostr identity derivation to prevent crypto during view rendering
Critical performance fix:

Problem: formatMessageHeader() called deriveIdentity(forGeohash:) during
every SwiftUI render for every media message. Each call performed:
- Keychain I/O (getOrCreateDeviceSeed)
- HMAC-SHA256 computation
- Up to 10 secp256k1 key validations (elliptic curve crypto)

With multiple media messages, this resulted in 100s of milliseconds of
blocking crypto on main thread per render cycle.

Solution: Add thread-safe cache for derived identities
- Check cache before expensive crypto operations
- NSLock protects concurrent access
- Identity is deterministic per geohash, so caching is safe

This eliminates crypto from the hot rendering path.
2025-10-15 17:37:19 +01:00
jackandislam 1825d115bc Eliminate disk I/O from SwiftUI view rendering path
Critical performance fix for UI freezes when receiving media:

Problem: mediaAttachment(for:) was called during every SwiftUI render,
performing synchronous disk I/O on main thread:
- FileManager.fileExists() called 2-6x per message (checking subdirs)
- applicationFilesDirectory() creating directories on every call
- With multiple media messages, this meant 20-100+ disk ops per render

Solution:
1. Remove fileExists checks - construct URLs directly
   - Files are validated during playback/display (fail gracefully if missing)
   - Sender determines subdirectory (outgoing vs incoming)

2. Cache applicationFilesDirectory() result
   - Static cache prevents repeated FileManager.url() calls
   - Directory created only once

3. Remove redundant playback.replaceURL() in VoiceNoteView.onAppear
   - Controller already initialized with correct URL

This eliminates ALL disk I/O from the view rendering hot path.
2025-10-15 17:37:19 +01:00
jackandislam 9754881af8 Fix memory leaks and post-playback freeze
Fixes:
1. Post-playback freeze: audioPlayerDidFinishPlaying now dispatches to main
   thread before updating @Published properties (Swift concurrency violation)

2. Unbounded waveform cache: Implement LRU eviction with 20-entry limit
   - Track last access time for each cached waveform
   - Evict oldest entry when cache is full
   - Prevents unlimited memory growth as voice notes accumulate

3. Audio buffer memory leaks: Wrap computeWaveform in autoreleasepool
   - AVAudioPCMBuffer allocations are autoreleased
   - Pool ensures buffers are freed promptly

4. Image processing memory: Add autoreleasepool around compression loops
   - Each jpegData() call creates temporary objects
   - Inner pool per iteration prevents memory spikes during quality search

Memory should now remain stable during extended use.
2025-10-15 17:37:19 +01:00
jackandislam 59cc857fde Fix UI freeze when receiving voice notes
Problem: AVAudioPlayer initialization in VoiceNotePlaybackController.init()
was running synchronously on main thread during view creation, blocking
UI for 50-200ms per voice note.

Solution:
- Remove eager preparePlayer() call from init
- Load duration asynchronously on background queue
- Player is only prepared when playback is actually requested via ensurePlayerReady()

This prevents UI freezes when voice notes appear in the chat.
2025-10-15 17:37:19 +01:00
jackandislam f0ca1b27c2 Fix critical issues from PR #681 review
Critical fixes:
- BinaryProtocol: Return nil for unknown versions (prevents buffer underflows)
- Add BinaryProtocol.Offsets struct to centralize magic numbers
- Replace magic offset calculations with named constants

Security/Privacy:
- FileAttachmentView: Use url.lastPathComponent instead of url.path
  (prevents exposing full system paths)

Documentation:
- Fix compression algorithm documentation (zlib, not LZ4)

All tests passing.
2025-10-15 17:37:19 +01:00
jackandislam 5beff8b4dc Fix binary protocol test fixtures 2025-10-15 17:37:19 +01:00
jackandislam ba49b885bb Reset BLE assembler on stalled fragment trains 2025-10-15 17:37:19 +01:00
jackandislam ac10e906c0 Drop attachment ceilings to 1 MiB and bump release version 2025-10-15 17:37:19 +01:00
jackandislam 119e882574 Guard peer map reads on BLE message path 2025-10-15 17:37:19 +01:00
jackandislam 0acbbdf2e3 Restore BLE broadcasts when notify buffer is saturated 2025-10-15 17:37:19 +01:00
jackandislam 40fe0c8ba5 Fix cleanupLocalFile lookup 2025-10-15 17:37:19 +01:00
jackandislam 7593053137 Resolve image/voice path handling 2025-10-15 17:37:19 +01:00
jackandislam 4dde063027 Hide absolute paths in media messages 2025-10-15 17:37:19 +01:00
jackandislam 8c4fead945 Stub file transfer methods in mock 2025-10-15 17:37:19 +01:00
jackandislam 2543277755 Stub file transfer methods in mock 2025-10-15 17:37:19 +01:00
jackandislam a8ce4cbf64 Use unique transfer identifiers 2025-10-15 17:37:19 +01:00
jackandislam 93aa714ed4 Preserve packet version when signing 2025-10-15 17:37:19 +01:00
jackandislam 9c7bf26e13 Fix CFMutableData handling 2025-10-15 17:37:19 +01:00
jackandislam c29c7d83fe Target image byte size across platforms 2025-10-15 17:37:19 +01:00
jackandislam 7f743d48fe Normalize mac JPEG color space 2025-10-15 17:37:19 +01:00
jackandislam 27c39332d5 Strip metadata in mac image encoding 2025-10-15 17:37:19 +01:00
jackandislam 53c08d6807 Revert unsupported JPEG option 2025-10-15 17:37:19 +01:00
jackandislam 7efe0a23b1 Align mac image JPEG encoding 2025-10-15 17:37:19 +01:00
jackandislam 8dedc81512 Allow user-selected write access 2025-10-15 17:37:19 +01:00
jackandislam 5cbe9d4aa7 Fix image attachment detection 2025-10-15 17:37:19 +01:00
jackandislam 515fae9e25 Use save panel for mac image export 2025-10-15 17:37:19 +01:00
jackandislam 0946d3921f Keep processed images for outgoing messages 2025-10-15 17:37:19 +01:00
jackandislam 1d40950118 Lowercase image preview buttons 2025-10-15 17:37:19 +01:00
jackandislam b404d48ba6 Reblur images via swipe 2025-10-15 17:37:19 +01:00
jackandislam 2f1ba27d51 Allow long-press reblur on images 2025-10-15 17:37:19 +01:00
jackandislam 23124b36b7 Use Photos picker on mac 2025-10-15 17:37:19 +01:00
jackandislam 3e8d05fa2e Restore mac photo picker access 2025-10-15 17:37:19 +01:00
jackandislam c36b582209 Display recording milliseconds 2025-10-15 17:37:19 +01:00
jackandislam 3c66ca7499 Harden attachment transfer bookkeeping 2025-10-15 17:37:19 +01:00
jackandislam a042576652 Describe microphone usage 2025-10-15 17:37:19 +01:00
jackandislam 3c120042a4 Permit mac media library access 2025-10-15 17:37:19 +01:00
jackandislam 4e3603d5e4 Allow mac microphone access 2025-10-15 17:37:19 +01:00
jackandislam f7ad970a83 Enable mac attachment importers 2025-10-15 17:37:19 +01:00
jackandislam 4cc87633e1 Fix compressed BLE file transfers 2025-10-15 17:37:19 +01:00
jackandislam 2d565ad918 Stop dropping partial BLE frames while assembling notifications 2025-10-15 17:37:19 +01:00
jackandislam b4cb4d36f1 Log incomplete BLE frames for debugging 2025-10-15 17:37:19 +01:00
jackandislam 7fcef2a9cb Add detailed logging for BLE fragment assembly 2025-10-15 17:37:18 +01:00
jackandislam e7dc0e0c5a Let BLE assembler accept large frames up to hard cap 2025-10-15 17:37:18 +01:00
jackandislam 3b2c7d2f73 Add guard to drop oversized BLE notification assemblies 2025-10-15 17:37:18 +01:00
jackandislam 209f926990 Revert "Raise BLE notification buffer cap for large file transfers"
This reverts commit b624523af843475db84e4a846db8dcbe824ae408.
2025-10-15 17:37:18 +01:00
jackandislam ef8b509ea5 Raise BLE notification buffer cap for large file transfers 2025-10-15 17:37:18 +01:00
jackandislam 06b5bdb6da Allow file transfers from connected but unverified peers 2025-10-15 17:37:18 +01:00
jackandislam a5a3efebcb Copy imported files before sending to preserve access 2025-10-15 17:37:18 +01:00
jackandislam cd39d8c8c0 Restore iOS file importer for attachments 2025-10-15 17:37:18 +01:00
jackandislam 1046bfec0f Reduce vertical padding between chat rows 2025-10-15 17:37:18 +01:00
jackandislam d8c83e25f4 Tighten spacing above media message bubbles 2025-10-15 17:37:18 +01:00
jackandislam a9fa614416 Gracefully disable mac attachment pickers in sandbox 2025-10-15 17:37:18 +01:00
jackandislam ecbbc23862 Add BLE file transfer support and media UX 2025-10-15 17:37:18 +01:00
jackandGitHub b6d42261d0 Guard peer collision checks with snapshot (#810) 2025-10-15 16:12:16 +02:00
3479c7d5df Fix people sheet dismiss gestures (#803)
* Allow closing people sheet from X and swipe

* Swipe right to return from DM to people list

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-14 21:11:44 +02:00
8a0727fcf7 Guard BLE link state lookups on BLE queue (#805)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-14 21:11:07 +02:00
6588861e34 Align gossip sync stale cleanup with Android client (#798)
* Align DM sheet toolbar with people list

* Gate stale gossip announcements

* Remove stale peer messages during gossip cleanup

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-14 13:54:36 +02:00
e3149fa098 Process incoming fragments on message queue (#804)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-14 12:20:39 +02:00
jack 987ba2e694 Fix people sheet close button 2025-10-12 20:18:29 +02:00
615273a63e Align DM sheet toolbar with people list (#795)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-12 19:53:08 +02:00
d994ccf012 Fix send button tap responsiveness and sidebar drag jitter (#783)
Restructured ContentView layout to prevent sidebar from covering input box
and removed gesture conflicts that caused jitter during slow drags.

Changes:
- Moved sidebar overlay to only cover messages area, not input box
- Input box now always accessible below sidebar (not covered by overlay)
- Removed blocking drag gesture from mainChatView
- Changed sidebar gesture from simultaneousGesture to gesture for priority
- Removed animation-disabling transactions that amplified touch noise
- Removed 2pt threshold checks that caused visible jumps

Result: Send button taps immediately, sidebar slides smoothly without jitter.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-09 23:45:05 +02:00
0f23ed0a99 Location notes: fix performance and UI issues (#774)
* Location notes: fix performance and UI issues

Performance fixes:
- Add Set-based duplicate detection (O(1) vs O(n) lookup)
- Eliminates lag when receiving 200+ notes during EOSE

Correctness fixes:
- Fix optimistic echo timestamp to match signed event timestamp
- Add echo IDs to noteIDs set for consistency

UI improvements:
- Remove duplicate "loading recent notes" text in header
- Simplify toolbar icon color logic for immediate green indication
- Icon now reliably turns green when notes exist in geohash

Tests: All 3 LocationNotes tests passing

* Location notes: add remaining robustness fixes

Additional improvements:
- Align counter/manager limits to 200 (prevents showing count higher than displayable)
- Set loading state before clearing notes to eliminate UI flicker on geohash change
- Add geohash validation for building-level precision (8 valid base32 chars)
- Add defensive 500-note memory cap with automatic trimming
- Clear stale notesGeohash state on sheet dismiss

Tests:
- Fix test geohashes to use valid base32 characters
- All 3 LocationNotes tests passing

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-07 12:36:54 +02:00
c583949031 Fix QR verification sending multiple notifications (#773)
The camera scanner was continuously detecting the same QR code (10-30+ times/second), causing multiple verification notifications to be sent. This happened because:

1. AVCaptureMetadataOutput fires repeatedly while QR is visible
2. Each detection triggered a new verification flow
3. After receiving response, pendingQRVerifications was cleared, allowing duplicate scans

Changes:
- Add deduplication using lastValid state to ignore re-scans of same QR code
- Only set lastValid after successful verification initiation
- Add onSuccess callback to close scanner after successful scan
- Automatically return to "My QR" view after verification starts
- Apply same logic to both iOS camera and macOS manual input paths

This ensures exactly one verification request per scan session.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-07 11:32:57 +02:00
7149182c56 Fix ghost peers and stale messages from gossip sync (#766)
Problem:
- Ghost peers from yesterday appeared on app restart
- Old messages resurfaced after restarting
- Peers running 24+ hours synced stale data to restarting peers

Root causes:
1. GossipSyncManager stored packets indefinitely (no time limit)
2. handleAnnounce/handleMessage had no timestamp validation
3. Relayed announces from other peers could be arbitrarily old

Solution (defense in depth):
- Added 15-minute age window to GossipSyncManager config
- Gossip storage rejects expired packets at ingestion
- Gossip sync responses filter out expired packets
- GCS payload building excludes expired packets
- Periodic cleanup removes expired announcements/messages
- handleAnnounce rejects stale announces before processing
- handleMessage rejects stale broadcast messages before processing

This prevents ghost peers from appearing on restart and ensures
only recent mesh state is synchronized between peers.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-07 00:56:25 +02:00
b5b05977fb Show Bluetooth permission alerts on launch and foreground (#765)
* Fix test suite peer ID collisions

Use unique peer IDs for each test suite to prevent global registry
collisions when Swift Testing runs suites in parallel.

- PrivateChatE2ETests: PRIV_* prefix
- PublicChatE2ETests: PUB_* prefix
- Update all peer ID references to use actual instance IDs

This fixes the race condition where simplePublicMessage() was receiving
duplicate deliveries due to registry contamination.

* Add Bluetooth permission & state alerts on launch and foreground

Wire up existing Bluetooth alert infrastructure to show notifications
when Bluetooth is off, unauthorized, or unsupported.

Changes:
- Add didUpdateBluetoothState() to BitchatDelegate protocol
- BLEService now notifies delegate when Bluetooth state changes
- ChatViewModel implements delegate method to show alerts
- Check Bluetooth state on app launch (after 100ms delay)
- Check Bluetooth state when app comes to foreground
- Add getCurrentBluetoothState() method to BLEService

The UI alert already existed but wasn't wired up. Now users will see
appropriate alerts for:
- Bluetooth turned off
- Bluetooth permission denied
- Bluetooth unsupported on device

Alert includes a button to open Settings on iOS.

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-06 22:47:11 +02:00
af6703cf24 Fix test suite peer ID collisions (#764)
Use unique peer IDs for each test suite to prevent global registry
collisions when Swift Testing runs suites in parallel.

- PrivateChatE2ETests: PRIV_* prefix
- PublicChatE2ETests: PUB_* prefix
- Update all peer ID references to use actual instance IDs

This fixes the race condition where simplePublicMessage() was receiving
duplicate deliveries due to registry contamination.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-06 22:46:45 +02:00
jack faae625e53 Add shared macOS scheme to match iOS scheme 2025-10-02 11:59:46 +02:00
jackandGitHub ea72212f66 Prune unused validation helpers (#701) 2025-10-01 13:29:09 +02:00
jack 2ffa709cca Bump version to 1.4.4 2025-09-30 13:14:26 +02:00
jackandGitHub b2214e30f5 Remove unused favorites and notification helpers (#693) 2025-09-30 12:44:46 +02:00
jackandGitHub 85063e9359 Optimize private chat deduplication (#694) 2025-09-30 12:37:48 +02:00
b873b19104 Add new languages (#700)
* Fix establishing encryption typo

* Add Bengali localizations

* Add Hindi localizations

* Add Turkish localizations

* Add Portuguese (Portugal) localizations

* Add widespread localization coverage

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-30 12:31:29 +02:00
f5caa1751a Add base localization infrastructure and externalize strings (#670)
* Add base localization infrastructure and externalize strings

* Add Spanish localization scaffolding with translations

* Add machine translations for expanded locales

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-24 15:12:42 +02:00
jack de906cb97c Refresh typography and channel sheet styling 2025-09-24 12:32:11 +02:00
1fd2da18f5 Improve BLE relay reliability (#665)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-23 21:09:22 +02:00
c49a1b264e Support Dynamic Type across chat surfaces (#664)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-23 19:56:16 +02:00
3a94b57341 Fix BLE stream crashes and gossip sync races (#663)
* Handle long BLE packets safely

* Keep BLE stream aligned after partial drops

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-23 14:06:13 +02:00
f8f780d2d6 Refine location notes UI and align sheet layouts (#660)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-21 14:56:20 +02:00
jackandGitHub c837afb818 Remove unused handshake coordinator and identity placeholders (#656) 2025-09-21 13:20:21 +02:00
jack d1e5ce21a7 Enable default relays when location permission granted 2025-09-21 12:43:27 +02:00
jack 50ae8da5f9 Bump marketing version to 1.4.2 2025-09-16 08:16:21 -07:00
54bb812469 Gate relays on mutual favorites and add Tor toggle (#631)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-16 08:12:51 -07:00
jack 56e7324069 Improve Tor dormant resume and restart flow 2025-09-15 23:08:29 +02:00
1733dda6cd Ignore self when presenting message actions (#625)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-15 21:58:39 +02:00
00ff5fd31c Refine panic mode to regenerate identities immediately (#624)
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-15 21:39:39 +02:00
9cbdb0a764 Gate Tor/Nostr start behind permissions or mutual favorites; add clean shutdown + robust gating (#619)
- Add NetworkActivationService to permit Tor/Nostr only when location is authorized OR at least one mutual favorite exists.
- Gate TorManager.startIfNeeded/ensureRunningOnForeground behind a global allowAutoStart flag.
- Always stop Tor on background for deterministic restarts; rebuild sessions on foreground when allowed.
- NostrRelayManager respects the gate in connect/ensureConnections/subscribe/send/connectToRelay and skips reconnection when disallowed.
- Symmetric shutdown when conditions become disallowed: disconnect relays and stop Tor.
- Fix double-start by avoiding restart if Tor is already ready; prevent background thrash.
- Improve UX: post "starting tor…" via TorWillStart, and "tor started…" on initial ready; keep existing restart messages.

Rationale: Avoid starting Tor/relays when the user has no location permission and no mutual favorites, and ensure a clean, predictable lifecycle (no stale sockets, no double starts).

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-15 16:46:49 +02:00
jack 42cdc4c123 Xcode: dedupe libz.tbd reference in project.pbxproj 2025-09-14 15:46:06 +02:00
de2b5ed142 Fix/various UI fixes (#605)
* UI: replace textual 'close' with X icon\n\n- AppInfoView (iOS): use xmark icon in nav bar to match Location Notes style.\n- LocationChannelsSheet: use xmark icon for close on iOS/macOS toolbars; add accessibility label.

* Location Notes: prefix usernames with @ and lighten #geohash\n\n- Show @ before usernames in notes list.\n- Split header into '@' and '#geohash' and color the geohash with secondary green for consistency.

* Header spacing: add breathing room between channel badge, notes button, and people count\n\n- Add trailing padding after #mesh/#geohash badge.\n- Add leading padding before notes button and people counter to improve readability.

* Header: nudge #mesh/#geohash badge right with leading padding

* Location Notes + Header polish\n\n- Header: add space in '@ #geohash' and use darker green for geohash.\n- Notes list: render '@name#abcd' with darker green for #abcd to match chat.\n- Header: move geochat bookmark icon after #geohash badge with consistent spacing.

* Location Notes: @name regular green, #abcd darker; nudge #hash\n\n- Render '@' and base name in regular green, suffix '#abcd' in darker green.\n- Add extra left padding before '#geohash' in notes header.\n- Increase leading padding for channel badge to push #mesh/#geohash further right.

* Fix notes icon color: subscribe/count at block-level geohash\n\n- Use block (precision 7) geohash for notes: when opening sheet, on channel changes, and when subscribing the counter.\n- Aligns with LocationNotesManager which publishes at street-level geohash, allowing the counter to detect notes and turn icon blue.

* Header spacing: move #mesh/#geohash closer to notes/bookmark\n\n- Reduce trailing padding on channel badge and leading padding on notes/bookmark to cluster them together.\n- Keeps larger gap before people count for readability.

* Notes: standardize on building-level (8 chars) for publish/read\n\n- Use .building geohash when opening notes, reacting to channel updates, and subscribing the counter.\n- Update comments to reflect building-level scope.

* Location Channels sheet: use black sheet background like other sheets\n\n- Add backgroundColor and apply to container and list.\n- Hide list default background with scrollContentBackground(.hidden).

* Notes icon: use green when notes exist (matches app green)

* Location Channels: boxed 'bookmarked' section; keep 'remove location access' outside box\n\n- Wrap bookmarked list in a rounded, subtle grey box within the list.\n- Ensure the 'remove location access' button is not inside the box and clears list row background.

* Notes counter UX: avoid grey flicker when closing sheet\n\n- Preserve last count during resubscribe to prevent brief 0 state.\n- Keep existing subscription when building geohash temporarily unavailable; only cancel if none or permission revoked.

* Notes counter: unsubscribe without clearing count on resubscribe\n\n- Avoid calling cancel() in subscribe; just unsubscribe old sub to prevent wiping count to 0.\n- Prevents green→grey flicker after closing sheet or location updates.

* Notes icon: use sheet count until counter finishes initial load; don’t zero on sheet close\n\n- Compute hasNotes using max(count, sheetCount) while initialLoadComplete is false.\n- Remove sheetNotesCount reset on sheet disappear to avoid transient grey.

* Location Notes header: remove extra gap before #geohash (drop leading padding)

* Notes sheet: color #abcd suffix as darker green via opacity (match chat)

* Notes sheet: show timestamp in brackets; drop #abcd from @name

* chore: commit remaining local changes

* Header: move notes + bookmark to left of #mesh/#geohash

* Header spacing: tighten gap between #mesh/#geohash and peer count

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-09-13 23:05:19 +02:00
jack 57cc9028cb Fix EXC_GUARD crash: avoid closing Tor-owned fd on tor exit\n\nCTorHost: stop calling close(owning_fd_tor) in tor_thread_main. Tor already\ncloses its end; closing a reused guarded fd can trigger EXC_GUARD on iOS 18.\nWe now treat it as Tor-owned and just invalidate our reference. 2025-09-13 21:09:23 +02:00