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

338 lines
11 KiB

//
// PresetsView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 13/02/2023.
//
import SwiftUI
class PresetModel : ObservableObject {
@Published var selectedPreset: Preset = Preset.nap
}
struct BasePresetsView: View {
var handler: (Preset) -> ()
var body: some View {
Section("Preselections") {
ForEach(PresetSection.allCases) { section in
ForEach(section.presets) { preset in
Button {
self.handler(preset)
} label: {
TimerItemView(name: preset.localizedName,
duration: preset.formattedDuration,
sound: preset.soundTitle)
}
}
}
}
}
fileprivate func _columnCount() -> Int {
return 2
}
fileprivate func _columns() -> [GridItem] {
return (0..<self._columnCount()).map { _ in GridItem(spacing: 10.0) }
}
}
struct PresetsView: View {
@Environment(\.managedObjectContext) private var viewContext
@StateObject var model: PresetModel = PresetModel()
@State var isShowingSubscription: Bool = false
@State var isPresented: Bool = false
@State var isShowingNewCountdown = false
@State var isShowingNewStopwatch = false
var tabSelection: Binding<Int>
fileprivate func _columnCount() -> Int {
return 2
// #if os(iOS)
// if UIDevice.isPhoneIdiom {
// 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: 10.0) {
Button {
self._showNewCountdown()
} label: {
Text(NSLocalizedString("Create countdown", comment: "").uppercased())
.frame(maxWidth: .infinity)
.frame(height: 40.0)
}.monospaced().buttonStyle(.bordered)
// Button {
// self.isShowingNewStopwatch = true
// } label: {
// Text("Create stopwatch".uppercased())
// .frame(maxWidth: .infinity)
// .frame(height: 40.0)
// }.monospaced().buttonStyle(.bordered)
Text("Presets")
.font(.system(.title, weight: .heavy))
Text("You can edit the duration, sound and label before adding")
.foregroundColor(.gray)
.font(.callout)
}.padding(.horizontal)
.padding(.bottom)
LazyVGrid(
columns: self._columns(),
alignment: .leading,
spacing: 10.0
) {
ForEach(PresetSection.allCases) { section in
Section(section.localizedName.uppercased()) {
ForEach(section.presets) { preset in
Button {
self.model.selectedPreset = preset
self.isPresented = true
} label: {
TimerItemView(name: preset.localizedName, duration: preset.formattedDuration, sound: preset.soundTitle)
}
}
}
}
}.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)
})
.sheet(isPresented: $isShowingSubscription, content: {
StoreView(isPresented: self.$isShowingSubscription)
})
.navigationTitle("Create")
}
fileprivate func _showNewCountdown() {
if AppGuard.main.isSubscriber || viewContext.count(entityName: "AbstractTimer") < AppGuard.freeTimersCount {
self.isShowingNewCountdown = true
} else {
self.isShowingSubscription = true
}
}
}
struct TimerItemView: View {
var name: String
var duration: String
var sound: String
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(self.name.uppercased()).foregroundColor(.black)
Text(self.duration)
.foregroundColor(Color.accentColor)
Text(self.sound.uppercased()).foregroundColor(Color(white: 0.7))
}//.padding()
.multilineTextAlignment(.leading)
Spacer()
}//.background(Color(white: 0.1))
//.cornerRadius(16.0)
.monospaced()
.font(Font.system(size: 16.0))
//.foregroundColor(Color.white)
}
}
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 [.pasta, .rice, .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
case pasta
case rice
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: "")
case .pasta: return NSLocalizedString("Pasta", comment: "")
case .rice: return NSLocalizedString("Rice", 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
case .pasta: return 10 * 60.0
case .rice: return 10 * 60.0
}
}
var playlist: Playlist {
switch self {
case .softBoiled, .mediumBoiledEggs, .hardBoiledEggs, .pasta, .rice, .runningSplits, .toothbrushing:
return .stephanBodzin
case .meditation, .blackTea, .greenTea:
return .relax
case .nap:
return .nature
}
}
// var sounds: Set<Sound> {
// return Set(SoundCatalog.main.sounds(for: self.playlist))
// }
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 self.playlist.localizedString
}
}
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)
}
}