feat: Add live countdown timers for cooldowns and bump version to 1.3

Replace static cooldown display with real-time countdown timers that update every second using TimelineView.
This commit is contained in:
Paweł Orzech 2026-01-18 03:30:43 +00:00
parent 3c53b046f8
commit 33857f8cfe
No known key found for this signature in database
3 changed files with 54 additions and 10 deletions

View file

@ -17,8 +17,8 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.2.5</string>
<string>1.3</string>
<key>CFBundleVersion</key>
<string>1.2.5</string>
<string>1.3</string>
</dict>
</plist>

View file

@ -201,12 +201,18 @@ struct StatusView: View {
.foregroundColor(.secondary)
HStack(spacing: 16) {
if let fetchTime = appState.lastUpdated {
LiveCooldownItem(label: "Drug", originalSeconds: cooldowns.drug, fetchTime: fetchTime, icon: "pills.fill")
LiveCooldownItem(label: "Medical", originalSeconds: cooldowns.medical, fetchTime: fetchTime, icon: "cross.case.fill")
LiveCooldownItem(label: "Booster", originalSeconds: cooldowns.booster, fetchTime: fetchTime, icon: "arrow.up.circle.fill")
} else {
CooldownItem(label: "Drug", seconds: cooldowns.drug, icon: "pills.fill")
CooldownItem(label: "Medical", seconds: cooldowns.medical, icon: "cross.case.fill")
CooldownItem(label: "Booster", seconds: cooldowns.booster, icon: "arrow.up.circle.fill")
}
}
}
}
// MARK: - Quick Links
private var quickLinksSection: some View {
@ -286,3 +292,41 @@ struct CooldownItem: View {
return String(format: "%d:%02d", minutes, secs)
}
}
// MARK: - Live Cooldown Item
struct LiveCooldownItem: View {
let label: String
let originalSeconds: Int
let fetchTime: Date
let icon: String
var body: some View {
TimelineView(.periodic(from: fetchTime, by: 1.0)) { context in
let elapsed = Int(context.date.timeIntervalSince(fetchTime))
let remaining = max(0, originalSeconds - elapsed)
VStack(spacing: 2) {
Image(systemName: icon)
.font(.caption)
.foregroundColor(remaining > 0 ? .orange : .green)
Text(formattedTime(remaining))
.font(.caption2.monospacedDigit())
.foregroundColor(remaining > 0 ? .primary : .green)
.fontWeight(remaining <= 0 ? .bold : .regular)
}
.frame(maxWidth: .infinity)
}
}
private func formattedTime(_ seconds: Int) -> String {
if seconds <= 0 { return "Ready" }
let hours = seconds / 3600
let minutes = (seconds % 3600) / 60
let secs = seconds % 60
if hours > 0 {
return String(format: "%d:%02d:%02d", hours, minutes, secs)
}
return String(format: "%d:%02d", minutes, secs)
}
}