diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index d3bc0dc..260401d 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ C4286EAE2A17753A0070D075 /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286E952A14EC4E0070D075 /* AppError.swift */; }; C4286EB02A1B75AB0070D075 /* BoringContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286EAF2A1B75AB0070D075 /* BoringContext.swift */; }; C4286EB22A1B75C60070D075 /* BoringContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286EAF2A1B75AB0070D075 /* BoringContext.swift */; }; + C4286EB72A1B98420070D075 /* StartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4286EB62A1B98420070D075 /* StartView.swift */; }; C42E96FB29E59E72005B1B8C /* BackgroundBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E96FA29E59E72005B1B8C /* BackgroundBlurView.swift */; }; C42E96FD29E5B06D005B1B8C /* ActivityCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E96FC29E5B06D005B1B8C /* ActivityCalendarView.swift */; }; C42E96FE29E5B5CD005B1B8C /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6429A3C37D00CB4FBA /* Filter.swift */; }; @@ -380,6 +381,7 @@ C4286E9F2A1502FD0070D075 /* Stopwatch+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Stopwatch+CoreDataClass.swift"; sourceTree = ""; }; C4286EA52A150A7E0070D075 /* TimePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePickerView.swift; sourceTree = ""; }; C4286EAF2A1B75AB0070D075 /* BoringContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoringContext.swift; sourceTree = ""; }; + C4286EB62A1B98420070D075 /* StartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartView.swift; sourceTree = ""; }; C42E96FA29E59E72005B1B8C /* BackgroundBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundBlurView.swift; sourceTree = ""; }; C42E96FC29E5B06D005B1B8C /* ActivityCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityCalendarView.swift; sourceTree = ""; }; C42E970129E6B32B005B1B8C /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; @@ -790,6 +792,7 @@ C4BA2B03299A42EF00CB4FBA /* NewDataView.swift */, C4BA2B05299A8F8D00CB4FBA /* PresetsView.swift */, C4E5D68929BB7953008E7465 /* SettingsView.swift */, + C4286EB62A1B98420070D075 /* StartView.swift */, C4E5D68529BB369E008E7465 /* TimersView.swift */, ); path = Views; @@ -1219,6 +1222,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C4286EB72A1B98420070D075 /* StartView.swift in Sources */, C4F8B1D2298BF646005C86A5 /* PermissionAlertView.swift in Sources */, C4BA2B7329A60CF000CB4FBA /* Shortcut.swift in Sources */, C4060DC9297AE73D003FAB80 /* Persistence.swift in Sources */, diff --git a/LeCountdown/LeCountdownApp.swift b/LeCountdown/LeCountdownApp.swift index 04255d4..96c2fb5 100644 --- a/LeCountdown/LeCountdownApp.swift +++ b/LeCountdown/LeCountdownApp.swift @@ -35,6 +35,9 @@ struct LeCountdownApp: App { WindowGroup { ZStack { + +// StartView() + CompactHomeView() .environment(\.managedObjectContext, persistenceController.container.viewContext) } diff --git a/LeCountdown/Views/ContentView.swift b/LeCountdown/Views/ContentView.swift index dfa2223..fe60dc6 100644 --- a/LeCountdown/Views/ContentView.swift +++ b/LeCountdown/Views/ContentView.swift @@ -239,10 +239,10 @@ struct MainToolbarView: ToolbarContent { } } .sheet(isPresented: self.$showStopwatchSheet, content: { - NewStopwatchView(isPresented: $showStopwatchSheet, tabSelection: .constant(0)) + NewStopwatchView(isPresented: $showStopwatchSheet) }) .sheet(isPresented: self.$showTimerSheet, content: { - NewCountdownView(isPresented: $showTimerSheet, tabSelection: .constant(0)) + NewCountdownView(isPresented: $showTimerSheet) }) } diff --git a/LeCountdown/Views/Countdown/CountdownFormView.swift b/LeCountdown/Views/Countdown/CountdownFormView.swift index f6664d8..8100e8a 100644 --- a/LeCountdown/Views/Countdown/CountdownFormView.swift +++ b/LeCountdown/Views/Countdown/CountdownFormView.swift @@ -21,13 +21,7 @@ struct CountdownFormView : View { @EnvironmentObject var model: TimerModel var nameBinding: Binding - -// var secondsBinding: Binding -// var minutesBinding: Binding -// var hoursBinding: Binding - var durationBinding: Binding - var imageBinding: Binding var repeatCountBinding: Binding @@ -45,49 +39,8 @@ struct CountdownFormView : View { } } - - - -// Section { -//// self.focusedField = nil -// -// TextField("Name", text: nameBinding) -// .focused($focusedField, equals: .name) -// .onSubmit { -// self.focusedField = nil -// -//// self.focusNextField($focusedField) -// } -// } header: { -// Text("Name") -// } footer: { -// if !self.nameBinding.wrappedValue.isEmpty { -// Text("Ask Siri: \(Bundle.main.applicationName) \(self.nameBinding.wrappedValue)!") -// } -// } - Section { - TimePickerView(duration: self.durationBinding) - -// TextField("Hours", text: hoursBinding) -// .keyboardType(.numberPad) -// .focused($focusedField, equals: .hours) -// .onSubmit { -// self.focusNextField($focusedField) -// } -// TextField("Minutes", text: minutesBinding) -// .keyboardType(.numberPad) -// .focused($focusedField, equals: .minutes) -// .onSubmit { -// self.focusNextField($focusedField) -// } -// TextField("Seconds", text: secondsBinding) -// .keyboardType(.numberPad) -// .focused($focusedField, equals: .seconds) -// .onSubmit { -// self.focusedField = nil -// } } header: { LabeledContent("Duration", value: self.duration().hourMinuteSecond).font(.footnote) } @@ -102,22 +55,6 @@ struct CountdownFormView : View { func duration() -> TimeInterval { return self.durationBinding.wrappedValue - - -// let formatter = NumberFormatter() -// var hours: Int = 0 -// var minutes: Int = 0 -// var seconds: Int = 0 -// if let h = formatter.number(from: hoursBinding.wrappedValue) { -// hours = h.intValue -// } -// if let m = formatter.number(from: minutesBinding.wrappedValue) { -// minutes = m.intValue -// } -// if let s = formatter.number(from: secondsBinding.wrappedValue) { -// seconds = s.intValue -// } -// return Double(seconds) + 60 * Double(minutes) + 60 * 60 * Double(hours) } } @@ -127,18 +64,12 @@ struct CountdownFormView_Previews: PreviewProvider { @FocusState static var textFieldIsFocused: Bool static var previews: some View { - - Form { - CountdownFormView( - nameBinding: .constant(""), -// secondsBinding: .constant(""), -// minutesBinding: .constant(""), -// hoursBinding: .constant(""), - durationBinding: .constant(0.0), - imageBinding: .constant(.pic3), - repeatCountBinding: .constant(2), - intervalRepeatBinding: .constant(2)) - .environmentObject(TimerModel()) - } + CountdownFormView( + nameBinding: .constant(""), + durationBinding: .constant(0.0), + imageBinding: .constant(.pic3), + repeatCountBinding: .constant(2), + intervalRepeatBinding: .constant(2)) + .environmentObject(TimerModel()) } } diff --git a/LeCountdown/Views/Countdown/NewCountdownView.swift b/LeCountdown/Views/Countdown/NewCountdownView.swift index 52e1148..48669d9 100644 --- a/LeCountdown/Views/Countdown/NewCountdownView.swift +++ b/LeCountdown/Views/Countdown/NewCountdownView.swift @@ -15,12 +15,10 @@ struct NewCountdownView : View { @Environment(\.managedObjectContext) private var viewContext @Binding var isPresented: Bool - var tabSelection: Binding var userActivity: NSUserActivity - init(isPresented: Binding, tabSelection: Binding) { + init(isPresented: Binding) { _isPresented = isPresented - self.tabSelection = tabSelection self.userActivity = Shortcut.newCountdown.userActivity // let shortcut = INShortcut(userActivity: self.userActivity) @@ -28,7 +26,7 @@ struct NewCountdownView : View { var body: some View { NavigationStack { - CountdownEditView(isPresented: $isPresented, tabSelection: self.tabSelection) + CountdownEditView(isPresented: $isPresented) .environment(\.managedObjectContext, viewContext) .onAppear { self.userActivity.becomeCurrent() @@ -71,8 +69,6 @@ struct CountdownEditView : View { @State var errorShown: Bool = false @State var error: Error? = nil - var tabSelection: Binding? = nil - @State var _isNewCountdown: Bool = false // false if editing an existing countdown @State var _hasLoaded = false @@ -82,16 +78,14 @@ struct CountdownEditView : View { @FocusState private var focusedField: CountdownField? - init(isPresented: Binding, countdown: Countdown? = nil, tabSelection: Binding? = nil) { + init(isPresented: Binding, countdown: Countdown? = nil) { _isPresented = isPresented self.countdown = countdown - self.tabSelection = tabSelection } - init(isPresented: Binding, preset: Preset, tabSelection: Binding) { + init(isPresented: Binding, preset: Preset) { _isPresented = isPresented self.preset = preset - self.tabSelection = tabSelection } fileprivate var _formId = "formId" @@ -375,9 +369,6 @@ struct CountdownEditView : View { } else { dismiss() } - - self.tabSelection?.wrappedValue = 1 - } fileprivate func _delete() { @@ -411,8 +402,7 @@ struct CountdownEditView : View { struct NewCountdownView_Previews: PreviewProvider { static var previews: some View { - NewCountdownView(isPresented: .constant(true), - tabSelection: .constant(0)) + NewCountdownView(isPresented: .constant(true)) .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) } } diff --git a/LeCountdown/Views/HomeView.swift b/LeCountdown/Views/HomeView.swift index d439824..d1b0a99 100644 --- a/LeCountdown/Views/HomeView.swift +++ b/LeCountdown/Views/HomeView.swift @@ -18,8 +18,6 @@ struct CompactHomeView: View { animation: .default) private var timers: FetchedResults - @State private var tabSelection: Int = 0 - var body: some View { NavigationStack { @@ -27,14 +25,6 @@ struct CompactHomeView: View { .environment(\.managedObjectContext, viewContext) .environmentObject(Conductor.maestro) } - .onAppear { - if self.timers.count > 0 { - self.tabSelection = 1 - } - } - .onOpenURL { _ in - self.tabSelection = 1 - } } } diff --git a/LeCountdown/Views/NewDataView.swift b/LeCountdown/Views/NewDataView.swift index 5c1f976..a0d3b9d 100644 --- a/LeCountdown/Views/NewDataView.swift +++ b/LeCountdown/Views/NewDataView.swift @@ -28,7 +28,6 @@ struct NewDataView: View { @Environment(\.managedObjectContext) private var viewContext @Binding var isPresented: Bool - var tabSelection: Binding @State var selection: Int = 0 @@ -47,10 +46,10 @@ struct NewDataView: View { .padding(.horizontal) TabView(selection: $selection) { - NewCountdownView(isPresented: $isPresented, tabSelection: self.tabSelection) + NewCountdownView(isPresented: $isPresented) .tag(0) .environment(\.managedObjectContext, viewContext) - NewStopwatchView(isPresented: $isPresented, tabSelection: self.tabSelection) + NewStopwatchView(isPresented: $isPresented) .tag(1) .environment(\.managedObjectContext, viewContext) }.tabViewStyle(.page(indexDisplayMode: .never)) @@ -62,7 +61,7 @@ struct NewDataView: View { struct NewDataView_Previews: PreviewProvider { static var previews: some View { - NewDataView(isPresented: .constant(true), tabSelection: .constant(0)) + NewDataView(isPresented: .constant(true)) .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) } } diff --git a/LeCountdown/Views/PresetsView.swift b/LeCountdown/Views/PresetsView.swift index f3aeab8..c5a9e62 100644 --- a/LeCountdown/Views/PresetsView.swift +++ b/LeCountdown/Views/PresetsView.swift @@ -60,8 +60,6 @@ struct PresetsView: View { @State var isShowingNewCountdown = false @State var isShowingNewStopwatch = false - var tabSelection: Binding - fileprivate func _columnCount() -> Int { return 2 @@ -135,15 +133,15 @@ struct PresetsView: View { Spacer() } .sheet(isPresented: $isShowingNewStopwatch, content: { - NewStopwatchView(isPresented: $isShowingNewStopwatch, tabSelection: self.tabSelection) + NewStopwatchView(isPresented: $isShowingNewStopwatch) .environment(\.managedObjectContext, viewContext) }) .sheet(isPresented: $isShowingNewCountdown, content: { - NewCountdownView(isPresented: $isShowingNewCountdown, tabSelection: self.tabSelection) + NewCountdownView(isPresented: $isShowingNewCountdown) .environment(\.managedObjectContext, viewContext) }) .sheet(isPresented: $isPresented, content: { - CountdownEditView(isPresented: $isPresented, preset: self.model.selectedPreset, tabSelection: self.tabSelection) + CountdownEditView(isPresented: $isPresented, preset: self.model.selectedPreset) .environment(\.managedObjectContext, viewContext) }) .sheet(isPresented: $isShowingSubscription, content: { @@ -174,12 +172,21 @@ struct TimerItemView: View { HStack { VStack(alignment: .leading) { Text(self.name.uppercased()).foregroundColor(colorScheme == .dark ? .white : .black) - Text(self.duration) + HStack(spacing: 4.0) { + Group { + Image(systemName: "timer") + Text(self.duration) + } .foregroundColor(Color.accentColor) - Text(self.sound.uppercased()).foregroundColor(Color(white: 0.7)) + Group { + Image(systemName: "music.note") + Text(self.sound.uppercased()) + } + .foregroundColor(Color(white: 0.25)) + }.font(.footnote) }//.padding() .multilineTextAlignment(.leading) - Spacer() +// Spacer() }//.background(Color(white: 0.1)) //.cornerRadius(16.0) .monospaced() @@ -193,18 +200,18 @@ enum PresetSection: Int, Identifiable, CaseIterable { var id: Int { return self.rawValue } // case workout - case chill + case mindfullness case cooking case tea - case other + case move 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] + case .mindfullness: return [.nap, .meditation, .writing, .reading] + case .move: return [.workout, .stretching, .toothbrushing, .cleaning] } } @@ -212,8 +219,8 @@ enum PresetSection: Int, Identifiable, CaseIterable { 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 .mindfullness: return NSLocalizedString("Self", comment: "") + case .move: return NSLocalizedString("Move", comment: "") case .tea: return NSLocalizedString("Tea", comment: "") } } @@ -232,18 +239,23 @@ struct CountdownInterval { 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 workout +// case runningSplits case pasta case rice - + case blackTea + case greenTea + case toothbrushing + case softBoiled + case mediumBoiledEggs + case hardBoiledEggs + case writing + case reading + case stretching + case cleaning + var localizedName: String { switch self { case .hardBoiledEggs: return NSLocalizedString("Hard boiled eggs", comment: "") @@ -251,24 +263,29 @@ enum Preset: Int, Identifiable, CaseIterable { 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 .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: "") + case .workout: return NSLocalizedString("Workout", comment: "") + case .writing: return NSLocalizedString("Writing", comment: "") + case .reading: return NSLocalizedString("Reading", comment: "") + case .stretching: return NSLocalizedString("Stretching", comment: "") + case .cleaning: return NSLocalizedString("Cleaning", 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: +// 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 { @@ -278,22 +295,27 @@ enum Preset: Int, Identifiable, CaseIterable { case .hardBoiledEggs: return 10 * 60 case .meditation: return 15 * 60 case .nap: return 20 * 60 - case .runningSplits: return 0.0 +// 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 + case .workout: return 30 * 60.0 + case .stretching: return 3 * 60.0 + case .writing: return 30 * 60.0 + case .reading: return 20 * 60.0 + case .cleaning: return 60 * 60.0 } } var playlist: Playlist { switch self { - case .softBoiled, .mediumBoiledEggs, .hardBoiledEggs, .pasta, .rice, .runningSplits, .toothbrushing: + case .softBoiled, .mediumBoiledEggs, .hardBoiledEggs, .pasta, .rice, .toothbrushing, .workout, .stretching: return .stephanBodzin - case .meditation, .blackTea, .greenTea: + case .meditation, .blackTea, .greenTea, .writing, .reading: return .relax - case .nap: + case .nap, .cleaning: return .nature } } @@ -305,7 +327,7 @@ enum Preset: Int, Identifiable, CaseIterable { var formattedDuration: String { let group = self.intervalGroup let count = group.repeatCount.formatted() - let durations = group.intervals.map { $0.duration.minuteSecond } + let durations = group.intervals.map { $0.duration.hourMinuteSecond } let formattedIntervals = durations.joined(separator: "/") if group.repeatCount > 1 { @@ -326,7 +348,7 @@ enum Preset: Int, Identifiable, CaseIterable { struct PresetsView_Previews: PreviewProvider { static var previews: some View { - PresetsView(tabSelection: .constant(0)) + PresetsView() } } @@ -335,6 +357,5 @@ struct TimerItemView_Previews: PreviewProvider { TimerItemView(name: "Hard boiled eggs", duration: "10:00", sound: "Stephan Bodzin") - .frame(width: UIScreen.main.bounds.width / 2.0) } } diff --git a/LeCountdown/Views/Reusable/TimerModel.swift b/LeCountdown/Views/Reusable/TimerModel.swift index 5602654..4bde9b3 100644 --- a/LeCountdown/Views/Reusable/TimerModel.swift +++ b/LeCountdown/Views/Reusable/TimerModel.swift @@ -7,6 +7,7 @@ import Foundation import SwiftUI +import Combine protocol SoundHolder { func selectSound(_ sound: Sound, selected: Bool) @@ -52,6 +53,9 @@ class SoundModel: ObservableObject, SoundHolder { } var soundSelection: String { + if self.playlists.count == 1 { + return self.playlists.first!.localizedString + } if !sounds.isEmpty { if sounds.count == 1 { return sounds.first!.localizedString diff --git a/LeCountdown/Views/StartView.swift b/LeCountdown/Views/StartView.swift new file mode 100644 index 0000000..d42f8b3 --- /dev/null +++ b/LeCountdown/Views/StartView.swift @@ -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 = [] + @Published var customizations: [Preset : Customization] = [:] +// @Published var expanded: Set = [] + +// @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)) + } +} diff --git a/LeCountdown/Views/Stopwatch/NewStopwatchView.swift b/LeCountdown/Views/Stopwatch/NewStopwatchView.swift index 1b2d5aa..5fc9654 100644 --- a/LeCountdown/Views/Stopwatch/NewStopwatchView.swift +++ b/LeCountdown/Views/Stopwatch/NewStopwatchView.swift @@ -15,15 +15,13 @@ struct NewStopwatchView: View { @Environment(\.managedObjectContext) private var viewContext @Binding var isPresented: Bool - var tabSelection: Binding - init(isPresented: Binding, tabSelection: Binding) { + init(isPresented: Binding) { _isPresented = isPresented - self.tabSelection = tabSelection } var body: some View { - StopwatchEditView(isPresented: $isPresented, tabSelection: self.tabSelection) + StopwatchEditView(isPresented: $isPresented) .environment(\.managedObjectContext, viewContext) .navigationTitle("New stopwatch") } @@ -61,15 +59,12 @@ struct StopwatchEditView: View { @Environment(\.isPresented) var envIsPresented - var tabSelection: Binding? = nil - @FetchRequest(sortDescriptors: []) private var timers: FetchedResults - init(isPresented: Binding, stopwatch: Stopwatch? = nil, tabSelection: Binding? = nil) { + init(isPresented: Binding, stopwatch: Stopwatch? = nil) { _isPresented = isPresented self.stopwatch = stopwatch - self.tabSelection = tabSelection } var body: some View { @@ -241,8 +236,6 @@ struct StopwatchEditView: View { } else { dismiss() } - - self.tabSelection?.wrappedValue = 1 } fileprivate func _delete() { @@ -276,7 +269,7 @@ struct StopwatchEditView: View { struct NewStopwatchView_Previews: PreviewProvider { static var previews: some View { - NewStopwatchView(isPresented: .constant(true), tabSelection: .constant(0)) + NewStopwatchView(isPresented: .constant(true)) .environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) } } diff --git a/LeCountdown/fr.lproj/Localizable.strings b/LeCountdown/fr.lproj/Localizable.strings index 34e79b4..df63f5d 100644 --- a/LeCountdown/fr.lproj/Localizable.strings +++ b/LeCountdown/fr.lproj/Localizable.strings @@ -267,3 +267,12 @@ "Calendar" = "Calendrier"; "Disclaimer" = "Ceci est une version beta d'Enchante.\n\nPour l'instant, veillez à ne pas dépendre de l'app pour des évènements trop critiques, on ne sait jamais :)\n\nSi vous avez des remarques ou un problème, merci de me contacter en allant dans les réglages."; "Volume" = "Volume"; +"Workout" = "Exercice"; +"Move" = "Bouger"; +"Self" = "Soi"; +"Writing" = "Écriture"; +"Reading" = "Lecture"; +"Stretching" = "Étirements"; +"Cleaning" = "Nettoyage"; +"Done" = "OK"; +"Create your own" = "Créez les vôtres";