3 Commits

Author SHA1 Message Date
d37170fc5d Disable stop timer button when no project is selected 2026-02-20 18:19:58 +01:00
fc3b7d7c2c adds group 2026-02-20 17:45:32 +01:00
32cb200408 Fix iOS timer widget not displaying active timers
The widget was failing to decode the cached timer data because WidgetTimer
struct didn't match the OngoingTimer JSON structure saved by the app.

Changes:
- Added missing fields (project, createdAt, updatedAt) to WidgetTimer
- Added WidgetProjectReference struct for nested project data
- Fixed project name to use project.name instead of projectId
- Added project color support
- Increased refresh interval from 15 min to 1 min for live updates
2026-02-20 15:57:16 +01:00
4 changed files with 20 additions and 5 deletions

View File

@@ -116,6 +116,7 @@ struct TimerView: View {
.buttonStyle(.borderedProminent)
.tint(.red)
.controlSize(.large)
.disabled(viewModel.selectedProject == nil && viewModel.activeTimer?.project == nil)
}
}
.padding(.horizontal, 24)

View File

@@ -3,6 +3,8 @@
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array/>
<array>
<string>group.simonfranken.timetracker</string>
</array>
</dict>
</plist>

View File

@@ -3,6 +3,8 @@
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array/>
<array>
<string>group.simonfranken.timetracker</string>
</array>
</dict>
</plist>

View File

@@ -12,6 +12,9 @@ struct WidgetTimer: Codable {
let id: String
let startTime: String
let projectId: String?
let project: WidgetProjectReference?
let createdAt: String
let updatedAt: String
var elapsedTime: TimeInterval {
guard let start = ISO8601DateFormatter().date(from: startTime) else {
@@ -21,6 +24,12 @@ struct WidgetTimer: Codable {
}
}
struct WidgetProjectReference: Codable {
let id: String
let name: String
let color: String?
}
struct Provider: TimelineProvider {
private let appGroupIdentifier = "group.com.timetracker.app"
@@ -41,7 +50,8 @@ struct Provider: TimelineProvider {
func getTimeline(in context: Context, completion: @escaping (Timeline<TimerEntry>) -> Void) {
let entry = loadTimerEntry()
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: Date())!
// Update every minute to show live timer countdown
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 1, to: Date())!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
@@ -63,8 +73,8 @@ struct Provider: TimelineProvider {
return TimerEntry(
date: Date(),
timer: timer,
projectName: timer.projectId,
projectColor: nil
projectName: timer.project?.name ?? timer.projectId,
projectColor: timer.project?.color
)
} catch {
return TimerEntry(