mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 10:05:20 +00:00
* 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>
87 lines
2.2 KiB
Swift
87 lines
2.2 KiB
Swift
import Foundation
|
|
|
|
// MARK: - Message Deduplicator (shared)
|
|
|
|
final class MessageDeduplicator {
|
|
private struct Entry {
|
|
let messageID: String
|
|
let timestamp: Date
|
|
}
|
|
|
|
private var entries: [Entry] = []
|
|
private var lookup = Set<String>()
|
|
private let lock = NSLock()
|
|
private let maxAge: TimeInterval = TransportConfig.messageDedupMaxAgeSeconds // 5 minutes
|
|
private let maxCount = TransportConfig.messageDedupMaxCount
|
|
|
|
/// Check if message is duplicate and add if not
|
|
func isDuplicate(_ messageID: String) -> Bool {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
|
|
cleanupOldEntries()
|
|
|
|
if lookup.contains(messageID) {
|
|
return true
|
|
}
|
|
|
|
entries.append(Entry(messageID: messageID, timestamp: Date()))
|
|
lookup.insert(messageID)
|
|
|
|
if entries.count > maxCount {
|
|
let toRemove = entries.prefix(100)
|
|
toRemove.forEach { lookup.remove($0.messageID) }
|
|
entries.removeFirst(100)
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
/// Add an ID without checking (for announce-back tracking)
|
|
func markProcessed(_ messageID: String) {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
|
|
if !lookup.contains(messageID) {
|
|
entries.append(Entry(messageID: messageID, timestamp: Date()))
|
|
lookup.insert(messageID)
|
|
}
|
|
}
|
|
|
|
/// Check if ID exists without adding
|
|
func contains(_ messageID: String) -> Bool {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
return lookup.contains(messageID)
|
|
}
|
|
|
|
/// Clear all entries
|
|
func reset() {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
|
|
entries.removeAll()
|
|
lookup.removeAll()
|
|
}
|
|
|
|
/// Periodic cleanup
|
|
func cleanup() {
|
|
lock.lock()
|
|
defer { lock.unlock() }
|
|
|
|
cleanupOldEntries()
|
|
|
|
if entries.capacity > maxCount * 2 {
|
|
entries.reserveCapacity(maxCount)
|
|
}
|
|
}
|
|
|
|
private func cleanupOldEntries() {
|
|
let cutoff = Date().addingTimeInterval(-maxAge)
|
|
while let first = entries.first, first.timestamp < cutoff {
|
|
lookup.remove(first.messageID)
|
|
entries.removeFirst()
|
|
}
|
|
}
|
|
}
|