Pending-Queue-UI: - AccountView.pendingQueueCard listet alle wartenden Submissions mit Text-Preview (120c), Author, createdAt, retryCount, lastError - "Jetzt versuchen"-Button triggert tryFlush(api:) - Trash-Icon pro Row löscht einzeln aus der Queue - Pull-to-Refresh aktualisiert beide Listen - Card-View nur sichtbar wenn Queue-Tiefe > 0 ReachabilityWatcher: - NWPathMonitor erkennt Reconnect-Flanke (offline → online) - Bei Reconnect: SubmissionQueue.tryFlush auf @MainActor - Filtert reine Wifi↔Mobil-Switches raus (nur "wieder erreichbar" zählt) - Lebt im App-Root, startet nach Launch via .task Files: - Sources/Core/Submit/ReachabilityWatcher.swift (neu) - Sources/App/ZitareNativeApp.swift: reachability.start in .task - Sources/Features/Account/AccountView.swift: pendingQueueCard iOS + macOS BUILD SUCCEEDED. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
40 lines
1.4 KiB
Swift
40 lines
1.4 KiB
Swift
import Foundation
|
|
import Network
|
|
import OSLog
|
|
|
|
/// Wrapper um `NWPathMonitor`. Feuert das übergebene Closure bei jedem
|
|
/// Wechsel von "kein Pfad" → "Pfad da" (= Wifi/Mobil wieder verfügbar
|
|
/// nach Offline). Ein wechselnder Pfad (Wifi → Mobil) feuert nicht
|
|
/// erneut — wir wollen nur die Reconnect-Flanke.
|
|
///
|
|
/// Lebenszyklus liegt beim App-Root. Stop-Methode existiert, aber
|
|
/// reale App startet Watcher einmal beim Launch und lässt ihn bis
|
|
/// zum Prozess-Ende laufen.
|
|
final class ReachabilityWatcher: @unchecked Sendable {
|
|
private let monitor = NWPathMonitor()
|
|
private let queue = DispatchQueue(label: "ev.mana.zitare.reachability")
|
|
private let log = Logger(subsystem: "ev.mana.zitare", category: "reachability")
|
|
private var wasReachable: Bool? = nil
|
|
private var onReconnect: @Sendable () -> Void = {}
|
|
|
|
func start(onReconnect: @escaping @Sendable () -> Void) {
|
|
self.onReconnect = onReconnect
|
|
monitor.pathUpdateHandler = { [weak self] path in
|
|
guard let self else { return }
|
|
let reachable = path.status == .satisfied
|
|
let prev = self.wasReachable
|
|
self.wasReachable = reachable
|
|
if prev == false, reachable {
|
|
self.log.info("Reachable again — triggering reconnect-hook")
|
|
self.onReconnect()
|
|
} else if prev == nil {
|
|
self.log.info("Initial reachability: \(reachable ? "yes" : "no", privacy: .public)")
|
|
}
|
|
}
|
|
monitor.start(queue: queue)
|
|
}
|
|
|
|
func stop() {
|
|
monitor.cancel()
|
|
}
|
|
}
|