You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
LeCountdown/LeCountdown/Views/LiveTimerListView.swift

208 lines
6.3 KiB

//
// LiveTimerView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 03/02/2023.
//
import SwiftUI
class LiveStopwatchModel: ObservableObject {
@Published var endDate: Date? = nil
func stop(_ stopwatch: Stopwatch) {
let now = Date()
self.endDate = now
Conductor.maestro.stopStopwatch(stopwatch)
}
}
struct LiveStopwatchView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var conductor: Conductor
@StateObject var model: LiveStopwatchModel = LiveStopwatchModel()
@State var stopwatch: Stopwatch
var date: Date
var body: some View {
let running = (self.model.endDate == nil)
HStack {
Text(stopwatch.displayName.uppercased()).padding()
Spacer()
if running {
TimelineView(.periodic(from: self.date, by: 0.01)) { context in
Text(self._formattedDuration(date: context.date))
.font(.title2)
.padding(.trailing)
.minimumScaleFactor(0.1)
}
} else {
let duration = self.model.endDate?.timeIntervalSince(self.date) ?? 0.0
Text(duration.hourMinuteSecondHS)
.font(.title2)
.padding(.trailing)
.minimumScaleFactor(0.1)
}
if running {
Button {
self.model.stop(stopwatch)
} label: {
Image(systemName: "stop.circle.fill")
.font(.title)
.foregroundColor(.white)
.cornerRadius(8.0)
.frame(minWidth: 0.0, maxWidth: 60.0, minHeight: 0.0, maxHeight: .infinity)
}.background(.red)
} else {
GreenCheckmarkView()
}
}.onTapGesture {
withAnimation {
self._dismiss()
}
}
.frame(height: 55.0)
.foregroundColor(.white)
.monospaced()
.background(Color(white: 0.2))
.cornerRadius(16.0)
}
fileprivate func _dismiss() {
conductor.removeLiveTimer(id: self.stopwatch.stringId)
}
fileprivate func _formattedDuration(date: Date) -> String {
let duration = date.timeIntervalSince(self.date)
return duration.hourMinuteSecondHS
}
}
struct LiveCountdownView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var conductor: Conductor
@State var countdown: Countdown
var date: Date
var body: some View {
HStack {
Text(self.countdown.displayName.uppercased()).padding()
Spacer()
TimelineView(.periodic(from: self.date, by: 0.01)) { context in
if self.date > context.date {
HStack {
Text(self._formattedDuration(date: context.date))
.font(.title2)
.minimumScaleFactor(0.1)
.padding(.trailing)
Button {
self._cancelCountdown()
} label: {
Image(systemName: "xmark.circle.fill")
.font(.title)
.foregroundColor(.white)
.cornerRadius(8.0)
.frame(minWidth: 0.0, maxWidth: 60.0, minHeight: 0.0, maxHeight: .infinity)
}.background(.red)
}
} else {
GreenCheckmarkView()
}
}
}
.contentShape(Rectangle()) // make the onTap react everywhere
.onTapGesture {
withAnimation {
self._dismiss()
}
}
.frame(height: 55.0)
.foregroundColor(.white)
.monospaced()
.background(Color(white: 0.2))
.cornerRadius(16.0)
}
fileprivate func _dismiss() {
conductor.cancelCountdown(id: self.countdown.stringId)
conductor.removeLiveTimer(id: self.countdown.stringId)
}
fileprivate func _formattedDuration(date: Date) -> String {
let duration = self.date.timeIntervalSince(date)
return duration.minuteSecond
}
fileprivate func _cancelCountdown() {
Conductor.maestro.cancelCountdown(id: self.countdown.stringId)
}
}
struct LiveTimerListView: View {
@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var conductor: Conductor
var body: some View {
LazyVStack {
ForEach(conductor.liveTimers) { liveTimer in
if let timer: AbstractTimer = liveTimer.timer(context: self.viewContext) {
switch timer {
case let cd as Countdown:
LiveCountdownView(countdown: cd, date: liveTimer.date)
case let sw as Stopwatch:
LiveStopwatchView(stopwatch: sw, date: liveTimer.date)
default:
Text("unmanaged timer: \(timer)")
}
}
}
}.padding(8.0)
}
}
struct LiveTimerView_Previews: PreviewProvider {
static var previews: some View {
// LiveTimerListView().environmentObject(Conductor.maestro)
Group {
VStack(spacing: 20.0) {
LiveCountdownView(countdown: Countdown.fake(context: PersistenceController.preview.container.viewContext), date: Date().addingTimeInterval(3600.0))
.environmentObject(Conductor.maestro)
LiveStopwatchView(stopwatch: Stopwatch.fake(context: PersistenceController.preview.container.viewContext), date: Date())
.environmentObject(Conductor.maestro)
}
}.padding()
}
}