Fixes stopwatches

main
Laurent 3 years ago
parent 5322f59f43
commit 4c4607ecc8
  1. 6
      LeCountdown.xcodeproj/project.pbxproj
  2. 36
      LeCountdown/Conductor.swift
  3. 38
      LeCountdown/Model/LiveStopWatch.swift
  4. 2
      LeCountdown/TimerRouter.swift
  5. 39
      LeCountdown/Views/LiveTimerListView.swift

@ -108,6 +108,8 @@
C498E5A3298D720600E90DE0 /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E5A2298D720600E90DE0 /* TestView.swift */; };
C498E5A5299152B400E90DE0 /* GreenCheckmarkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E5A4299152B400E90DE0 /* GreenCheckmarkView.swift */; };
C498E5A6299152C600E90DE0 /* GreenCheckmarkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E5A4299152B400E90DE0 /* GreenCheckmarkView.swift */; };
C49C346929DECA4400AAC6FC /* LiveStopWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C346829DECA4400AAC6FC /* LiveStopWatch.swift */; };
C49C346A29DECC7100AAC6FC /* LiveStopWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49C346829DECA4400AAC6FC /* LiveStopWatch.swift */; };
C4A16D8C29C4A5BA00143D5E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4A16D8B29C4A5BA00143D5E /* GoogleService-Info.plist */; };
C4A16D8D29C4A5BA00143D5E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4A16D8B29C4A5BA00143D5E /* GoogleService-Info.plist */; };
C4A16D8E29C4A5BA00143D5E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C4A16D8B29C4A5BA00143D5E /* GoogleService-Info.plist */; };
@ -395,6 +397,7 @@
C498E5A0298D543900E90DE0 /* LiveTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTimer.swift; sourceTree = "<group>"; };
C498E5A2298D720600E90DE0 /* TestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestView.swift; sourceTree = "<group>"; };
C498E5A4299152B400E90DE0 /* GreenCheckmarkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GreenCheckmarkView.swift; sourceTree = "<group>"; };
C49C346829DECA4400AAC6FC /* LiveStopWatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveStopWatch.swift; sourceTree = "<group>"; };
C4A16D8B29C4A5BA00143D5E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
C4A16D9429C4B06400143D5E /* StatePlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatePlayer.swift; sourceTree = "<group>"; };
C4A16D9A29D0A7D300143D5E /* MRKRSTPHR_synth_one_shot_bleep_G.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = MRKRSTPHR_synth_one_shot_bleep_G.wav; sourceTree = "<group>"; };
@ -701,6 +704,7 @@
C4F8B188298AC248005C86A5 /* Generation */,
C4060DCA297AE73D003FAB80 /* LeCountdown.xcdatamodeld */,
C438C80C2982847300BF3EF9 /* CoreDataRequests.swift */,
C49C346829DECA4400AAC6FC /* LiveStopWatch.swift */,
C498E5A0298D543900E90DE0 /* LiveTimer.swift */,
C438C806298195E600BF3EF9 /* Model+Extensions.swift */,
C4BA2B39299F838000CB4FBA /* Model+SharedExtensions.swift */,
@ -1219,6 +1223,7 @@
C473C33C29ACEC4F0056B38A /* Tip.swift in Sources */,
C498E5A3298D720600E90DE0 /* TestView.swift in Sources */,
C4BA2B06299A8F8D00CB4FBA /* PresetsView.swift in Sources */,
C49C346929DECA4400AAC6FC /* LiveStopWatch.swift in Sources */,
C473C33929ACDBD70056B38A /* TipView.swift in Sources */,
C445FA8F2987B83B0054D761 /* SoundPlayer.swift in Sources */,
C4F8B1A7298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */,
@ -1343,6 +1348,7 @@
buildActionMask = 2147483647;
files = (
C473C2FD29A8DC690056B38A /* CoreDataRequests.swift in Sources */,
C49C346A29DECC7100AAC6FC /* LiveStopWatch.swift in Sources */,
C473C2F329A8DA6F0056B38A /* LiveTimer.swift in Sources */,
C4F8B1C6298ACC1F005C86A5 /* SoundPlayer.swift in Sources */,
C4F8B1A2298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */,

@ -31,7 +31,7 @@ class Conductor: ObservableObject {
fileprivate var _delayedSoundPlayers: [TimerID : DelaySoundPlayer] = [:]
@UserDefault(PreferenceKey.countdowns.rawValue, defaultValue: [:]) static var savedCountdowns: [String : DateInterval]
@UserDefault(PreferenceKey.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : Date]
@UserDefault(PreferenceKey.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : LiveStopWatch]
@Published private (set) var liveTimers: [LiveTimer] = []
@ -52,7 +52,7 @@ class Conductor: ObservableObject {
}
}
@Published var currentStopwatches: [String : Date] = [:] {
@Published var currentStopwatches: [String : LiveStopWatch] = [:] {
didSet {
Conductor.savedStopwatches = currentStopwatches
withAnimation {
@ -92,8 +92,8 @@ class Conductor: ObservableObject {
}
}
let liveStopwatches = self.currentStopwatches.map {
return LiveTimer(id: $0, date: $1)
let liveStopwatches: [LiveTimer] = self.currentStopwatches.map {
return LiveTimer(id: $0, date: $1.start)
}
for liveStopwatch in liveStopwatches {
if let index = self.liveTimers.firstIndex(where: { $0.id == liveStopwatch.id }) {
@ -180,29 +180,35 @@ class Conductor: ObservableObject {
func startStopwatch(_ stopwatch: Stopwatch) {
DispatchQueue.main.async {
let now = Date()
Conductor.maestro.currentStopwatches[stopwatch.stringId] = now
let lsw = LiveStopWatch(start: Date())
Conductor.maestro.currentStopwatches[stopwatch.stringId] = lsw
if Preferences.playConfirmationSound {
self._playSound(Const.confirmationSound.rawValue)
}
self._launchLiveActivity(stopwatch: stopwatch, start: now)
self._launchLiveActivity(stopwatch: stopwatch, start: lsw.start)
// self._createTimerIntent(stopwatch)
}
}
func stopStopwatch(_ stopwatch: Stopwatch, end: Date? = nil) {
if let start = Conductor.maestro.currentStopwatches[stopwatch.stringId] {
Conductor.maestro.currentStopwatches.removeValue(forKey: stopwatch.stringId)
do {
try CoreDataRequests.recordActivity(timer: stopwatch, dateInterval: DateInterval(start: start, end: end ?? Date()))
} catch {
Logger.error(error)
func stopStopwatch(_ stopwatch: Stopwatch) {
if let lsw = Conductor.maestro.currentStopwatches[stopwatch.stringId] {
if lsw.end == nil {
let end = Date()
lsw.end = end
// Conductor.maestro.currentStopwatches.removeValue(forKey: stopwatch.stringId)
do {
try CoreDataRequests.recordActivity(timer: stopwatch, dateInterval: DateInterval(start: lsw.start, end: end))
} catch {
Logger.error(error)
}
self._endLiveActivity(timerId: stopwatch.stringId)
}
self._endLiveActivity(timerId: stopwatch.stringId)
}
}

@ -0,0 +1,38 @@
//
// LiveStopWatch.swift
// LeCountdown
//
// Created by Laurent Morvillier on 06/04/2023.
//
import Foundation
class LiveStopWatch: Codable, Hashable, Equatable {
var start: Date
var end: Date?
init(start: Date, end: Date? = nil) {
self.start = start
self.end = end
}
static func == (lhs: LiveStopWatch, rhs: LiveStopWatch) -> Bool {
if lhs.start == rhs.start {
if let end = lhs.end, end == rhs.end {
return true
} else if lhs.end == nil && rhs.end == nil {
return true
}
}
return false
}
func hash(into hasher: inout Hasher) {
hasher.combine(start)
if let end {
hasher.combine(end)
}
}
}

@ -71,7 +71,7 @@ class TimerRouter {
handler(.success(Void()))
}
fileprivate static func _stopStopwatch(_ stopwatch: Stopwatch, end: Date? = nil) {
fileprivate static func _stopStopwatch(_ stopwatch: Stopwatch) {
Conductor.maestro.stopStopwatch(stopwatch)
}

@ -7,20 +7,6 @@
import SwiftUI
class LiveStopwatchModel: ObservableObject {
@Published var endDate: Date? = nil
func stop(_ stopwatch: Stopwatch) {
let now = Date()
self.endDate = now
Conductor.maestro.stopStopwatch(stopwatch)
}
}
fileprivate let liveViewSize: CGFloat = 70.0
fileprivate let timerFontSize: CGFloat = 32.0
fileprivate let actionButtonFontSize: CGFloat = 36.0
@ -42,25 +28,27 @@ struct LiveStopwatchView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var conductor: Conductor
@StateObject var model: LiveStopwatchModel = LiveStopwatchModel()
@State var stopwatch: Stopwatch
@State var stopped: Bool = false
var date: Date
var endDate: Date? {
return self.conductor.currentStopwatches[stopwatch.stringId]?.end
}
var body: some View {
let running = (self.model.endDate == nil)
HStack {
VStack(alignment: .leading) {
if running {
if !self.stopped {
TimelineView(.periodic(from: self.date, by: 0.01)) { context in
TimeView(text: self._formattedDuration(date: context.date))
}
} else {
let duration = self.model.endDate?.timeIntervalSince(self.date) ?? 0.0
let duration = self.endDate?.timeIntervalSince(self.date) ?? 0.0
TimeView(text: duration.hourMinuteSecondHS)
}
Text(stopwatch.displayName.uppercased())
@ -69,7 +57,7 @@ struct LiveStopwatchView: View {
Spacer()
Group {
if !running {
if self.stopped {
GreenCheckmarkView()
} else {
Image(systemName: "stop.circle.fill")
@ -88,8 +76,8 @@ struct LiveStopwatchView: View {
fileprivate func _actionHandler() {
withAnimation {
if self.model.endDate == nil {
self.model.stop(stopwatch)
if self.endDate == nil {
self._stop()
} else {
self._dismiss()
}
@ -104,6 +92,11 @@ struct LiveStopwatchView: View {
let duration = date.timeIntervalSince(self.date)
return duration.hourMinuteSecondHS
}
fileprivate func _stop() {
self.stopped = true
Conductor.maestro.stopStopwatch(self.stopwatch)
}
}
struct LiveCountdownView: View {

Loading…
Cancel
Save