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

283 lines
7.9 KiB

//
// StartView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 22/05/2023.
//
import SwiftUI
class Customization: ObservableObject {
var preset: Preset
@Published var added: Bool = false
@Published var duration: Double = 0.0 {
didSet {
self.added = true
}
}
@Published var timerModel: TimerModel = TimerModel()
init(preset: Preset) {
self.preset = preset
self.duration = preset.duration
self.timerModel.soundModel.selectPlaylist(preset.playlist, selected: true)
self.added = false
}
func toggleAdd() {
self.added = !self.added
Logger.log("ADDDED = \(self.added)")
}
func createTimer() -> AbstractTimer {
let context = PersistenceController.shared.container.viewContext
let countdown = Countdown(context: context)
countdown.duration = self.duration
countdown.playableIds = self.timerModel.soundModel.playableIds
return countdown
}
}
class PresetSelectionModel: ObservableObject {
// @Published var addedPresets: Set<Preset> = []
@Published var customizations: [Preset : Customization] = [:]
// @Published var expanded: Set<Preset> = []
// @Published var duration: [TimeInterval] = []
init() {
self.customizations = Preset.allCases.reduce(into: [Preset: Customization]()) { $0[$1] = Customization(preset: $1) }
}
}
struct DurationButtonView: View {
@StateObject var customization: Customization
@State var showDurationSheet: Bool = false
var body: some View {
Button {
self.showDurationSheet = true
} label: {
Image(systemName: "timer")
Text(customization.duration.hourMinuteSecond)
}
.sheet(isPresented: $showDurationSheet) {
TimePickerView(duration: self.$customization.duration)
.presentationDetents([.height(320.0)])
}
}
}
struct SoundButtonView: View {
@StateObject var soundModel: SoundModel
@State var showSoundSheet: Bool = false
var body: some View {
Button {
self.showSoundSheet = true
} label: {
Image(systemName: "music.note")
Text(self.soundModel.soundSelection)
}.sheet(isPresented: $showSoundSheet) {
PlaylistsView(model: self.soundModel,
catalog: .ring)
.presentationDetents([.height(320.0)])
}
}
}
struct PresetSelectionView: View {
@StateObject var model: PresetSelectionModel
var body: some View {
List {
ForEach(PresetSection.allCases) { section in
Section(section.localizedName.uppercased()) {
ForEach(section.presets.indices, id: \.self) { i in
let preset = section.presets[i]
let customization = self.model.customizations[preset]!
HStack {
Button {
self._addOrRemove(preset: preset)
} label: {
HStack {
let added = customization.added
let image = added ? "checkmark.circle.fill" : "circle"
Image(systemName: image)
.padding(.trailing, 8.0)
.font(.title2)
.foregroundColor(Color.accentColor)
Text(preset.localizedName)
Spacer()
VStack(alignment: .trailing) {
DurationButtonView(customization: customization)
SoundButtonView(soundModel: customization.timerModel.soundModel)
}.buttonStyle(.bordered)
}.font(.callout)
}
}
}
}
}
}.listStyle(.inset)
}
fileprivate func _addOrRemove(preset: Preset) {
self.model.customizations[preset]?.toggleAdd()
}
fileprivate func _added(preset: Preset) -> Bool {
return self.model.customizations[preset]?.added ?? false
}
fileprivate func _image(preset: Preset) -> String {
return self._added(preset: preset) ? "checkmark.circle.fill" : "circle"
}
}
struct StartView: View {
@StateObject var model: PresetSelectionModel = PresetSelectionModel()
@State var showAddScreen: Bool = false
var body: some View {
VStack(spacing: 0.5) {
PresetSelectionView(model: self.model).monospaced()
Button {
self.showAddScreen = true
} label: {
HStack {
Image(systemName: "plus.circle").font(.title)
Text("Create your own").font(.title3)
}
.padding()
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color.accentColor)
}.sheet(isPresented: self.$showAddScreen) {
NewCountdownView(isPresented: $showAddScreen)
}
Button {
self._done()
} label: {
Text("Done")
.font(.title2).fontWeight(.semibold)
.padding()
.frame(maxWidth: .infinity)
// .foregroundColor(.white)
.background(.white)
}
}.background(.gray)
}
fileprivate func _done() {
let customizations = self.model.customizations.values.filter { $0.added }
for custo in customizations {
let _ = custo.createTimer()
}
let context = PersistenceController.shared.container.viewContext
do {
try context.save()
} catch {
Logger.error(error)
}
}
}
struct ConfigurationButtonView: View {
var preset: Preset
@Binding var duration: TimeInterval
@State var selectedPreset: Preset? = nil
var body: some View {
Button {
self.selectedPreset = preset
} label: {
Image(systemName: "gearshape.fill")
}
.buttonStyle(.plain)
// .foregroundColor(.gray)
.sheet(item: $selectedPreset) { preset in
ConfigurationView(preset: preset,
model: TimerModel(),
duration: self.$duration)
.presentationDetents([.height(320.0)])
}
}
}
struct ConfigurationView: View {
var preset: Preset
var model: TimerModel
@Binding var duration: TimeInterval
var body: some View {
List {
SoundLinkView(soundModel: self.model.soundModel,
catalog: .ring,
title: NSLocalizedString("Sound", comment: ""))
TimePickerView(duration: self.$duration)
}
.listStyle(.plain)
.onAppear {
self.model.soundModel.setPlayables([preset.playlist])
self.duration = preset.duration
}
}
}
struct StartView_Previews: PreviewProvider {
static var previews: some View {
StartView()
ConfigurationView(preset: Preset.blackTea,
model: TimerModel(),
duration: .constant(60.0))
}
}