Commit Graph
33 Commits
Author SHA1 Message Date
dd6b624cae Quality pass on the 1.7.0 batch: fix confirmed bugs, bump to 1.7.1 (#1418)
* Quality pass on the 1.7.0 batch: fix confirmed bugs, bump to 1.7.1

Post-merge review of PRs #1400–#1417 (push-to-talk, mesh bridging, DM
store-and-forward, empty-mesh liveliness, geo-notes). Fixes the confirmed,
well-scoped findings; deeper architectural/security items are tracked
separately.

- PTT hot-mic leak: releasing the mic during VoiceCaptureSession.start()'s
  150ms retry pause left the mic live and streaming for up to 120s, because
  cancel() no-op'd once `completed` was set. Bail after the sleep if the hold
  was released, and make cancel() always tear down a late-started capture.
- Bridge courier depositDrop reported success and burned the dedup slot before
  the drop was actually published (evicted/compose-fail = lying 📦 "carried"
  with no retry). Only consume publishedDropKeys on durable accept; add
  BoundedIDSet.remove() to release evicted/failed slots (uses the dead dedupKey).
- Blocked senders resurfaced via archived "heard here earlier" echoes, the one
  path that bypassed the live block filter — filter at seed time.
- A late optimistic .sent clobbered the router's .carried state; extend
  ConversationStore.shouldSkipStatusUpdate to a full precedence guard
  (sending < sent < carried < delivered < read).
- Read receipts were permanently burned when the router dropped them (marked
  sent then dropped). sendReadReceipt/routeReadReceipt now return Bool; only
  record as sent on a successful route, else retry on the next read scan.
- MessageRouter.cleanupExpiredMessages() had no production caller, so DMs to a
  peer that never reconnects sat on .sending until relaunch — run it in the
  120s bridge sweep.
- Sightings tally now rolls over at midnight while idle; wave notification
  action localized across all 29 locales; bridged anon#tag uses suffix(4) like
  everything else; makeThrowawayIdentity delegates to NostrIdentity.generate();
  .swiftlint.yml excludes .claude worktrees.
- Add regression tests: carried→sent no-downgrade, carried→delivered upgrade,
  evicted pending drop stays retryable.
- Bump MARKETING_VERSION to 1.7.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Fix CI: adjust delivery-status benchmark and drop now-dead addSystemMessage

The stricter no-downgrade guard (delivered/carried never regress to sent) broke
two things the earlier commit didn't catch locally (perf tests are skipped in
the default run, and Periphery runs only in CI):

- PerformanceBaselineTests delivery benchmarks alternated sent <-> delivered
  assuming both directions apply; the delivered -> sent half is now correctly
  skipped, so the pass measured 0 updates. Alternate two delivered timestamps
  instead — every update is real, no downgrade.
- Routing the geoDM "not in a location channel" error into the thread removed
  the only caller of ChatPrivateConversationContext.addSystemMessage, leaving
  it (and its mock) dead per Periphery. Drop the protocol requirement, the mock
  impl, and the now-vacuous systemMessages.isEmpty assertions (the invariant is
  compile-time enforced: the context can no longer emit a public system line).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Address review findings: read-receipt dedup, carried-vs-sending, block-time echo purge

- Read receipts: claim the receipt in sentReadReceipts synchronously
  before spawning the routing task (chat open runs two read scans in one
  MainActor stretch, so the async insert let every unread message route
  twice), and release the claim when the route fails so the
  retry-on-failed-route behavior is preserved.

- Delivery status: extend the no-downgrade guard so the `.sending`
  stamp a pre-handshake resend emits can no longer clobber
  carried/delivered/read (the 📦 indicator survived `.sent` but not
  `.sending`).

- Archived echoes: blocking a peer now purges their carried public
  messages from the gossip archive at block time (UnifiedPeerService
  and /block), while the fingerprint-to-peerID mapping is still known —
  the seed-time filter can't resolve offline non-favorite strangers and
  stays only as defense-in-depth. New Transport hook (default no-op) +
  GossipSyncManager.removePublicMessages with immediate persist.

