mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-27 09:05:19 +00:00
Notes counter: subscribe only to building geohash (precision 8) so count reflects current 8-cell; auto-begin live location refresh on mesh (and permission) to update as you walk
This commit is contained in:
@@ -9,8 +9,7 @@ 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
|
||||||
|
|
||||||
// Support multiple subscriptions (e.g., building + parent block fallback)
|
private var subscriptionID: String? = nil
|
||||||
private var subscriptionIDs: Set<String> = []
|
|
||||||
private var noteIDs = Set<String>()
|
private var noteIDs = Set<String>()
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
@@ -24,40 +23,27 @@ final class LocationNotesCounter: ObservableObject {
|
|||||||
noteIDs.removeAll()
|
noteIDs.removeAll()
|
||||||
initialLoadComplete = false
|
initialLoadComplete = false
|
||||||
|
|
||||||
// Subscribe to both building and parent block to capture legacy notes
|
// Subscribe only to the building geohash (precision 8)
|
||||||
var targets: [String] = [norm]
|
let subID = "locnotes-count-\(norm)-\(UUID().uuidString.prefix(6))"
|
||||||
if norm.count >= 8 {
|
subscriptionID = subID
|
||||||
let parent7 = String(norm.prefix(7))
|
let filter = NostrFilter.geohashNotes(norm, since: nil, limit: 500)
|
||||||
if parent7 != norm { targets.append(parent7) }
|
let relays = GeoRelayDirectory.shared.closestRelays(toGeohash: norm, count: TransportConfig.nostrGeoRelayCount)
|
||||||
}
|
NostrRelayManager.shared.subscribe(filter: filter, id: subID, relayUrls: relays, handler: { [weak self] event in
|
||||||
var pendingEOSE = Set<String>()
|
guard let self = self else { return }
|
||||||
for target in targets {
|
guard event.kind == NostrProtocol.EventKind.textNote.rawValue else { return }
|
||||||
let subID = "locnotes-count-\(target)-\(UUID().uuidString.prefix(6))"
|
guard event.tags.contains(where: { $0.count >= 2 && $0[0].lowercased() == "g" && $0[1].lowercased() == norm }) else { return }
|
||||||
subscriptionIDs.insert(subID)
|
if !self.noteIDs.contains(event.id) {
|
||||||
pendingEOSE.insert(subID)
|
self.noteIDs.insert(event.id)
|
||||||
let filter = NostrFilter.geohashNotes(target, since: nil, limit: 500)
|
self.count = self.noteIDs.count
|
||||||
let relays = GeoRelayDirectory.shared.closestRelays(toGeohash: target, count: TransportConfig.nostrGeoRelayCount)
|
}
|
||||||
NostrRelayManager.shared.subscribe(filter: filter, id: subID, relayUrls: relays, handler: { [weak self] event in
|
}, onEOSE: { [weak self] in
|
||||||
guard let self = self else { return }
|
self?.initialLoadComplete = true
|
||||||
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() {
|
||||||
for sub in subscriptionIDs { NostrRelayManager.shared.unsubscribe(id: sub) }
|
if let sub = subscriptionID { NostrRelayManager.shared.unsubscribe(id: sub) }
|
||||||
subscriptionIDs.removeAll()
|
subscriptionID = nil
|
||||||
geohash = nil
|
geohash = nil
|
||||||
count = 0
|
count = 0
|
||||||
noteIDs.removeAll()
|
noteIDs.removeAll()
|
||||||
|
|||||||
@@ -858,7 +858,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
self.torRestartPending = true
|
self.torRestartPending = true
|
||||||
// Post only in geohash channels (queue if not active)
|
// Post only in geohash channels (queue if not active)
|
||||||
self.addGeohashOnlySystemMessage("tor restarting to recover connectivity…")
|
self.addGeohashOnlySystemMessage("tor restarting to recover connectivity...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1095,7 +1095,7 @@ struct ContentView: View {
|
|||||||
TextField("nickname", text: $viewModel.nickname)
|
TextField("nickname", text: $viewModel.nickname)
|
||||||
.textFieldStyle(.plain)
|
.textFieldStyle(.plain)
|
||||||
.font(.system(size: 14, design: .monospaced))
|
.font(.system(size: 14, design: .monospaced))
|
||||||
.frame(maxWidth: 100)
|
.frame(maxWidth: 80)
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
.focused($isNicknameFieldFocused)
|
.focused($isNicknameFieldFocused)
|
||||||
.autocorrectionDisabled(true)
|
.autocorrectionDisabled(true)
|
||||||
@@ -1188,14 +1188,12 @@ struct ContentView: View {
|
|||||||
Image(systemName: "long.text.page.and.pencil")
|
Image(systemName: "long.text.page.and.pencil")
|
||||||
.font(.system(size: 12))
|
.font(.system(size: 12))
|
||||||
.foregroundColor(Color(hue: 0.60, saturation: 0.85, brightness: 0.82))
|
.foregroundColor(Color(hue: 0.60, saturation: 0.85, brightness: 0.82))
|
||||||
if let c = notesCounter.count {
|
.padding(.top, 1)
|
||||||
Text("\(c)")
|
Text("\(notesCounter.count ?? 0)")
|
||||||
.font(.system(size: 11, design: .monospaced))
|
.font(.system(size: 11, design: .monospaced))
|
||||||
.foregroundColor(Color(hue: 0.60, saturation: 0.85, brightness: 0.82))
|
.foregroundColor(Color(hue: 0.60, saturation: 0.85, brightness: 0.82))
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.fixedSize(horizontal: true, vertical: false)
|
.fixedSize(horizontal: true, vertical: false)
|
||||||
.alignmentGuide(.firstTextBaseline) { d in d[.firstTextBaseline] }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.fixedSize(horizontal: true, vertical: false)
|
.fixedSize(horizontal: true, vertical: false)
|
||||||
}
|
}
|
||||||
@@ -1213,9 +1211,12 @@ struct ContentView: View {
|
|||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
.foregroundColor(headerCountColor)
|
.foregroundColor(headerCountColor)
|
||||||
|
.lineLimit(1)
|
||||||
|
.fixedSize(horizontal: true, vertical: false)
|
||||||
|
|
||||||
// QR moved to the PEOPLE header in the sidebar when on mesh channel
|
// QR moved to the PEOPLE header in the sidebar when on mesh channel
|
||||||
}
|
}
|
||||||
|
.layoutPriority(3)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
withAnimation(.easeInOut(duration: TransportConfig.uiAnimationMediumSeconds)) {
|
withAnimation(.easeInOut(duration: TransportConfig.uiAnimationMediumSeconds)) {
|
||||||
showSidebar.toggle()
|
showSidebar.toggle()
|
||||||
@@ -1285,9 +1286,30 @@ struct ContentView: View {
|
|||||||
LocationChannelManager.shared.endLiveRefresh()
|
LocationChannelManager.shared.endLiveRefresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear { updateNotesCounterSubscription() }
|
.onAppear {
|
||||||
.onChange(of: locationManager.selectedChannel) { _ in updateNotesCounterSubscription() }
|
updateNotesCounterSubscription()
|
||||||
|
// Keep live updates running while on mesh to follow building changes
|
||||||
|
if case .mesh = locationManager.selectedChannel, locationManager.permissionState == .authorized {
|
||||||
|
LocationChannelManager.shared.beginLiveRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: locationManager.selectedChannel) { _ in
|
||||||
|
updateNotesCounterSubscription()
|
||||||
|
if case .mesh = locationManager.selectedChannel, locationManager.permissionState == .authorized {
|
||||||
|
LocationChannelManager.shared.beginLiveRefresh()
|
||||||
|
} else {
|
||||||
|
LocationChannelManager.shared.endLiveRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
.onChange(of: locationManager.availableChannels) { _ in updateNotesCounterSubscription() }
|
.onChange(of: locationManager.availableChannels) { _ in updateNotesCounterSubscription() }
|
||||||
|
.onChange(of: locationManager.permissionState) { _ in
|
||||||
|
updateNotesCounterSubscription()
|
||||||
|
if case .mesh = locationManager.selectedChannel, locationManager.permissionState == .authorized {
|
||||||
|
LocationChannelManager.shared.beginLiveRefresh()
|
||||||
|
} else {
|
||||||
|
LocationChannelManager.shared.endLiveRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
.alert("heads up", isPresented: $viewModel.showScreenshotPrivacyWarning) {
|
.alert("heads up", isPresented: $viewModel.showScreenshotPrivacyWarning) {
|
||||||
Button("ok", role: .cancel) {}
|
Button("ok", role: .cancel) {}
|
||||||
} message: {
|
} message: {
|
||||||
|
|||||||
Reference in New Issue
Block a user