Location notes: restore timestamps in notes list; remove loading state from manager and sheet (no spinner/animation)

This commit is contained in:
jack
2025-09-13 10:31:17 +02:00
parent d0bfb179ed
commit 086699961d
3 changed files with 27 additions and 70 deletions
@@ -21,12 +21,8 @@ final class LocationNotesManager: ObservableObject {
}
@Published private(set) var notes: [Note] = [] // reverse-chron sorted
@Published private(set) var isLoading: Bool = true
@Published private(set) var geohash: String
private var subscriptionID: String?
private var loadingWorkItem: DispatchWorkItem?
private var loadingStartedAt: Date?
private var loadingMinDuration: TimeInterval = 0
init(geohash: String) {
self.geohash = geohash.lowercased()
@@ -46,11 +42,6 @@ final class LocationNotesManager: ObservableObject {
}
private func subscribe() {
// Begin loading: always display Matrix for a randomized 13 seconds
isLoading = true
loadingStartedAt = Date()
loadingMinDuration = Double.random(in: 1.0...3.0)
loadingWorkItem?.cancel()
let subID = "locnotes-\(geohash)-\(UUID().uuidString.prefix(8))"
subscriptionID = subID
// For persistent notes, allow relays to return recent history without an aggressive time cutoff
@@ -67,24 +58,7 @@ final class LocationNotesManager: ObservableObject {
let note = Note(id: event.id, pubkey: event.pubkey, content: event.content, createdAt: ts, nickname: nick)
self.notes.append(note)
self.notes.sort { $0.createdAt > $1.createdAt }
// Respect minimum loader time
if self.isLoading {
let elapsed = Date().timeIntervalSince(self.loadingStartedAt ?? Date())
if elapsed >= self.loadingMinDuration {
self.isLoading = false
} else {
self.loadingWorkItem?.cancel()
let remaining = self.loadingMinDuration - elapsed
let wi = DispatchWorkItem { [weak self] in self?.isLoading = false }
self.loadingWorkItem = wi
DispatchQueue.main.asyncAfter(deadline: .now() + remaining, execute: wi)
}
}
}
// Ensure we hide after the minimum loader duration even if no events arrive
let wi = DispatchWorkItem { [weak self] in self?.isLoading = false }
loadingWorkItem = wi
DispatchQueue.main.asyncAfter(deadline: .now() + loadingMinDuration, execute: wi)
}
/// Send a location note for the current geohash using the per-geohash identity.
@@ -105,18 +79,6 @@ final class LocationNotesManager: ObservableObject {
// Optimistic local-echo
let echo = Note(id: event.id, pubkey: id.publicKeyHex, content: trimmed, createdAt: Date(), nickname: nickname)
self.notes.insert(echo, at: 0)
if self.isLoading {
let elapsed = Date().timeIntervalSince(self.loadingStartedAt ?? Date())
if elapsed >= self.loadingMinDuration {
self.isLoading = false
} else {
self.loadingWorkItem?.cancel()
let remaining = self.loadingMinDuration - elapsed
let wi = DispatchWorkItem { [weak self] in self?.isLoading = false }
self.loadingWorkItem = wi
DispatchQueue.main.asyncAfter(deadline: .now() + remaining, execute: wi)
}
}
} catch {
SecureLogger.error("LocationNotesManager: failed to send note: \(error)", category: .session)
}
@@ -128,7 +90,5 @@ final class LocationNotesManager: ObservableObject {
NostrRelayManager.shared.unsubscribe(id: sub)
subscriptionID = nil
}
loadingWorkItem?.cancel()
loadingWorkItem = nil
}
}
+24 -30
View File
@@ -1235,41 +1235,35 @@ struct ContentView: View {
LocationNotesView(geohash: gh)
.environmentObject(viewModel)
} else {
ZStack {
VStack(spacing: 0) {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text("notes")
.font(.system(size: 16, weight: .bold, design: .monospaced))
Text("acquiring location…")
.font(.system(size: 12, design: .monospaced))
.foregroundColor(secondaryTextColor)
}
Spacer()
Button(action: { showLocationNotes = false }) {
Image(systemName: "xmark")
.font(.system(size: 13, weight: .semibold, design: .monospaced))
.foregroundColor(textColor)
.frame(width: 32, height: 32)
}
.buttonStyle(.plain)
.accessibilityLabel("Close")
}
.frame(height: 44)
.padding(.horizontal, 12)
.background(backgroundColor.opacity(0.95))
VStack(spacing: 12) {
HStack {
Text("notes")
.font(.system(size: 16, weight: .bold, design: .monospaced))
Spacer()
Button(action: { showLocationNotes = false }) {
Image(systemName: "xmark")
.font(.system(size: 13, weight: .semibold, design: .monospaced))
.foregroundColor(textColor)
.frame(width: 32, height: 32)
}
.buttonStyle(.plain)
.accessibilityLabel("Close")
}
// Simple system spinner while waiting for geohash
ProgressView()
.progressViewStyle(.circular)
.frame(height: 44)
.padding(.horizontal, 12)
.background(backgroundColor.opacity(0.95))
Text("location unavailable")
.font(.system(size: 14, design: .monospaced))
.foregroundColor(secondaryTextColor)
Button("enable location") {
LocationChannelManager.shared.enableLocationChannels()
LocationChannelManager.shared.refreshChannels()
}
.buttonStyle(.bordered)
Spacer()
}
.background(backgroundColor)
.foregroundColor(textColor)
.onAppear {
LocationChannelManager.shared.enableLocationChannels()
LocationChannelManager.shared.refreshChannels()
}
.onChange(of: locationManager.availableChannels) { channels in
if notesGeohash == nil, let block = channels.first(where: { $0.level == .block }) {
notesGeohash = block.geohash
+3
View File
@@ -72,6 +72,9 @@ struct LocationNotesView: View {
Text(note.displayName)
.font(.system(size: 12, weight: .semibold, design: .monospaced))
.foregroundColor(secondaryTextColor)
Text(note.createdAt, style: .time)
.font(.system(size: 11, design: .monospaced))
.foregroundColor(secondaryTextColor.opacity(0.8))
}
Text(note.content)
.font(.system(size: 14, design: .monospaced))