Compare commits
2 Commits
da0cd302bf
...
ed8a160a49
| Author | SHA1 | Date | |
|---|---|---|---|
| ed8a160a49 | |||
| f42de3353c |
@@ -44,6 +44,8 @@ struct TimerView: View {
|
|||||||
.font(.system(size: 64, weight: .light, design: .monospaced))
|
.font(.system(size: 64, weight: .light, design: .monospaced))
|
||||||
.foregroundStyle(viewModel.activeTimer != nil ? .primary : .secondary)
|
.foregroundStyle(viewModel.activeTimer != nil ? .primary : .secondary)
|
||||||
|
|
||||||
|
TimerUnitLabels(elapsed: viewModel.elapsedTime)
|
||||||
|
|
||||||
if let project = viewModel.selectedProject {
|
if let project = viewModel.selectedProject {
|
||||||
ProjectColorBadge(
|
ProjectColorBadge(
|
||||||
color: project.color,
|
color: project.color,
|
||||||
@@ -123,6 +125,32 @@ struct TimerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Displays "h min sec" (or "min sec") column labels aligned under the
|
||||||
|
/// monospaced timer digits.
|
||||||
|
private struct TimerUnitLabels: View {
|
||||||
|
let elapsed: TimeInterval
|
||||||
|
|
||||||
|
private var showHours: Bool { Int(elapsed) >= 3600 }
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
if showHours {
|
||||||
|
Text("h")
|
||||||
|
.frame(width: 64)
|
||||||
|
Text("min")
|
||||||
|
.frame(width: 64)
|
||||||
|
} else {
|
||||||
|
Text("min")
|
||||||
|
.frame(width: 64)
|
||||||
|
}
|
||||||
|
Text("sec")
|
||||||
|
.frame(width: 64)
|
||||||
|
}
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ProjectPickerSheet: View {
|
struct ProjectPickerSheet: View {
|
||||||
let projects: [Project]
|
let projects: [Project]
|
||||||
let selectedProject: Project?
|
let selectedProject: Project?
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>API_BASE_URL</key>
|
<key>API_BASE_URL</key>
|
||||||
<string>http://localhost:3001</string>
|
<string>https://timetracker.simon-franken.de/api</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ struct OngoingTimer: Codable, Identifiable, Equatable {
|
|||||||
let updatedAt: String
|
let updatedAt: String
|
||||||
|
|
||||||
var elapsedTime: TimeInterval {
|
var elapsedTime: TimeInterval {
|
||||||
guard let start = ISO8601DateFormatter().date(from: startTime) else {
|
guard let start = Date.fromISO8601(startTime) else { return 0 }
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return Date().timeIntervalSince(start)
|
return Date().timeIntervalSince(start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,8 @@ struct TimeEntry: Codable, Identifiable, Equatable {
|
|||||||
let updatedAt: String
|
let updatedAt: String
|
||||||
|
|
||||||
var duration: TimeInterval {
|
var duration: TimeInterval {
|
||||||
guard let start = ISO8601DateFormatter().date(from: startTime),
|
guard let start = Date.fromISO8601(startTime),
|
||||||
let end = ISO8601DateFormatter().date(from: endTime) else {
|
let end = Date.fromISO8601(endTime) else { return 0 }
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return end.timeIntervalSince(start)
|
return end.timeIntervalSince(start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,14 @@ extension Date {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func fromISO8601(_ string: String) -> Date? {
|
static func fromISO8601(_ string: String) -> Date? {
|
||||||
let formatter = ISO8601DateFormatter()
|
// Try with fractional seconds first (e.g. "2026-02-20T09:00:00.000Z" from
|
||||||
formatter.formatOptions = [.withInternetDateTime]
|
// Prisma/Node.js JSON serialisation), then fall back to whole seconds.
|
||||||
return formatter.date(from: string)
|
let withFractional = ISO8601DateFormatter()
|
||||||
|
withFractional.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
||||||
|
if let date = withFractional.date(from: string) { return date }
|
||||||
|
|
||||||
|
let wholeSec = ISO8601DateFormatter()
|
||||||
|
wholeSec.formatOptions = [.withInternetDateTime]
|
||||||
|
return wholeSec.date(from: string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array/>
|
||||||
<string>group.com.timetracker.app</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -2,28 +2,28 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Timer</string>
|
<string>Timer</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>$(PRODUCT_NAME)</string>
|
<string>$(PRODUCT_NAME)</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionPointIdentifier</key>
|
<key>NSExtensionPointIdentifier</key>
|
||||||
<string>com.apple.widgetkit-extension</string>
|
<string>com.apple.widgetkit-extension</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.application-groups</key>
|
<key>com.apple.security.application-groups</key>
|
||||||
<array>
|
<array/>
|
||||||
<string>group.com.timetracker.app</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
Reference in New Issue
Block a user