Notes counter: show 0 by default; subscribe at building-level (precision 8) for notes and counter; keep building hidden in channel list

This commit is contained in:
jack
2025-09-13 11:59:55 +02:00
parent 5bf28e5ba4
commit f10e5d1788
5 changed files with 24 additions and 10 deletions
+6
View File
@@ -2,6 +2,7 @@ import Foundation
/// Levels of location channels mapped to geohash precisions. /// Levels of location channels mapped to geohash precisions.
enum GeohashChannelLevel: CaseIterable, Codable, Equatable { enum GeohashChannelLevel: CaseIterable, Codable, Equatable {
case building
case block case block
case neighborhood case neighborhood
case city case city
@@ -11,6 +12,7 @@ enum GeohashChannelLevel: CaseIterable, Codable, Equatable {
/// Geohash length used for this level. /// Geohash length used for this level.
var precision: Int { var precision: Int {
switch self { switch self {
case .building: return 8
case .block: return 7 case .block: return 7
case .neighborhood: return 6 case .neighborhood: return 6
case .city: return 5 case .city: return 5
@@ -21,6 +23,7 @@ enum GeohashChannelLevel: CaseIterable, Codable, Equatable {
var displayName: String { var displayName: String {
switch self { switch self {
case .building: return "Building"
case .block: return "Block" case .block: return "Block"
case .neighborhood: return "Neighborhood" case .neighborhood: return "Neighborhood"
case .city: return "City" case .city: return "City"
@@ -35,6 +38,7 @@ extension GeohashChannelLevel {
let container = try decoder.singleValueContainer() let container = try decoder.singleValueContainer()
if let raw = try? container.decode(String.self) { if let raw = try? container.decode(String.self) {
switch raw { switch raw {
case "building": self = .building
case "block": self = .block case "block": self = .block
case "neighborhood": self = .neighborhood case "neighborhood": self = .neighborhood
case "city": self = .city case "city": self = .city
@@ -46,6 +50,7 @@ extension GeohashChannelLevel {
} }
} else if let precision = try? container.decode(Int.self) { } else if let precision = try? container.decode(Int.self) {
switch precision { switch precision {
case 8: self = .building
case 7: self = .block case 7: self = .block
case 6: self = .neighborhood case 6: self = .neighborhood
case 5: self = .city case 5: self = .city
@@ -61,6 +66,7 @@ extension GeohashChannelLevel {
func encode(to encoder: Encoder) throws { func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer() var container = encoder.singleValueContainer()
switch self { switch self {
case .building: try container.encode("building")
case .block: try container.encode("block") case .block: try container.encode("block")
case .neighborhood: try container.encode("neighborhood") case .neighborhood: try container.encode("neighborhood")
case .city: try container.encode("city") case .city: try container.encode("city")
@@ -291,6 +291,12 @@ final class LocationChannelManager: NSObject, CLLocationManagerDelegate, Observa
} else if let locality = pm.locality, !locality.isEmpty { } else if let locality = pm.locality, !locality.isEmpty {
dict[.block] = locality dict[.block] = locality
} }
// Building: prefer place name/street/venue when available
if let name = pm.name, !name.isEmpty {
dict[.building] = name
} else if let thoroughfare = pm.thoroughfare, !thoroughfare.isEmpty {
dict[.building] = thoroughfare
}
return dict return dict
} }
} }
+3 -3
View File
@@ -6,7 +6,7 @@ final class LocationNotesCounter: ObservableObject {
static let shared = LocationNotesCounter() static let shared = LocationNotesCounter()
@Published private(set) var geohash: String? = nil @Published private(set) var geohash: String? = nil
@Published private(set) var count: Int? = nil @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 private var subscriptionID: String? = nil
@@ -19,7 +19,7 @@ final class LocationNotesCounter: ObservableObject {
if geohash == norm { return } if geohash == norm { return }
cancel() cancel()
geohash = norm geohash = norm
count = nil count = 0
noteIDs.removeAll() noteIDs.removeAll()
initialLoadComplete = false initialLoadComplete = false
@@ -46,7 +46,7 @@ final class LocationNotesCounter: ObservableObject {
} }
subscriptionID = nil subscriptionID = nil
geohash = nil geohash = nil
count = nil count = 0
noteIDs.removeAll() noteIDs.removeAll()
} }
} }
+7 -6
View File
@@ -1180,7 +1180,7 @@ struct ContentView: View {
LocationChannelManager.shared.enableLocationChannels() LocationChannelManager.shared.enableLocationChannels()
LocationChannelManager.shared.refreshChannels() LocationChannelManager.shared.refreshChannels()
// If we already have a block geohash, pass it; otherwise wait in the sheet. // If we already have a block geohash, pass it; otherwise wait in the sheet.
notesGeohash = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .block })?.geohash notesGeohash = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .building })?.geohash
showLocationNotes = true showLocationNotes = true
}) { }) {
HStack(alignment: .center, spacing: 4) { HStack(alignment: .center, spacing: 4) {
@@ -1193,6 +1193,7 @@ struct ContentView: View {
.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)
@@ -1234,7 +1235,7 @@ struct ContentView: View {
} }
.sheet(isPresented: $showLocationNotes) { .sheet(isPresented: $showLocationNotes) {
Group { Group {
if let gh = notesGeohash ?? LocationChannelManager.shared.availableChannels.first(where: { $0.level == .block })?.geohash { if let gh = notesGeohash ?? LocationChannelManager.shared.availableChannels.first(where: { $0.level == .building })?.geohash {
LocationNotesView(geohash: gh) LocationNotesView(geohash: gh)
.environmentObject(viewModel) .environmentObject(viewModel)
} else { } else {
@@ -1268,8 +1269,8 @@ struct ContentView: View {
.background(backgroundColor) .background(backgroundColor)
.foregroundColor(textColor) .foregroundColor(textColor)
.onChange(of: locationManager.availableChannels) { channels in .onChange(of: locationManager.availableChannels) { channels in
if notesGeohash == nil, let block = channels.first(where: { $0.level == .block }) { if notesGeohash == nil, let building = channels.first(where: { $0.level == .building }) {
notesGeohash = block.geohash notesGeohash = building.geohash
} }
} }
} }
@@ -1481,8 +1482,8 @@ extension ContentView {
private func updateNotesCounterSubscription() { private func updateNotesCounterSubscription() {
switch locationManager.selectedChannel { switch locationManager.selectedChannel {
case .mesh: case .mesh:
if let block = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .block })?.geohash { if let building = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .building })?.geohash {
LocationNotesCounter.shared.subscribe(geohash: block) LocationNotesCounter.shared.subscribe(geohash: building)
} else { } else {
LocationNotesCounter.shared.cancel() LocationNotesCounter.shared.cancel()
} }
+2 -1
View File
@@ -107,7 +107,7 @@ struct LocationChannelsSheet: View {
// Nearby options // Nearby options
if !manager.availableChannels.isEmpty { if !manager.availableChannels.isEmpty {
ForEach(manager.availableChannels) { channel in ForEach(manager.availableChannels.filter { $0.level != .building }) { channel in
let coverage = coverageString(forPrecision: channel.geohash.count) let coverage = coverageString(forPrecision: channel.geohash.count)
let nameBase = locationName(for: channel.level) let nameBase = locationName(for: channel.level)
let namePart = nameBase.map { formattedNamePrefix(for: channel.level) + $0 } let namePart = nameBase.map { formattedNamePrefix(for: channel.level) + $0 }
@@ -381,6 +381,7 @@ struct LocationChannelsSheet: View {
case 5: return .city case 5: return .city
case 6: return .neighborhood case 6: return .neighborhood
case 7: return .block case 7: return .block
case 8: return .building
default: return .block default: return .block
} }
} }