mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 23:25:20 +00:00
* Location notes: Matrix loader with 1–3s minimum display; allow multiple notes; wire notes UI from mesh toolbar * Location notes: prevent duplicate loading/subscriptions (StateObject manager, geohash captured at open, guard subscribe) * Location notes: fix duplicate state redeclaration in ContentView * Location notes: render note bodies with monospaced font in notes list * Location notes: add close (x) button in sheet header (upper-right) using @Environment(\.dismiss) * Location notes: acquire geohash on open (force refresh) and show Matrix loader until block-level geohash resolves; add LocationNotesSheet wrapper * Location notes: inline sheet wrapper to avoid missing file in target; force location refresh and show Matrix loader until block geohash resolves * Location notes: remove Matrix animation; use simple spinner when acquiring location; simplify notes view layout * Location notes: remove per-note relative timestamp from sheet (no seconds display) * Location notes: fix intermittent first load by ensuring resubscribe after geohash change, removing over-eager subscribe guard, and widening note fetch window (no since filter) * Location notes: add background counter service and show count next to notes icon on mesh; auto-subscribe to current block geohash * Location notes: move notes icon to the right of #mesh badge in toolbar * Location notes: restore timestamps in notes list; remove loading state from manager and sheet (no spinner/animation) * Location notes: drop 'teleport' tag from kind-1 events; simplify note builder API and usage * Location notes: show timestamp as relative within 7 days, else absolute date (MMM d or MMM d, y); keep monospaced styling * Location notes: append 'ago' to relative timestamps (within 7 days) * Switch location notes and UI helpers to building-level geohash (precision 8): add GeohashChannelLevel.building, map length 8, use building for notes selection and counter, add building name mapping * Location notes: change notes toolbar icon to SF Symbol 'long.text.page.and.pencil' * Revert notes geohash selection back to block-level (precision 7); keep building level available but unused; update counter and sheet accordingly * Location notes: show block name (from reverse geocode) in header instead of 'street-level notes' * Nostr: add EOSE handling with callback support for subscriptions; wire to LocationNotesManager/Counter (initialLoadComplete). Remove 'building' level from channel enum and geocoder mapping; geohash list shows block/neighborhood/city/province/region only * Fix Swift 6 isolation: hop to @MainActor inside Timer callback for EOSE tracker mutations * Notes counter: show 0 by default; subscribe at building-level (precision 8) for notes and counter; keep building hidden in channel list * Notes header: show building name when available (fallback to block name) * 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 * 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 * Notes header: show count in parentheses; icon shows blue if any notes, grey if none; only start live location updates while sheet is open; reduce nickname width; set live distance filter to 10m * Notes header: show count as '(N note/notes)' with non-bold, secondary styling next to title * Notes header: move count before title as '<N> note(s) @ #gh'; remove parentheses; keep count non-bold * Notes header: bold count text; ensure sheet updates when building geohash changes by calling manager.setGeohash on geohash change * Notes sheet: add light haptic feedback when building geohash changes (iOS only) * Notes icon: force a one-shot location refresh before (re)subscribing the counter so icon turns blue immediately on current 8-cell * Notes subscribe: fallback to default relays when GeoRelayDirectory has no entries (pass nil relayUrls) so counter/icon turn blue and sheet loads even before georelay fetch * Notes icon: turn blue immediately when sheet shows notes by passing count up from sheet (closure) and OR-ing with counter; reset on sheet close --------- Co-authored-by: jack <jackjackbits@users.noreply.github.com>
53 lines
2.0 KiB
Swift
53 lines
2.0 KiB
Swift
import Foundation
|
|
|
|
/// Lightweight background counter for location notes (kind 1) at block-level geohash.
|
|
@MainActor
|
|
final class LocationNotesCounter: ObservableObject {
|
|
static let shared = LocationNotesCounter()
|
|
|
|
@Published private(set) var geohash: String? = nil
|
|
@Published private(set) var count: Int? = 0
|
|
@Published private(set) var initialLoadComplete: Bool = false
|
|
|
|
private var subscriptionID: String? = nil
|
|
private var noteIDs = Set<String>()
|
|
|
|
private init() {}
|
|
|
|
func subscribe(geohash gh: String) {
|
|
let norm = gh.lowercased()
|
|
if geohash == norm { return }
|
|
cancel()
|
|
geohash = norm
|
|
count = 0
|
|
noteIDs.removeAll()
|
|
initialLoadComplete = false
|
|
|
|
// Subscribe only to the building geohash (precision 8)
|
|
let subID = "locnotes-count-\(norm)-\(UUID().uuidString.prefix(6))"
|
|
subscriptionID = subID
|
|
let filter = NostrFilter.geohashNotes(norm, since: nil, limit: 500)
|
|
let relays = GeoRelayDirectory.shared.closestRelays(toGeohash: norm, count: TransportConfig.nostrGeoRelayCount)
|
|
let relayUrls: [String]? = relays.isEmpty ? nil : relays
|
|
NostrRelayManager.shared.subscribe(filter: filter, id: subID, relayUrls: relayUrls, handler: { [weak self] event in
|
|
guard let self = self else { return }
|
|
guard event.kind == NostrProtocol.EventKind.textNote.rawValue else { return }
|
|
guard event.tags.contains(where: { $0.count >= 2 && $0[0].lowercased() == "g" && $0[1].lowercased() == norm }) else { return }
|
|
if !self.noteIDs.contains(event.id) {
|
|
self.noteIDs.insert(event.id)
|
|
self.count = self.noteIDs.count
|
|
}
|
|
}, onEOSE: { [weak self] in
|
|
self?.initialLoadComplete = true
|
|
})
|
|
}
|
|
|
|
func cancel() {
|
|
if let sub = subscriptionID { NostrRelayManager.shared.unsubscribe(id: sub) }
|
|
subscriptionID = nil
|
|
geohash = nil
|
|
count = 0
|
|
noteIDs.removeAll()
|
|
}
|
|
}
|