parent
afbcd4b5d5
commit
e76429b7cb
@ -0,0 +1,283 @@ |
||||
// |
||||
// 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)) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue