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 notes: [Note] = [] // reverse-chron sorted
@Published private(set) var isLoading: Bool = true
@Published private(set) var geohash: String @Published private(set) var geohash: String
private var subscriptionID: String? private var subscriptionID: String?
private var loadingWorkItem: DispatchWorkItem?
private var loadingStartedAt: Date?
private var loadingMinDuration: TimeInterval = 0
init(geohash: String) { init(geohash: String) {
self.geohash = geohash.lowercased() self.geohash = geohash.lowercased()
@@ -46,11 +42,6 @@ final class LocationNotesManager: ObservableObject {
} }
private func subscribe() { 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))" let subID = "locnotes-\(geohash)-\(UUID().uuidString.prefix(8))"
subscriptionID = subID subscriptionID = subID
// For persistent notes, allow relays to return recent history without an aggressive time cutoff // 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) let note = Note(id: event.id, pubkey: event.pubkey, content: event.content, createdAt: ts, nickname: nick)
self.notes.append(note) self.notes.append(note)
self.notes.sort { $0.createdAt > $1.createdAt } 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. /// Send a location note for the current geohash using the per-geohash identity.
@@ -105,18 +79,6 @@ final class LocationNotesManager: ObservableObject {
// Optimistic local-echo // Optimistic local-echo
let echo = Note(id: event.id, pubkey: id.publicKeyHex, content: trimmed, createdAt: Date(), nickname: nickname) let echo = Note(id: event.id, pubkey: id.publicKeyHex, content: trimmed, createdAt: Date(), nickname: nickname)
self.notes.insert(echo, at: 0) 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 { } catch {
SecureLogger.error("LocationNotesManager: failed to send note: \(error)", category: .session) SecureLogger.error("LocationNotesManager: failed to send note: \(error)", category: .session)
} }
@@ -128,7 +90,5 @@ final class LocationNotesManager: ObservableObject {
NostrRelayManager.shared.unsubscribe(id: sub) NostrRelayManager.shared.unsubscribe(id: sub)
subscriptionID = nil subscriptionID = nil
} }
loadingWorkItem?.cancel()
loadingWorkItem = nil
} }
} }
+24 -30
View File
@@ -1235,41 +1235,35 @@ struct ContentView: View {
LocationNotesView(geohash: gh) LocationNotesView(geohash: gh)
.environmentObject(viewModel) .environmentObject(viewModel)
} else { } else {
ZStack { VStack(spacing: 12) {
VStack(spacing: 0) { HStack {
HStack { Text("notes")
VStack(alignment: .leading, spacing: 2) { .font(.system(size: 16, weight: .bold, design: .monospaced))
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))
Spacer() 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 .frame(height: 44)
ProgressView() .padding(.horizontal, 12)
.progressViewStyle(.circular) .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) .background(backgroundColor)
.foregroundColor(textColor) .foregroundColor(textColor)
.onAppear {
LocationChannelManager.shared.enableLocationChannels()
LocationChannelManager.shared.refreshChannels()
}
.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 block = channels.first(where: { $0.level == .block }) {
notesGeohash = block.geohash notesGeohash = block.geohash
+3
View File
@@ -72,6 +72,9 @@ struct LocationNotesView: View {
Text(note.displayName) Text(note.displayName)
.font(.system(size: 12, weight: .semibold, design: .monospaced)) .font(.system(size: 12, weight: .semibold, design: .monospaced))
.foregroundColor(secondaryTextColor) .foregroundColor(secondaryTextColor)
Text(note.createdAt, style: .time)
.font(.system(size: 11, design: .monospaced))
.foregroundColor(secondaryTextColor.opacity(0.8))
} }
Text(note.content) Text(note.content)
.font(.system(size: 14, design: .monospaced)) .font(.system(size: 14, design: .monospaced))