Files
lemana-vpn/app/Sources/LemanaVPN/SessionTiming.swift
T

72 lines
2.5 KiB
Swift

import Foundation
enum VPNIndicatorStyle: String, CaseIterable, Identifiable {
case shield, ring, clock, time
var id: String { rawValue }
var title: String {
switch self {
case .shield: return "Щит"
case .ring: return "Кольцо"
case .clock: return "Часы"
case .time: return "Время"
}
}
}
struct SessionTiming {
enum Urgency { case normal, warning, critical, unknown }
private(set) var startedAt: Date?
private(set) var expiresAt: Date?
private var duration: TimeInterval? {
guard let start = startedAt, let end = expiresAt, end > start else { return nil }
return end.timeIntervalSince(start)
}
mutating func observe(startedAt: String?, expires: String?, now: Date = Date()) {
let formatter = ISO8601DateFormatter()
if let start = startedAt.flatMap({ formatter.date(from: $0) }) {
// The CLI timestamp is authoritative, including after a legacy fallback.
self.startedAt = start
} else if self.startedAt == nil {
self.startedAt = now
}
if let expires = expires {
expiresAt = formatter.date(from: expires)
}
}
func remaining(at now: Date) -> TimeInterval? {
guard duration != nil, let end = expiresAt else { return nil }
return max(0, end.timeIntervalSince(now))
}
func progress(at now: Date) -> Double? {
guard let duration = duration, let start = startedAt else { return nil }
return min(1, max(0, now.timeIntervalSince(start) / duration))
}
func urgency(at now: Date) -> Urgency {
guard let remaining = remaining(at: now) else { return .unknown }
if remaining <= 15 * 60 { return .critical }
if remaining <= 60 * 60 { return .warning }
return .normal
}
func compactRemaining(at now: Date) -> String? {
guard let seconds = remaining(at: now) else { return nil }
let minutes = Int(ceil(seconds / 60))
return String(format: "%d:%02d", minutes / 60, minutes % 60)
}
func remainingLabel(at now: Date) -> String {
guard let seconds = remaining(at: now) else { return "Срок сессии неизвестен" }
if seconds <= 0 { return "Время сессии истекло" }
let minutes = Int(ceil(seconds / 60))
if minutes < 60 { return "Осталось \(minutes) мин" }
return "Осталось \(minutes / 60) ч \(minutes % 60) мин"
}
}