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

This commit is contained in:
jack
2025-09-13 12:09:41 +02:00
parent 8cc6d538ae
commit 6cf6e3c420
2 changed files with 39 additions and 31 deletions
+33 -20
View File
@@ -9,7 +9,8 @@ final class LocationNotesCounter: ObservableObject {
@Published private(set) var count: Int? = 0 @Published private(set) var count: Int? = 0
@Published private(set) var initialLoadComplete: Bool = false @Published private(set) var initialLoadComplete: Bool = false
private var subscriptionID: String? = nil // Support multiple subscriptions (e.g., building + parent block fallback)
private var subscriptionIDs: Set<String> = []
private var noteIDs = Set<String>() private var noteIDs = Set<String>()
private init() {} private init() {}
@@ -23,28 +24,40 @@ final class LocationNotesCounter: ObservableObject {
noteIDs.removeAll() noteIDs.removeAll()
initialLoadComplete = false initialLoadComplete = false
let subID = "locnotes-count-\(norm)-\(UUID().uuidString.prefix(6))" // Subscribe to both building and parent block to capture legacy notes
subscriptionID = subID var targets: [String] = [norm]
let filter = NostrFilter.geohashNotes(norm, since: nil, limit: 500) if norm.count >= 8 {
let relays = GeoRelayDirectory.shared.closestRelays(toGeohash: norm, count: TransportConfig.nostrGeoRelayCount) let parent7 = String(norm.prefix(7))
NostrRelayManager.shared.subscribe(filter: filter, id: subID, relayUrls: relays, handler: { [weak self] event in if parent7 != norm { targets.append(parent7) }
guard let self = self else { return } }
guard event.kind == NostrProtocol.EventKind.textNote.rawValue else { return } var pendingEOSE = Set<String>()
guard event.tags.contains(where: { $0.count >= 2 && $0[0].lowercased() == "g" && $0[1].lowercased() == norm }) else { return } for target in targets {
if !self.noteIDs.contains(event.id) { let subID = "locnotes-count-\(target)-\(UUID().uuidString.prefix(6))"
self.noteIDs.insert(event.id) subscriptionIDs.insert(subID)
self.count = self.noteIDs.count pendingEOSE.insert(subID)
} let filter = NostrFilter.geohashNotes(target, since: nil, limit: 500)
}, onEOSE: { [weak self] in let relays = GeoRelayDirectory.shared.closestRelays(toGeohash: target, count: TransportConfig.nostrGeoRelayCount)
self?.initialLoadComplete = true NostrRelayManager.shared.subscribe(filter: filter, id: subID, relayUrls: relays, handler: { [weak self] event in
}) guard let self = self else { return }
guard event.kind == NostrProtocol.EventKind.textNote.rawValue else { return }
// Ensure matching g-tag for either building or parent block
let matches = event.tags.contains(where: { $0.count >= 2 && $0[0].lowercased() == "g" && targets.contains($0[1].lowercased()) })
guard matches else { return }
if !self.noteIDs.contains(event.id) {
self.noteIDs.insert(event.id)
self.count = self.noteIDs.count
}
}, onEOSE: { [weak self] in
guard let self = self else { return }
pendingEOSE.remove(subID)
if pendingEOSE.isEmpty { self.initialLoadComplete = true }
})
}
} }
func cancel() { func cancel() {
if let sub = subscriptionID { for sub in subscriptionIDs { NostrRelayManager.shared.unsubscribe(id: sub) }
NostrRelayManager.shared.unsubscribe(id: sub) subscriptionIDs.removeAll()
}
subscriptionID = nil
geohash = nil geohash = nil
count = 0 count = 0
noteIDs.removeAll() noteIDs.removeAll()
+6 -11
View File
@@ -52,7 +52,7 @@ struct ContentView: View {
@State private var expandedMessageIDs: Set<String> = [] @State private var expandedMessageIDs: Set<String> = []
@State private var showLocationNotes = false @State private var showLocationNotes = false
@State private var notesGeohash: String? = nil @State private var notesGeohash: String? = nil
@State private var notesRefreshTimer: Timer? = nil // Timer-based refresh removed; use LocationChannelManager live updates instead
// Window sizes for rendering (infinite scroll up) // Window sizes for rendering (infinite scroll up)
@State private var windowCountPublic: Int = 300 @State private var windowCountPublic: Int = 300
@State private var windowCountPrivate: [String: Int] = [:] @State private var windowCountPrivate: [String: Int] = [:]
@@ -1174,8 +1174,8 @@ struct ContentView: View {
} }
.buttonStyle(.plain) .buttonStyle(.plain)
// Notes icon (mesh only), to the right of #mesh // Notes icon (mesh only and when location is authorized), to the right of #mesh
if case .mesh = locationManager.selectedChannel { if case .mesh = locationManager.selectedChannel, locationManager.permissionState == .authorized {
Button(action: { Button(action: {
// Kick a one-shot refresh and show the sheet immediately. // Kick a one-shot refresh and show the sheet immediately.
LocationChannelManager.shared.enableLocationChannels() LocationChannelManager.shared.enableLocationChannels()
@@ -1277,17 +1277,12 @@ struct ContentView: View {
} }
} }
.onAppear { .onAppear {
// Ensure we are authorized and start periodic refresh while the sheet is open // Ensure we are authorized and start live location updates (distance-filtered)
LocationChannelManager.shared.enableLocationChannels() LocationChannelManager.shared.enableLocationChannels()
LocationChannelManager.shared.refreshChannels() LocationChannelManager.shared.beginLiveRefresh()
notesRefreshTimer?.invalidate()
notesRefreshTimer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { _ in
LocationChannelManager.shared.refreshChannels()
}
} }
.onDisappear { .onDisappear {
notesRefreshTimer?.invalidate() LocationChannelManager.shared.endLiveRefresh()
notesRefreshTimer = nil
} }
} }
.onAppear { updateNotesCounterSubscription() } .onAppear { updateNotesCounterSubscription() }