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/PresetsView.swift

293 lines
9.1 KiB

//
// PresetsView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 13/02/2023.
//
import SwiftUI
enum PresetSection: Int, Identifiable, CaseIterable {
var id: Int { return self.rawValue }
// case workout
case chill
case cooking
case tea
case other
var presets: [Preset] {
switch self {
case .cooking: return [.softBoiled, .mediumBoiledEggs, .hardBoiledEggs]
case .tea: return [.greenTea, .blackTea]
// case .workout: return [.runningSplits]
case .chill: return [.nap, .meditation]
case .other: return [.toothbrushing]
}
}
var localizedName: String {
switch self {
case .cooking: return NSLocalizedString("Cooking", comment: "")
// case .workout: return NSLocalizedString("Workout", comment: "")
case .chill: return NSLocalizedString("Chill", comment: "")
case .other: return NSLocalizedString("Other", comment: "")
case .tea: return NSLocalizedString("Tea", comment: "")
}
}
}
struct CountdownIntervalGroup {
var repeatCount: Int
var intervals: [CountdownInterval]
}
struct CountdownInterval {
var duration: TimeInterval
var sound: Sound?
}
enum Preset: Int, Identifiable, CaseIterable {
var id: Int { return self.rawValue }
case softBoiled
case mediumBoiledEggs
case hardBoiledEggs
case meditation
case nap
case runningSplits
case toothbrushing
case blackTea
case greenTea
var localizedName: String {
switch self {
case .hardBoiledEggs: return NSLocalizedString("Hard boiled eggs", comment: "")
case .softBoiled: return NSLocalizedString("Soft boiled eggs", comment: "")
case .mediumBoiledEggs: return NSLocalizedString("Medium boiled eggs", comment: "")
case .meditation: return NSLocalizedString("Meditation", comment: "")
case .nap: return NSLocalizedString("Nap", comment: "")
case .runningSplits: return NSLocalizedString("Running splits", comment: "")
case .toothbrushing: return NSLocalizedString("Tooth brushing", comment: "")
case .blackTea: return NSLocalizedString("Black tea", comment: "")
case .greenTea: return NSLocalizedString("Green tea", comment: "")
}
}
var intervalGroup: CountdownIntervalGroup {
switch self {
case .runningSplits:
let runInterval = CountdownInterval(duration: 30.0, sound: Sound.sbArpeggio_Loop_River)
let breakInterval = CountdownInterval(duration: 30.0, sound: Sound.sbLoop_ToneSD_Boavista)
return CountdownIntervalGroup(repeatCount: 8, intervals: [runInterval, breakInterval])
default:
return CountdownIntervalGroup(repeatCount: 0, intervals: [CountdownInterval(duration: self.duration)])
}
}
var duration: TimeInterval {
switch self {
case .softBoiled: return 3 * 60
case .mediumBoiledEggs: return 6 * 60
case .hardBoiledEggs: return 10 * 60
case .meditation: return 15 * 60
case .nap: return 20 * 60
case .runningSplits: return 0.0
case .toothbrushing: return 2 * 60.0
case .greenTea: return 3 * 60.0
case .blackTea: return 4 * 60.0
}
}
var sound: Set<Sound> {
switch self {
case .softBoiled: return []
case .mediumBoiledEggs: return []
case .hardBoiledEggs: return []
case .meditation: return []
case .nap: return []
case .runningSplits: return []
case .toothbrushing: return []
case .blackTea: return []
case .greenTea: return []
}
}
var formattedDuration: String {
let group = self.intervalGroup
let count = group.repeatCount.formatted()
let durations = group.intervals.map { $0.duration.minuteSecond }
let formattedIntervals = durations.joined(separator: "/")
if group.repeatCount > 1 {
return "\(count) * [\(formattedIntervals)]"
} else if durations.count > 1 {
return "[\(formattedIntervals)]"
} else {
return formattedIntervals
}
}
var soundTitle: String {
return "Great sound"
}
}
class PresetModel : ObservableObject {
@Published var selectedPreset: Preset = Preset.hardBoiledEggs
}
struct PresetsView: View {
@Environment(\.managedObjectContext) private var viewContext
#if os(iOS)
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
#endif
@StateObject var model: PresetModel = PresetModel()
@State var isPresented: Bool = false
@State var isShowingNewCountdown = false
@State var isShowingNewStopwatch = false
var tabSelection: Binding<Int>
fileprivate func _columnCount() -> Int {
#if os(iOS)
if horizontalSizeClass == .compact {
return 2
} else {
return 3
}
#else
return 3
#endif
}
fileprivate func _columns() -> [GridItem] {
return (0..<self._columnCount()).map { _ in GridItem(spacing: 10.0) }
}
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 0.0) {
Button {
self.isShowingNewCountdown = true
} label: {
Text(".create countdown")
.font(.system(.title, weight: .heavy))
Spacer()
}.frame(height: 40.0)
Button {
self.isShowingNewStopwatch = true
} label: {
Text(".create stopwatch")
.font(.system(.title, weight: .heavy))
Spacer()
}.frame(height: 40.0)
Text("You can ask Siri to create and launch countdowns and stopwatches")
.font(.callout)
.padding(.vertical)
Text("Presets")
.font(.system(.title, weight: .heavy))
Text("You can edit the duration, sound and label before adding")
.font(.callout)
}.padding(.horizontal)
LazyVGrid(
columns: self._columns(),
spacing: 10.0
) {
ForEach(PresetSection.allCases) { section in
Section {
ForEach(section.presets) { preset in
Button {
self.model.selectedPreset = preset
self.isPresented = true
} label: {
TimerItemView(name: preset.localizedName, duration: preset.formattedDuration, sound: preset.soundTitle)
}
}
} header: {
HStack {
Text(section.localizedName.uppercased())
Spacer()
}
}
}
}.padding(.horizontal)
Spacer()
}
.sheet(isPresented: $isShowingNewStopwatch, content: {
NewStopwatchView(isPresented: $isShowingNewStopwatch, tabSelection: self.tabSelection)
.environment(\.managedObjectContext, viewContext)
})
.sheet(isPresented: $isShowingNewCountdown, content: {
NewCountdownView(isPresented: $isShowingNewCountdown, tabSelection: self.tabSelection)
.environment(\.managedObjectContext, viewContext)
})
.sheet(isPresented: $isPresented, content: {
CountdownEditView(isPresented: $isPresented, preset: self.model.selectedPreset, tabSelection: self.tabSelection)
.environment(\.managedObjectContext, viewContext)
})
.navigationTitle("Create")
}
}
struct TimerItemView: View {
var name: String
var duration: String
var sound: String
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(name.uppercased()).multilineTextAlignment(.leading)
Text(duration)
Text(sound.uppercased()).foregroundColor(Color(white: 0.7))
}.padding()
Spacer()
}.background(Color(white: 0.1))
.cornerRadius(16.0)
.monospaced()
.font(Font.system(size: 16.0, weight: .semibold))
.foregroundColor(Color.white)
}
}
struct PresetsView_Previews: PreviewProvider {
static var previews: some View {
PresetsView(tabSelection: .constant(0))
}
}
struct TimerItemView_Previews: PreviewProvider {
static var previews: some View {
TimerItemView(name: "Hard boiled eggs",
duration: "10:00",
sound: "Stephan Bodzin")
.frame(width: UIScreen.main.bounds.width / 2.0)
}
}