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

This commit is contained in:
jack
2025-09-11 13:35:36 +02:00
parent 36b5fdabc4
commit 4894a0b04e
2 changed files with 51 additions and 32 deletions
+4
View File
@@ -61,6 +61,10 @@ struct BitchatApp: App {
// Optionally nudge Tor to dormant to save power
TorManager.shared.setAppForeground(false)
TorManager.shared.goDormantOnBackground()
// Stop geohash sampling while backgrounded
Task { @MainActor in
chatViewModel.endGeohashSampling()
}
// Proactively disconnect Nostr to avoid spurious socket errors while Tor is down
NostrRelayManager.shared.disconnect()
break
+47 -32
View File
@@ -614,30 +614,34 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
self.cancellables.insert(cancellable)
// Resubscribe geohash on relay reconnect
if let relayMgr = self.nostrRelayManager {
relayMgr.$isConnected
.receive(on: DispatchQueue.main)
.sink { [weak self] connected in
guard let self = self else { return }
if connected {
Task { @MainActor in
// Set up DM handler once on first connect
if !self.nostrHandlersSetup {
self.setupNostrMessageHandling()
self.nostrHandlersSetup = true
}
self.resubscribeCurrentGeohash()
// Re-init sampling for regional + bookmarked geohashes after reconnect
let regional = LocationChannelManager.shared.availableChannels.map { $0.geohash }
let bookmarks = GeohashBookmarksStore.shared.bookmarks
let union = Array(Set(regional).union(bookmarks))
// Resubscribe geohash on relay reconnect
if let relayMgr = self.nostrRelayManager {
relayMgr.$isConnected
.receive(on: DispatchQueue.main)
.sink { [weak self] connected in
guard let self = self else { return }
if connected {
Task { @MainActor in
// Set up DM handler once on first connect
if !self.nostrHandlersSetup {
self.setupNostrMessageHandling()
self.nostrHandlersSetup = true
}
self.resubscribeCurrentGeohash()
// Re-init sampling for regional + bookmarked geohashes after reconnect
let regional = LocationChannelManager.shared.availableChannels.map { $0.geohash }
let bookmarks = GeohashBookmarksStore.shared.bookmarks
let union = Array(Set(regional).union(bookmarks))
if TorManager.shared.isForeground() {
self.beginGeohashSampling(for: union)
} else {
self.endGeohashSampling()
}
}
}
.store(in: &self.cancellables)
}
}
.store(in: &self.cancellables)
}
}
// Set up Noise encryption callbacks
@@ -658,7 +662,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
self.switchLocationChannel(to: LocationChannelManager.shared.selectedChannel)
}
// Background: keep sampling nearby geohashes + bookmarks for notifications even when sheet is closed
// Foreground-only: sample nearby geohashes + bookmarks (disabled in background)
LocationChannelManager.shared.$availableChannels
.receive(on: DispatchQueue.main)
.sink { [weak self] channels in
@@ -667,7 +671,11 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
let bookmarks = GeohashBookmarksStore.shared.bookmarks
let union = Array(Set(regional).union(bookmarks))
Task { @MainActor in
self.beginGeohashSampling(for: union)
if TorManager.shared.isForeground() {
self.beginGeohashSampling(for: union)
} else {
self.endGeohashSampling()
}
}
}
.store(in: &cancellables)
@@ -680,17 +688,21 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
let regional = LocationChannelManager.shared.availableChannels.map { $0.geohash }
let union = Array(Set(regional).union(bookmarks))
Task { @MainActor in
self.beginGeohashSampling(for: union)
if TorManager.shared.isForeground() {
self.beginGeohashSampling(for: union)
} else {
self.endGeohashSampling()
}
}
}
.store(in: &cancellables)
// Kick off initial sampling if we have regional channels or bookmarks
// Kick off initial sampling if we have regional channels or bookmarks (foreground only)
do {
let regional = LocationChannelManager.shared.availableChannels.map { $0.geohash }
let bookmarks = GeohashBookmarksStore.shared.bookmarks
let union = Array(Set(regional).union(bookmarks))
if !union.isEmpty {
if !union.isEmpty && TorManager.shared.isForeground() {
Task { @MainActor in self.beginGeohashSampling(for: union) }
}
}
@@ -1931,6 +1943,11 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
/// Begin sampling multiple geohashes (used by channel sheet) without changing active channel.
@MainActor
func beginGeohashSampling(for geohashes: [String]) {
// Disable sampling when app is backgrounded (Tor is stopped there)
if !TorManager.shared.isForeground() {
endGeohashSampling()
return
}
// Determine which to add and which to remove
let desired = Set(geohashes)
let current = Set(geoSamplingSubs.values)
@@ -1976,15 +1993,13 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
// Avoid notifications for old sampled events when launching or (re)subscribing
let eventTime = Date(timeIntervalSince1970: TimeInterval(event.created_at))
if Date().timeIntervalSince(eventTime) > 30 { return }
// Foreground policy: allow if it's a different geohash than the one currently open
// Foreground-only notifications: app must be active, and not already viewing this geohash
#if os(iOS)
if UIApplication.shared.applicationState == .active {
if case .location(let ch) = self.activeChannel, ch.geohash == gh { return }
}
guard UIApplication.shared.applicationState == .active else { return }
if case .location(let ch) = self.activeChannel, ch.geohash == gh { return }
#elseif os(macOS)
if NSApplication.shared.isActive {
if case .location(let ch) = self.activeChannel, ch.geohash == gh { return }
}
guard NSApplication.shared.isActive else { return }
if case .location(let ch) = self.activeChannel, ch.geohash == gh { return }
#endif
// Cooldown per geohash
let now = Date()