mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 19:45:22 +00:00
Location notes: restore timestamps in notes list; remove loading state from manager and sheet (no spinner/animation)
This commit is contained in:
@@ -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 1–3 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user