diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index e98aac9..5c18de7 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -69,6 +69,7 @@ C498E5A5299152B400E90DE0 /* GreenCheckmarkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E5A4299152B400E90DE0 /* GreenCheckmarkView.swift */; }; C498E5A6299152C600E90DE0 /* GreenCheckmarkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E5A4299152B400E90DE0 /* GreenCheckmarkView.swift */; }; C4BA2AD62993F62700CB4FBA /* SoundSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2AD52993F62700CB4FBA /* SoundSelectionView.swift */; }; + C4BA2ADB299549BC00CB4FBA /* TimerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2ADA299549BC00CB4FBA /* TimerModel.swift */; }; C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; }; C4F8B15729891271005C86A5 /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; }; C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C4F8B15829891528005C86A5 /* forest_stream.mp3 */; }; @@ -235,6 +236,7 @@ C498E5A4299152B400E90DE0 /* GreenCheckmarkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GreenCheckmarkView.swift; sourceTree = ""; }; C4BA2AD52993F62700CB4FBA /* SoundSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundSelectionView.swift; sourceTree = ""; }; C4BA2AD72993F7D200CB4FBA /* LeCountdown.0.5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.5.xcdatamodel; sourceTree = ""; }; + C4BA2ADA299549BC00CB4FBA /* TimerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerModel.swift; sourceTree = ""; }; C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = ""; }; C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = ""; }; C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = ""; }; @@ -542,6 +544,7 @@ C4F8B1D1298BF646005C86A5 /* PermissionAlertView.swift */, C4F8B165298A9ABB005C86A5 /* SoundImageFormView.swift */, C4BA2AD52993F62700CB4FBA /* SoundSelectionView.swift */, + C4BA2ADA299549BC00CB4FBA /* TimerModel.swift */, ); path = Components; sourceTree = ""; @@ -774,6 +777,7 @@ C4F8B1BD298AC8DE005C86A5 /* AlarmDialView.swift in Sources */, C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */, C4F8B182298AC234005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */, + C4BA2ADB299549BC00CB4FBA /* TimerModel.swift in Sources */, C4F8B16B298AA240005C86A5 /* NewStopwatchView.swift in Sources */, C4060DF5297AE9A7003FAB80 /* TimeInterval+Extensions.swift in Sources */, C4F8B166298A9ABB005C86A5 /* SoundImageFormView.swift in Sources */, diff --git a/LeCountdown/Model/Model+Extensions.swift b/LeCountdown/Model/Model+Extensions.swift index ea0af2f..ec8bee1 100644 --- a/LeCountdown/Model/Model+Extensions.swift +++ b/LeCountdown/Model/Model+Extensions.swift @@ -53,25 +53,24 @@ extension AbstractSoundTimer { return [] } - var playlists: [Playlist] { - if let playlistList { - return playlistList.enumItems() - } - return [] - } +// var playlists: [Playlist] { +// if let playlistList { +// return playlistList.enumItems() +// } +// return [] +// } func setSounds(_ sounds: [Sound]) { self.soundList = sounds.stringRepresentation } - func setPlaylists(_ playlists: [Playlist]) { - self.playlistList = playlists.stringRepresentation - } +// func setPlaylists(_ playlists: [Playlist]) { +// self.playlistList = playlists.stringRepresentation +// } var coolSound: Sound { var allSounds: [Sound] = [] allSounds.append(contentsOf: self.sounds) - allSounds.append(contentsOf: self.playlists.reduce([], { $0 + $1.sounds })) return allSounds.randomElement() ?? Sound.allCases[0] } diff --git a/LeCountdown/Sound/Sound.swift b/LeCountdown/Sound/Sound.swift index f48b591..b6dba4c 100644 --- a/LeCountdown/Sound/Sound.swift +++ b/LeCountdown/Sound/Sound.swift @@ -8,56 +8,27 @@ import Foundation import AVFoundation -struct PlaylistSelection: Identifiable { - var id: Int { return self.playlist.id } - var playlist: Playlist - - var selected: Bool { - didSet { - if selected { - self.sounds = playlist.sounds - } else { - self.sounds = [] - } - } - } +protocol Localized { + var localizedString: String { get } +} + +class SoundCatalog { - fileprivate var _sounds: [Sound] = [] - var sounds: [Sound] { - set { - _sounds = newValue - - if !selected && (newValue.count == self.playlist.sounds.count) { - self.selected = true - } else if selected && (newValue.count != self.playlist.sounds.count) { - self.selected = false - } - - } - get { - return _sounds - } + static let main: SoundCatalog = SoundCatalog() + + fileprivate var _soundsByPlaylist: [Playlist : [Sound]] = [:] + + init() { + self._soundsByPlaylist = Dictionary(grouping: Sound.allCases) { $0.playlist } } - static func allPlaylists() -> [PlaylistSelection] { - - var playlistSelections: [PlaylistSelection] = [] - for playlist in Playlist.allCases { - let playlistSelection = PlaylistSelection(playlist: playlist, selected: false) - playlistSelections.append(playlistSelection) - } - return playlistSelections + func sounds(for playlist: Playlist) -> [Sound] { + return self._soundsByPlaylist[playlist] ?? [] } } -struct SoundSelection: Identifiable { - var id: Int { return self.sound.id } - var sound: Sound - var selected: Bool -} - -enum Playlist: Int, CaseIterable, Identifiable { +enum Playlist: Int, CaseIterable, Identifiable, Localized { var id: Int { return self.rawValue } @@ -65,17 +36,17 @@ enum Playlist: Int, CaseIterable, Identifiable { case fun case stephanBodzin - var sounds: [Sound] { - switch self { - case .nature: - return Sound.allCases - case .fun: - return Sound.allCases - case .stephanBodzin: - return Sound.allCases - } - } - +// var sounds: [Sound] { +// switch self { +// case .nature: +// return Sound.allCases +// case .fun: +// return Sound.allCases +// case .stephanBodzin: +// return Sound.allCases +// } +// } +// var localizedString: String { switch self { case .nature: @@ -89,17 +60,21 @@ enum Playlist: Int, CaseIterable, Identifiable { } // Sound id are stored thus case order should not be changed -enum Sound: Int, CaseIterable, Identifiable { +enum Sound: Int, CaseIterable, Identifiable, Localized { var id: Int { return self.rawValue } case trainhorn = 1 // default case forestStream + case sb1 + case sb2 var localizedString: String { switch self { case .trainhorn: return NSLocalizedString("Train horn", comment: "") case .forestStream: return NSLocalizedString("Forest stream", comment: "") + case .sb1: return "sb1" + case .sb2: return "sb2" } } @@ -107,16 +82,27 @@ enum Sound: Int, CaseIterable, Identifiable { switch self { case .trainhorn: return "train_horn.mp3" case .forestStream: return "forest_stream.mp3" + case .sb1: return "forest_stream.mp3" + case .sb2: return "forest_stream.mp3" + } } - var duration: TimeInterval { + var playlist: Playlist { switch self { - case .trainhorn: return 7.8 - case .forestStream: return 300.1 + case .trainhorn: return .fun + case .forestStream: return .nature + case .sb1, .sb2: return .stephanBodzin } } +// var duration: TimeInterval { +// switch self { +// case .trainhorn: return 7.8 +// case .forestStream: return 300.1 +// } +// } + var url: URL? { let components = self.soundName.components(separatedBy: ".") diff --git a/LeCountdown/Views/Alarm/NewAlarmView.swift b/LeCountdown/Views/Alarm/NewAlarmView.swift index 0345838..3cbf148 100644 --- a/LeCountdown/Views/Alarm/NewAlarmView.swift +++ b/LeCountdown/Views/Alarm/NewAlarmView.swift @@ -153,7 +153,7 @@ struct AlarmEditView: View { } self.sounds = alarm.sounds - self.playlists = alarm.playlists +// self.playlists = alarm.playlists if let image = alarm.image, let coolpic = CoolPic(rawValue: image) { self.image = coolpic @@ -184,7 +184,7 @@ struct AlarmEditView: View { a.image = self.image.rawValue a.setSounds(self.sounds) - a.setPlaylists(self.playlists) +// a.setPlaylists(self.playlists) a.repeatCount = self.soundRepeatCount diff --git a/LeCountdown/Views/Components/SoundImageFormView.swift b/LeCountdown/Views/Components/SoundImageFormView.swift index afddd59..ea0f489 100644 --- a/LeCountdown/Views/Components/SoundImageFormView.swift +++ b/LeCountdown/Views/Components/SoundImageFormView.swift @@ -11,7 +11,7 @@ struct SoundImageFormView : View { var imageBinding: Binding - var playlistBinding: Binding<[PlaylistSelection]> + @EnvironmentObject var model: TimerModel var repeatCountBinding: Binding? = nil var optionalSound: Binding? = nil @@ -30,7 +30,10 @@ struct SoundImageFormView : View { if self.optionalSound?.wrappedValue == true { NavigationLink { - SoundSelectionView(playlistBinding: playlistBinding) + + PlaylistsView().environmentObject(self.model) + +// SoundSelectionView(playlistBinding: playlistBinding) } label: { Text("Sound") } @@ -55,7 +58,7 @@ struct SoundImageFormView : View { } NavigationLink { - SoundSelectionView(playlistBinding: playlistBinding) + PlaylistsView().environmentObject(self.model) } label: { Text("Sound") } @@ -99,12 +102,19 @@ struct SoundImageFormView : View { struct SoundImageFormView_Previews: PreviewProvider { + static func soundBinding(sound: Sound) -> Binding { + return .constant(true) + } + static func playlistBinding(playlist: Playlist) -> Binding { + return .constant(true) + } + static var previews: some View { Form { SoundImageFormView( imageBinding: .constant(.pic1), - playlistBinding: .constant([]), repeatCountBinding: .constant(2)) + .environmentObject(SoundHolderPlaceholder()) } } } diff --git a/LeCountdown/Views/Components/SoundSelectionView.swift b/LeCountdown/Views/Components/SoundSelectionView.swift index 12ba7ce..4780034 100644 --- a/LeCountdown/Views/Components/SoundSelectionView.swift +++ b/LeCountdown/Views/Components/SoundSelectionView.swift @@ -7,57 +7,142 @@ import SwiftUI -struct SoundRow: View { - var sound: Sound - @State var selected: Bool - var handleSelection: (Bool) -> () +struct PlaylistsView: View { + + @EnvironmentObject var model: TimerModel var body: some View { - Toggle(sound.localizedString, isOn: $selected) - .onChange(of: selected, perform: handleSelection) + + Form { + ForEach(Playlist.allCases) { playlist in + PlaylistSectionView(playlist: playlist) + .environmentObject(self.model) + } + } + } } -struct SoundSelectionView: View { +struct PlaylistSectionView: View { - @Binding var playlistBinding: [PlaylistSelection] + @EnvironmentObject var model: TimerModel + + var playlist: Playlist + + var body: some View { + + Section { + let sounds = SoundCatalog.main.sounds(for: self.playlist) + List(sounds) { sound in + ToggleRow(item: sound, selected: self.model.isSelected(sound: sound)) { selected in + self.model.selectSound(sound, selected: selected) + } + } + } header: { + ToggleRow(item: self.playlist, selected: self.model.isSelected(playlist: self.playlist)) { selected in + self.model.selectPlaylist(self.playlist, selected: selected) + } + } + + } + +} + +struct ToggleRow: View { + var item: T + @State var selected: Bool + var handleSelection: (Bool) -> () + + var body: some View { + Toggle(item.localizedString, isOn: $selected) + .onChange(of: self.selected, perform: handleSelection) + } +} + +//struct PlaylistRow: View { +// var playlist: Playlist +// @State var selected: Bool +// var handleSelection: (Bool) -> () +// +// var body: some View { +// Toggle(playlist.localizedString, isOn: $selected) +// .onChange(of: selected, perform: handleSelection) +// } +//} +// +//struct SoundRow: View { +// var sound: Sound +// @State var selected: Bool +// var handleSelection: (Bool) -> () +// +// var body: some View { +// Toggle(sound.localizedString, isOn: $selected) +// .onChange(of: selected, perform: handleSelection) +// } +//} + +struct SoundSelectionView: View { var body: some View { Form { - ForEach($playlistBinding, id: \.id) { $ps in - -// List(selection: <#T##Binding<_?>?#>) { -// <#code#> +// PlaylistsView() + + +// ForEach($playlistBinding, id: \.id) { $ps in +// +// +// Section { +// ForEach(ps.playlist.sounds) { sound in +// +// SoundRow(sound: sound, selected: ps.sounds.contains(sound)) { selected in +// if selected { +// ps.sounds.append(sound) +// } else { +// ps.sounds.removeAll(where: { $0 == sound }) +// } +// } +// } +// } header: { +// Toggle(ps.playlist.localizedString, isOn: $ps.selected) // } - - - Section { - ForEach(ps.playlist.sounds) { sound in - - SoundRow(sound: sound, selected: ps.sounds.contains(sound)) { selected in - if selected { - ps.sounds.append(sound) - } else { - ps.sounds.removeAll(where: { $0 == sound }) - } - } - } - } header: { - Toggle(ps.playlist.localizedString, isOn: $ps.selected) - } - }.navigationTitle("Sounds") +// }.navigationTitle("Sounds") } } } -struct SoundSelectionView_Previews: PreviewProvider { +struct PlaylistsView_Previews: PreviewProvider { + + static func soundBinding(sound: Sound) -> Binding { + return .constant(true) + } + static func playlistBinding(playlist: Playlist) -> Binding { + return .constant(true) + } + + static var previews: some View { + PlaylistsView() + .environmentObject(SoundHolderPlaceholder()) + } +} + +struct PlaylistSectionView_Previews: PreviewProvider { + + static func soundBinding(sound: Sound) -> Binding { + return .constant(true) + } + static func playlistBinding(playlist: Playlist) -> Binding { + return .constant(true) + } + static var previews: some View { - SoundSelectionView( - playlistBinding: .constant([])) + Form { + PlaylistSectionView(playlist: .stephanBodzin) + .environmentObject(SoundHolderPlaceholder()) + } } } diff --git a/LeCountdown/Views/Components/TimerModel.swift b/LeCountdown/Views/Components/TimerModel.swift new file mode 100644 index 0000000..c951cc0 --- /dev/null +++ b/LeCountdown/Views/Components/TimerModel.swift @@ -0,0 +1,84 @@ +// +// SoundHolder.swift +// LeCountdown +// +// Created by Laurent Morvillier on 09/02/2023. +// + +import Foundation + +protocol SoundHolder { + func selectSound(_ sound: Sound, selected: Bool) + func selectPlaylist(_ playlist: Playlist, selected: Bool) + + func isSelected(sound: Sound) -> Bool + func isSelected(playlist: Playlist) -> Bool +} + +class TimerModel : ObservableObject, SoundHolder { + + @Published var playlists: Set = [] + @Published var sounds: Set = [] + + // MARK: - SoundHolder + + func selectSound(_ sound: Sound, selected: Bool) { + print("selectSound") + + if selected { + self.sounds.insert(sound) + } else { + self.sounds.remove(sound) + } + + // toggle playlist if necessary + let playlist = sound.playlist + let playlistSounds: [Sound] = SoundCatalog.main.sounds(for: playlist) + if self.sounds.isSuperset(of: playlistSounds) { + self.playlists.insert(playlist) + } else { + self.playlists.remove(playlist) + } + + } + + func selectPlaylist(_ playlist: Playlist, selected: Bool) { + print("selectPlaylist") + + if selected { + self.playlists.insert(playlist) + } else { + self.playlists.remove(playlist) + } + + let sounds: [Sound] = SoundCatalog.main.sounds(for: playlist) + self.sounds.formSymmetricDifference(sounds) + + print("sounds = \(self.sounds.count)") + } + + func isSelected(sound: Sound) -> Bool { + self.sounds.contains(sound) + } + + func isSelected(playlist: Playlist) -> Bool { + self.playlists.contains(playlist) + } + +} + +class SoundHolderPlaceholder : ObservableObject, SoundHolder { + func selectSound(_ sound: Sound, selected: Bool) { + } + + func selectPlaylist(_ playlist: Playlist, selected: Bool) { + } + + func isSelected(sound: Sound) -> Bool { + return false + } + + func isSelected(playlist: Playlist) -> Bool { + return false + } +} diff --git a/LeCountdown/Views/ContentView.swift b/LeCountdown/Views/ContentView.swift index ccfbe3a..379541d 100644 --- a/LeCountdown/Views/ContentView.swift +++ b/LeCountdown/Views/ContentView.swift @@ -59,7 +59,8 @@ struct ContentView: View { ReorderableForEach(items: timersArray) { timer in - DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width) .environment(\.managedObjectContext, viewContext) + DialView(timer: timer, isEditingBinding: self.$isEditing, frameSize: width) + .environment(\.managedObjectContext, viewContext) .environmentObject(Conductor.maestro) .environmentObject(boringContext) @@ -70,7 +71,8 @@ struct ContentView: View { }.padding(.horizontal, itemSpacing) if !conductor.liveTimers.isEmpty { - LiveTimerListView() .environment(\.managedObjectContext, viewContext) + LiveTimerListView() + .environment(\.managedObjectContext, viewContext) .environmentObject(conductor) .background(Color(white: 0.9)) .cornerRadius(16.0, corners: [.topRight, .topLeft]) @@ -118,6 +120,8 @@ struct ContentView: View { } + // MARK: - Subviews + @ViewBuilder fileprivate func _newView(isPresented: Binding) -> some View { switch T.self { @@ -132,6 +136,8 @@ struct ContentView: View { } } + + // MARK: - Business fileprivate func _reorder(from: IndexSet, to: Int) { var timers: [AbstractTimer] = self.timersArray diff --git a/LeCountdown/Views/Countdown/CountdownFormView.swift b/LeCountdown/Views/Countdown/CountdownFormView.swift index 691d352..8159112 100644 --- a/LeCountdown/Views/Countdown/CountdownFormView.swift +++ b/LeCountdown/Views/Countdown/CountdownFormView.swift @@ -10,12 +10,14 @@ import SwiftUI struct CountdownFormView : View { + @EnvironmentObject var model: TimerModel + var secondsBinding: Binding var minutesBinding: Binding var nameBinding: Binding var imageBinding: Binding - var playlistBinding: Binding<[PlaylistSelection]> + var repeatCountBinding: Binding var textFieldIsFocused: FocusState.Binding @@ -37,8 +39,8 @@ struct CountdownFormView : View { SoundImageFormView( imageBinding: imageBinding, - playlistBinding: playlistBinding, repeatCountBinding: repeatCountBinding) + .environmentObject(self.model) } } @@ -49,8 +51,21 @@ struct CountdownFormView_Previews: PreviewProvider { @FocusState static var textFieldIsFocused: Bool + static func soundBinding(sound: Sound) -> Binding { + return .constant(true) + } + static func playlistBinding(playlist: Playlist) -> Binding { + return .constant(true) + } + static var previews: some View { - CountdownFormView(secondsBinding: .constant(""), minutesBinding: .constant(""), - nameBinding: .constant(""), imageBinding: .constant(.pic3), playlistBinding: .constant([]), repeatCountBinding: .constant(2), textFieldIsFocused: $textFieldIsFocused) + CountdownFormView( + secondsBinding: .constant(""), + minutesBinding: .constant(""), + nameBinding: .constant(""), + imageBinding: .constant(.pic3), + repeatCountBinding: .constant(2), + textFieldIsFocused: $textFieldIsFocused) + .environmentObject(SoundHolderPlaceholder()) } } diff --git a/LeCountdown/Views/Countdown/NewCountdownView.swift b/LeCountdown/Views/Countdown/NewCountdownView.swift index 48e4268..84cf4a2 100644 --- a/LeCountdown/Views/Countdown/NewCountdownView.swift +++ b/LeCountdown/Views/Countdown/NewCountdownView.swift @@ -28,6 +28,8 @@ struct CountdownEditView : View { @Environment(\.managedObjectContext) private var viewContext @Environment(\.dismiss) private var dismiss + @StateObject var model: TimerModel = TimerModel() + var countdown: Countdown? = nil @Binding var isPresented: Bool @@ -36,8 +38,9 @@ struct CountdownEditView : View { @State var minutesString: String = "" @State var nameString: String = "" -// @State var sounds: [SoundSelection] = [] - @State var playlists: [PlaylistSelection] = [] + @State var playlists: Set = [] + @State var sounds: Set = [] + @State var soundRepeatCount: Int16 = 0 @State var image: CoolPic = .pic1 @@ -71,9 +74,9 @@ struct CountdownEditView : View { minutesBinding: $minutesString, nameBinding: $nameString, imageBinding: $image, - playlistBinding: $playlists, repeatCountBinding: $soundRepeatCount, textFieldIsFocused: $textFieldIsFocused) + .environmentObject(self.model) .onAppear { self._onAppear() } @@ -137,9 +140,31 @@ struct CountdownEditView : View { } + // MARK: - Bindings + + func playlistBinding(playlist: Playlist) -> Binding { + Binding( + get: { self.playlists.contains(playlist) }, + set: { if $0 { self.playlists.insert(playlist) } + else { self.playlists.remove(playlist) } + } + ) + } + + func soundBinding(sound: Sound) -> Binding { + Binding( + get: { self.sounds.contains(sound) }, + set: { if $0 { self.sounds.insert(sound) } + else { self.sounds.remove(sound) } + } + ) + } + + // MARK: - Business + fileprivate func _onAppear() { - self.playlists = PlaylistSelection.allPlaylists() +// self.playlists = PlaylistSelection.allPlaylists() self._isAdding = (self.countdown == nil) diff --git a/LeCountdown/Views/Stopwatch/NewStopwatchView.swift b/LeCountdown/Views/Stopwatch/NewStopwatchView.swift index dbc6751..e17cbf4 100644 --- a/LeCountdown/Views/Stopwatch/NewStopwatchView.swift +++ b/LeCountdown/Views/Stopwatch/NewStopwatchView.swift @@ -28,6 +28,8 @@ struct StopwatchEditView: View { @Environment(\.managedObjectContext) private var viewContext @Environment(\.dismiss) private var dismiss + @StateObject var model: TimerModel = TimerModel() + var stopwatch: Stopwatch? = nil @Binding var isPresented: Bool @@ -37,7 +39,7 @@ struct StopwatchEditView: View { @State var nameString: String = "" @State var playSound: Bool = false - @State var playlists: [PlaylistSelection] = [] + @State var image: CoolPic = .pic1 @State var deleteConfirmationShown: Bool = false @@ -68,9 +70,8 @@ struct StopwatchEditView: View { StopwatchFormView(nameBinding: self.$nameString, imageBinding: self.$image, playSoundBinding: self.$playSound, - playlistBinding: self.$playlists, - textFieldIsFocused: $textFieldIsFocused) - .onAppear { + textFieldIsFocused: $textFieldIsFocused).environmentObject(self.model) + .onAppear { self._onAppear() } .confirmationDialog("", isPresented: $deleteConfirmationShown, actions: { @@ -133,6 +134,59 @@ struct StopwatchEditView: View { } + // MARK: - Bindings + +// func playlistBinding(playlist: Playlist) -> Binding { +// Binding( +// get: { self.playlists.contains(playlist) }, +// set: { self._playlistChange(playlist, selected: $0) } +// ) +// } +// +// fileprivate func _playlistChange(_ playlist: Playlist, selected: Bool) { +// +// print("_playlistChange") +// +// let sounds: [Sound] = SoundCatalog.main.sounds(for: playlist) +// if selected { +// self.playlists.insert(playlist) +// self.sounds.formUnion(sounds) +// } else { +// self.playlists.remove(playlist) +// self.sounds.formIntersection(sounds) +// } +// } +// +// func soundBinding(sound: Sound) -> Binding { +// Binding( +// get: { self.sounds.contains(sound) }, +// set: { self._soundChange(sound, selected: $0) } +// ) +// } +// +// fileprivate func _soundChange(_ sound: Sound, selected: Bool) { +// +// print("_soundChange") +// +// if selected { +// self.sounds.insert(sound) +// } else { +// self.sounds.remove(sound) +// } +// +// // toggle playlist if necessary +// let playlist = sound.playlist +// let playlistSounds: [Sound] = SoundCatalog.main.sounds(for: playlist) +// if self.sounds.isSuperset(of: playlistSounds) { +// self.playlists.insert(playlist) +// } else { +// self.playlists.remove(playlist) +// } +// +// } + + // MARK: - Business + fileprivate func _onAppear() { self._isAdding = (self.stopwatch == nil) diff --git a/LeCountdown/Views/Stopwatch/StopwatchFormView.swift b/LeCountdown/Views/Stopwatch/StopwatchFormView.swift index 8936234..be485e6 100644 --- a/LeCountdown/Views/Stopwatch/StopwatchFormView.swift +++ b/LeCountdown/Views/Stopwatch/StopwatchFormView.swift @@ -13,7 +13,7 @@ struct StopwatchFormView: View { var imageBinding: Binding var playSoundBinding: Binding - var playlistBinding: Binding<[PlaylistSelection]> + @EnvironmentObject var model: TimerModel var textFieldIsFocused: FocusState.Binding @@ -26,8 +26,8 @@ struct StopwatchFormView: View { } SoundImageFormView(imageBinding: imageBinding, - playlistBinding: playlistBinding, optionalSound: playSoundBinding) + .environmentObject(self.model) } } @@ -36,11 +36,13 @@ struct StopwatchFormView: View { struct StopwatchFormView_Previews: PreviewProvider { @FocusState static var textFieldIsFocused: Bool - + static var previews: some View { - StopwatchFormView(nameBinding: .constant(""), - imageBinding: .constant(.pic1), - playSoundBinding: .constant(true), - playlistBinding: .constant([]), textFieldIsFocused: $textFieldIsFocused) + StopwatchFormView( + nameBinding: .constant(""), + imageBinding: .constant(.pic1), + playSoundBinding: .constant(true), + textFieldIsFocused: $textFieldIsFocused) + .environmentObject(SoundHolderPlaceholder()) } }