Files
luma/Sources/LumaCore/ConnectionActivity.swift
Ole André Vadla Ravnås 8d8ca30a76 core: Track how connecting to a device is going
A device connects on the first call that reaches it, and on the
Barebone backend that can take a while. Follow every discovered
device's connecting and connected events into a ConnectionActivityCenter
keyed by device id, holding the current stage, its fraction, and how
long the stage is expected to take. StageDurationMemory learns the
duration of each stage from what it observes, so a frontend can pace a
progress bar over the expected time rather than jumping between stages.
2026-09-16 13:00:08 +02:00

59 lines
1.7 KiB
Swift

import Foundation
import Observation
public struct ConnectionStage: Sendable, Equatable {
public let status: String
public let fraction: Double
public let expectedDuration: TimeInterval
public init(status: String, fraction: Double, expectedDuration: TimeInterval) {
self.status = status
self.fraction = fraction
self.expectedDuration = expectedDuration
}
}
@MainActor
@Observable
public final class ConnectionActivityCenter {
public private(set) var stages: [String: ConnectionStage] = [:]
@ObservationIgnored private let durations: StageDurationMemory
@ObservationIgnored private var arrivals: [String: StageArrival] = [:]
private struct StageArrival {
let key: String
let at: Date
}
public init(durations: StageDurationMemory) {
self.durations = durations
}
public func stage(for deviceID: String) -> ConnectionStage? {
stages[deviceID]
}
public func report(deviceID: String, deviceKind: String, status: String, fraction: Double) {
recordElapsed(for: deviceID)
let key = "\(deviceKind)/\(status)"
stages[deviceID] = ConnectionStage(
status: status,
fraction: fraction,
expectedDuration: durations.expectedDuration(forKey: key)
)
arrivals[deviceID] = StageArrival(key: key, at: Date())
}
public func complete(deviceID: String) {
recordElapsed(for: deviceID)
stages[deviceID] = nil
}
private func recordElapsed(for deviceID: String) {
guard let arrival = arrivals.removeValue(forKey: deviceID) else { return }
durations.record(elapsed: Date().timeIntervalSince(arrival.at), forKey: arrival.key)
}
}