- Bridge courier: an envelope that can't encode within the drop size
  caps fails identically on every attempt; consume the dedup slot so
  the 120s retry sweep stops re-running Noise sealing on it.

- MeshSightingsTracker: cache the day-key DateFormatter instead of
  building one per call.

Tests: double-markAsRead dedup + failed-route retry, carried→sending
no-downgrade matrix, block-time purge (manager + service wiring),
oversize-drop slot consumption.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Also skip the sent→sending downgrade in the delivery-status guard

Codex review follow-up: sendPrivateMessage without an established Noise
session emits `.sending` asynchronously, so it can land after the
message already reached `.sent` and visibly walk "Sent" back to
"Sending...". Treat `.sending` as weaker than `.sent` too — the status
was already truthful. `.failed` → `.sending` stays allowed so a retry
after a real failure remains visible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Retain Codable properties in Periphery scan (same fix as #1421)

The noiseKey assign-only false positive fired persistently on this branch
(twice, including a rerun) despite the baselined USR. Byte-identical to the
fix on fix/announce-replay-link-steal so the branches merge cleanly in
either order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:44:02 +02:00
74b8a47d02 Empty-mesh liveliness: nearby conversations, echoes, wave, dead drops, radar (#1409)
* Empty-mesh liveliness: nearby conversations, echoes, wave action, dead drops, radar

The empty mesh timeline was a dead end: a grey zero and "nobody in range
yet". This turns it into a live surface and gives the app pull when the
mesh wakes up:

- Nearest conversation: background geohash sampling now tracks actual
  chat messages (not just presence) per regional channel; the empty state
  surfaces the busiest nearby conversation with a preview, one tap to
  join (GeohashChatActivityTracker, fed from GeoPresenceTracker).
- Echoes: the carried 6h store-and-forward window renders as dimmed
  "heard here earlier" rows at launch (new Transport
  collectArchivedPublicMessages -> GossipSyncManager snapshot, decoded
  with signature-derived nicknames; content-identity dedup guards
  against re-synced duplicates).
- Wave: the "bitchatters nearby" notification gains a "wave" quick
  action that broadcasts a mesh 👋 straight from the notification, even
  backgrounded (first UNNotificationCategory in the app).
- Dead drops: /drop pins a note to the current building geohash as a
  kind-1 location note with a 24h NIP-40 expiry; expired notes are now
  dropped client-side at ingest; the notices sheet shows "fades in Xh";
  a "location notes" toggle plus location-permission controls live in
  app info (also fixes the duplicated Voice section).
- Radar: an ambient sonar animation shows the radio scanning, with a
  privacy-safe daily tally ("N devices passed within range today" via
  salted per-day hashes) and a "notes left here" hint that opens the
  notices geo tab.

All new user-facing strings ship in all 29 locales. 1403 tests green,
including new suites for the activity tracker, sightings tally, and
note expiry/drop publishing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Review round: app-info polish, urgent/expiry parity, centered radar, pin fill, Codex P2s

- App info: LOCATION header uppercased like sibling sections; location
  notes and live voice descriptions shortened (29 locales); redundant
  "location access granted" line removed.
- Notices parity: urgent + expiry controls now show on the geo tab too;
  the bridged Nostr note carries ["t","urgent"] and NIP-40 so relay-side
  readers see both; urgent parsed back from incoming notes.
- Radar moved from the top of the empty state to the center of the chat
  area, below the help text (empty state fills the visible height).
- Header pin fills whenever the scope has notices (was: only unseen),
  and Nostr-only nearby notes now light it too.
- Codex P2 fixes: notification completion deferred until the wave action
  is handled (background suspension dropped the send); NIP-40 notes now
  prune on a timer when they expire while displayed; the location-notes
  kill switch retargets the nearby-notes counter immediately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Hide macOS segmented picker's built-in label in notices composer (duplicate 'expires in')

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Geo notes: permanent (∞) expiry default, urgent stays mesh-only

- Geo expiry picker gains ∞ as the default: a permanent note posts as a
  pure relay note (no NIP-40 tag, no mesh-board copy — a board copy must
  fade within days, contradicting the ∞ the user picked). 1/3/7d keep
  the board + bridged-note path with NIP-40.
- The notes manager is now owned by the notices sheet (not the list) so
  the composer local-echoes ∞ notes into the list; it revives via
  refresh() after a tab-switch cancel, and its expiry-prune timer
  survives cancel (weak self, dies with the instance).
- Urgent toggle returns to mesh-only per review — notes are ambient;
  the read-side urgent-tag parse stays so tagged notes still render.
- macOS: hide the segmented picker's built-in label (duplicate
  "expires in").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Clearing the mesh timeline dismisses echoes for good; tighter divider copy

Triple-tap /clear emptied the timeline but the next launch re-seeded
"heard here earlier" from the persisted archive. A MeshEchoSettings
watermark now records the clear; only messages heard after it come back
(the archive itself still carries everything for peers' sync). The
echo dedup keys reset with it, and panic wipe drops the watermark.

Divider copy tightened to "heard here earlier · last 6h" (29 locales).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Echoes visual polish: tinted history block, radar-captioned tally, ambient footer

Device-test feedback on the echoes screen:
- Archived echoes now sit on a subtle tinted background (secondary at
  8%) in addition to the dim, so "heard here earlier" reads as one
  distinct block; the divider carries the echo ID prefix to join it.
- "N devices passed within range today" moves out of the narration
  lines to sit centered under the radar as its caption.
- When the timeline holds only echoes/system lines, a compact ambient
  footer (small radar + tally + live hints) renders below the history
  instead of the whole ambient layer vanishing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Notes strip persists above the mesh chat; leaner empty-state narration

- The 📍 "notes left here" line was empty-state-only, so starting a
  conversation hid it. It is now a tappable strip pinned above the mesh
  timeline whenever unexpired notes exist at this place (opens the
  notices geo tab); the nearby-notes counter runs for the whole mesh
  timeline, not just the empty state.
- Empty state narration drops "nobody in range yet..." (the radar and
  the sightings caption already say it) and the nearby-conversation
  hint moves below the help line instead of splitting the narration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Radar means searching: hide the sweep once mesh peers are connected or reachable

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 13:24:44 +02:00
b31a63ce37 Burn down SwiftLint advisory violations from 109 to 4 (#1362)
Mechanical style fixes across the enabled rule set, mostly via
swiftlint --fix (trailing_comma, comma, colon, trailing_newline,
comment_spacing, unused_closure_parameter, unneeded_break_in_switch,
opening_brace) plus hand fixes:

- non_optional_string_data_conversion (45): .data(using: .utf8)! and
  ?? Data() fallbacks replaced with the non-optional Data(_.utf8),
  including two production sites (NIP-44 HKDF info constant and the
  announce canonicalization context/nickname bytes — byte-identical
  output, only the impossible-nil handling is gone).
- switch_case_alignment: LocationChannel had a misindented closing
  brace; also repaired an --fix artifact in BLEService's .none case.
- redundant_string_enum_value: TrustLevel raw values equal to the case
  names (encoded form unchanged).
- unused_optional_binding: let _ = binds replaced with != nil / is Bool.
- static_over_final_class: PreviewView.layerClass.
- Resolved the BinaryProtocolTests TODO by documenting that 8-byte
  recipient ID truncation is the fixed wire-field size, not a bug.

The 4 remaining violations are all todo markers for a shared
test-helpers module (tracked in #1088) and one Reuse note.

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 14:09:13 +02:00
IslamandGitHub 4cfcefcda6 BitFoundation module to centralize shared components (#1089)
* Run local packages’ tests as well on CI

* BitFoundation module to centralize shared components
2026-04-14 14:10:03 -05:00
a136b5b7e9 Expand coverage for relay, identity, and location flows (#1055)
* Expand coverage for relay, identity, and location flows

* Fix macOS SwiftPM CI failures

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2026-03-12 12:50:46 -10:00
90a5e8ba9d fix: Rate limit iOS peer notifications to prevent flood (#972)
* fix: Rate limit iOS peer notifications to prevent flood

- Remove aggressive formIntersection that cleared recentlySeenPeers
  when peers temporarily dropped, causing them to be treated as "new"
- Add 5-minute cooldown between notifications (aligns with Android)
- Use fixed notification identifier so iOS updates existing notification
  instead of creating new ones
- Only mark peers as seen when notification is sent, so peers arriving
  during cooldown are included in the next notification

Fixes notification spam every 10-30 seconds when peers fluctuate.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: Bump version to 1.5.1

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 09:51:26 -10:00
jack d2edb651c1 Add CI environment detection to isRunningTests
Add checks for GITHUB_ACTIONS, CI, and XCTestBundlePath environment
variables to reliably detect test/CI environments where notification
APIs should be skipped.
2025-11-25 09:29:26 -10:00
jack 858e878959 Fix CI test failures by simplifying isRunningTests check
Remove Bundle.main.bundleIdentifier == nil from isRunningTests
detection as it may have unintended side effects in CI environments.
The XCTestCase class check and XCTestConfigurationFilePath environment
variable are sufficient for detecting both XCTest and Swift Testing.
2025-11-25 09:20:38 -10:00
jack 3e680f40bf Add ChatViewModel testability and unit tests
- Add testable ChatViewModel initializer accepting Transport dependency
- Create MockTransport implementing full Transport protocol for testing
- Add 21 unit tests covering:
  - Initialization (delegate, services, nickname)
  - Message sending (basic, empty, mentions, commands)
  - Message receiving (delegate calls, public messages)
  - Peer connections (connect, disconnect, isPeerConnected)
  - Deduplication service integration
  - Private chat routing
  - Bluetooth state handling
  - Panic clear functionality
- Guard NotificationService methods against test environment crashes
- Make deduplicationService internal for test access

All 303 tests pass.
2025-11-25 09:20:38 -10:00
5034732515 Simplify validation, compression heuristics, and notification scheduling (#841)
* Simplify validation, compression heuristics, and notification scheduling

* Consolidate notification logic and add InputValidator monitoring

Follow-up improvements to address PR feedback:

1. Consolidate notification functions
   - Add interruptionLevel parameter to sendLocalNotification
   - Refactor sendNetworkAvailableNotification to use consolidated function
   - Removes 12 lines of duplicate code

2. Add monitoring to InputValidator
   - Log control character rejections for production monitoring
   - Privacy-preserving: logs length + count, not actual content
   - Uses .security category for proper log routing

3. Add comprehensive InputValidator tests
   - 28 test cases covering validation, control characters, unicode, edge cases
   - Ensures behavioral changes are well-tested and documented

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-22 12:33:37 +02:00
0776c9813c PeerID 30/n: Update some leftovers + case normalization (#839)
* PeerID 28/n: `ChatViewModel.getShortIDForNoiseKey`

* PeerID 29/n: `BLEService` + remove dupe funcs from #823

* `handleFileTransfer` to use PeerID

* `sendMessage` and `sendPrivateMessage`

* PeerID 30/n: Update some leftovers

* `lowercased()` inside PeerID for normalization

---------

Co-authored-by: jack <212554440+jackjackbits@users.noreply.github.com>
2025-10-19 20:40:45 +02:00
jackandGitHub b2214e30f5 Remove unused favorites and notification helpers (#693) 2025-09-30 12:44:46 +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
IslamandGitHub e72fe50ffa Perf: Add final to classes that are not inherited (#574) 2025-09-11 19:17:04 +02: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
Mario NachbaurandGitHub d4262fab6c Remove noisy at symbol in mention (#420) 2025-08-08 18:41:50 +02:00
jack c4ca38654d Improve private message notification behavior
- Don't show PM notifications when the private chat is already open
- Store peer ID in notification userInfo for reliable chat opening
- Tap notification to open the correct private chat using peer ID
- Check both in ChatViewModel (when receiving) and NotificationDelegate (when presenting)
2025-07-24 12:00:01 +02:00
jack b849cfbad3 Remove debug logging from notification system
- Clean up all temporary logging statements
- Production-ready notification implementation
- Network notifications trigger when peers become available
2025-07-22 17:42:53 +02:00
jack 3d3c711885 Remove app state checks to fix thread issues
- Completely remove UIApplication/NSApplication state checks
- Let NotificationDelegate handle foreground presentation
- All notifications now sent regardless of app state
- Fixes all thread checker warnings
2025-07-22 16:47:04 +02:00
jack 29c0fdca21 Fix macOS thread checker warning
- Move all app state checks inside DispatchQueue.main.async
- Ensures NSApplication.isActive is only accessed from main thread
- Prevents thread checker warnings on macOS
2025-07-22 16:46:03 +02:00
jack 7b81dd4c0d Fix remaining thread safety issues in notifications
- Favorite notifications now bypass sendLocalNotification to avoid thread issues
- All notification code properly wrapped in DispatchQueue.main.async
- Added logging for favorite notifications
- Network and favorite notifications now work independently
2025-07-22 16:44:34 +02:00
jack 9f42d91903 Fix thread safety issue for notifications
- Wrap UIApplication.applicationState access in DispatchQueue.main.async
- Network notifications bypass app state check entirely
- Added more detailed logging for debugging
- Notifications now handled properly from background threads
2025-07-22 16:41:43 +02:00
jack 537d489daa Fix network notifications and add debugging
- Network notifications now show even when app is in foreground
- Added unique identifiers to prevent iOS deduplication
- Set interruptionLevel to timeSensitive for prominence
- Added comprehensive logging throughout notification flow
- Improved error handling for notification permissions
2025-07-22 16:39:56 +02:00
jack 79d9f8ee88 Add network availability notifications when bitchatters are nearby
- Show notification when network transitions from empty to having peers
- Single notification per session, resets when network becomes empty
- Background Bluetooth modes enabled for iOS
- Generic message: 'bitchatters nearby\! 1 person around' or 'X people around'
2025-07-22 16:32:07 +02:00
jack 9794f3ebdc UI improvements and rename rooms to channels
- Changed system messages from green to grey with consistent 12pt font
- Fixed text wrapping to flow naturally under timestamps
- Changed default nickname to anonXXXX format
- Replaced text with icon representations in status bar
- Added icons to sidebar section headers
- Made autocomplete UI consistent between commands and @mentions
- Added welcome message for new users (3 second delay)
- Changed sidebar header to 'YOUR NETWORK'
- Added command aliases (/join, /msg)
- Implemented /hug and /slap commands with haptic feedback
- Improved command help display with alphabetization
- Renamed 'rooms' to 'channels' throughout entire codebase
2025-07-08 01:42:35 +02:00
jack cbcfaed7a2 Fix compilation errors and prepare for App Store submission
- Fix NotificationService warnings by replacing unused error parameters with _
- Add public domain header to NotificationService.swift
- Fix BluetoothMeshService compilation errors:
  - Replace removeAll with filter for processedKeyExchanges cleanup
  - Remove duplicate cleanupStalePeers function declaration
  - Remove duplicate peerLastSeenTimestamps property declaration
- All code now compiles cleanly for both iOS and macOS targets
2025-07-04 17:28:36 +02:00
jack 4d61cec032 Prepare for App Store submission
- Added public domain headers to all test files
- Updated Info.plist with required App Store keys:
  - ITSAppUsesNonExemptEncryption = NO
  - LSApplicationCategoryType = Social Networking
  - UIRequiresFullScreen = YES
- Created proper Assets.xcassets structure
- Configured AppIcon.appiconset with all icon references
- Removed last TODO comment
- Created comprehensive App Store submission checklist
- Updated project.yml to include Assets.xcassets

The app is now ready for App Store submission. All debug code has been removed,
icons are configured, and privacy/security compliance is documented.
2025-07-04 16:44:00 +02:00
jack a569b87a18 Fix store-and-forward duplicate message delivery and timestamp ordering
- Add tracking to prevent sending cached messages multiple times to the same peer
- Only cache messages for offline favorites, not online ones
- Sort messages by timestamp when adding to ensure proper chronological order
- Clean up tracking sets when peers disconnect to allow re-sending on reconnect
- Cache our own messages when sending to offline favorites
- Update favorite online notification text to "wanna get in there?"
2025-07-04 15:02:49 +02:00
jack 2cd8443292 Remove grey overlay and reduce logging noise
- Removed grey background overlay during sidebar slide-over for cleaner UI
- Removed excessive logging while keeping critical error/security logs
- Kept only: error conditions, connection events, panic mode, security events
- Significantly reduced console noise for better production readiness
2025-07-04 12:28:00 +02:00
jack fe986ed397 Fix encryption key exchange and add persistent identity for favorites
- Implement three-tier key system: ephemeral encryption, ephemeral signing, persistent identity
- Fix signature verification by properly separating KeyAgreement and Signing keys
- Add persistent identity key for favorites that survives app restarts
- Fix Data subscript crashes by converting to byte arrays
- Add proper error handling for key exchange
- Private messages remain fully encrypted with ephemeral keys
2025-07-04 01:43:16 +02:00
jack d6ae19e11e Enable notifications on macOS
- Add macOS support to NotificationService
- Use NSApplication.shared.isActive to check app state on macOS
- Move notification logic outside of iOS-only block in ChatViewModel
- Notifications now work for both mentions and private messages on macOS
- Keep haptic feedback iOS-only as it's not available on macOS
2025-07-03 23:21:29 +02:00
jack 6652d1f429 Fix voice recording and notification issues
- Fix mic button gesture handling by removing Button wrapper
  - Use direct gesture on ZStack for more reliable touch detection
  - Add debug logging for recording start/stop

- Fix notification delivery on iOS
  - Change from checking .background to checking != .active
  - Notifications now work when app is inactive or background
  - Add NSUserNotificationsUsageDescription to Info.plist

- Improve voice note fragment transmission
  - Reduce fragment size from 400 to 200 bytes for better reliability
  - Simplify fragment timing from batch-based to linear 100ms delays
  - Lower packet size threshold from 512 to 400 bytes for fragmentation
  - Add peripheral connection state checking before writes
  - Better error logging throughout transmission pipeline

- Add comprehensive debug logging for troubleshooting
  - Log audio file size and duration
  - Log peripheral connection states during broadcast
  - Log fragment send progress with timing info
2025-07-03 23:14:42 +02:00
jack 2993fc3e06 Implement favorites, notifications, and improve fragment handling
- Add favorites functionality for peers with persistent storage
  - Star icon to toggle favorites in sidebar
  - Favorites appear at top of peer list
  - Storage persists across app launches using peer ID

- Add local notifications for mentions and private messages
  - Notifications appear when app is in background
  - Separate notification types for mentions vs private messages
  - Request notification permissions on app launch

- Fix sidebar header alignment to match main toolbar (44pt)
  - Add background color to sidebar header for visual consistency

- Improve voice note fragment transmission
  - Send fragments in batches of 5 with delays to prevent congestion
  - Better logging to debug fragment transmission issues
  - Reduce delay between fragments from 50ms to batch-based timing

- Fix other UI issues
  - Ensure mic recording ripples originate from mic button
  - Update sidebar to swipe from right edge smoothly
2025-07-03 22:13:28 +02:00