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