From ea459d7a2aeba3214f920e30e8559db1fc229f37 Mon Sep 17 00:00:00 2001 From: Laurent Date: Mon, 27 Mar 2023 15:26:18 +0200 Subject: [PATCH] Adds confirmation sounds --- LeCountdown.xcodeproj/project.pbxproj | 14 ++- LeCountdown/AppDelegate.swift | 7 +- LeCountdown/Conductor.swift | 18 +++- LeCountdown/CountdownScheduler.swift | 5 - ...bstractSoundTimer+CoreDataProperties.swift | 3 +- .../.xccurrentversion | 2 +- .../LeCountdown.0.6.3.xcdatamodel/contents | 52 +++++++++ LeCountdown/Model/Model+Extensions.swift | 19 ++-- LeCountdown/Sound/Sound.swift | 39 +++++-- ...firm_1_Notification_Button_Settings_UI.wav | Bin ...shot_cancel_clicky_reverb_digital_long.wav | Bin ...le_18_Interface_Button_Alert_System_Cm.wav | Bin .../MRKRSTPHR_synth_one_shot_bleep_G.wav | Bin .../PVP_Stab_Oneshot_Bleep_Em.wav | Bin LeCountdown/TimerRouter.swift | 11 -- .../Views/Countdown/CountdownFormView.swift | 3 +- .../Views/Countdown/NewCountdownView.swift | 8 +- .../Views/Reusable/SoundFormView.swift | 100 +++++++++--------- .../Views/Reusable/SoundSelectionView.swift | 81 ++------------ LeCountdown/Views/Reusable/TimerModel.swift | 26 +++-- .../Views/Stopwatch/StopwatchFormView.swift | 6 +- LeCountdown/fr.lproj/Localizable.strings | 1 + 22 files changed, 207 insertions(+), 188 deletions(-) create mode 100644 LeCountdown/Model/LeCountdown.xcdatamodeld/LeCountdown.0.6.3.xcdatamodel/contents rename LeCountdown/Sound_Assets/{ => Shorts}/ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav (100%) rename LeCountdown/Sound_Assets/{ => Shorts}/ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav (100%) rename LeCountdown/Sound_Assets/{ => Shorts}/ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav (100%) rename LeCountdown/Sound_Assets/{ => Shorts}/MRKRSTPHR_synth_one_shot_bleep_G.wav (100%) rename LeCountdown/Sound_Assets/{ => Shorts}/PVP_Stab_Oneshot_Bleep_Em.wav (100%) diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index 1964ee9..19c384d 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -397,6 +397,7 @@ C4A16DA029D0A7FE00143D5E /* ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav; sourceTree = ""; }; C4A16DA629D0AAA800143D5E /* ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav; sourceTree = ""; }; C4A16DAC29D0AB1F00143D5E /* ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav; sourceTree = ""; }; + C4A16DBD29D1C9DE00143D5E /* LeCountdown.0.6.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.6.3.xcdatamodel; 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 = ""; }; @@ -754,16 +755,24 @@ C445FA962987D0CF0054D761 /* Sound_Assets */ = { isa = PBXGroup; children = ( + C4A16DBC29D1A69200143D5E /* Shorts */, C415D3EB29C376530037B215 /* Nature */, C415D3CC29C0B13A0037B215 /* Relax */, C4BA2ADC2995AB7600CB4FBA /* Stephan_Bodzin */, + ); + path = Sound_Assets; + sourceTree = ""; + }; + C4A16DBC29D1A69200143D5E /* Shorts */ = { + isa = PBXGroup; + children = ( C4E5D68129B93583008E7465 /* PVP_Stab_Oneshot_Bleep_Em.wav */, C4A16DA029D0A7FE00143D5E /* ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav */, C4A16DA629D0AAA800143D5E /* ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav */, C4A16DAC29D0AB1F00143D5E /* ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav */, C4A16D9A29D0A7D300143D5E /* MRKRSTPHR_synth_one_shot_bleep_G.wav */, ); - path = Sound_Assets; + path = Shorts; sourceTree = ""; }; C4BA2ADC2995AB7600CB4FBA /* Stephan_Bodzin */ = { @@ -1894,6 +1903,7 @@ C4060DCA297AE73D003FAB80 /* LeCountdown.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + C4A16DBD29D1C9DE00143D5E /* LeCountdown.0.6.3.xcdatamodel */, C4BA2B6B29A4C47100CB4FBA /* LeCountdown.0.6.2.xcdatamodel */, C4BA2B46299FCD8B00CB4FBA /* LeCountdown.0.6.1.xcdatamodel */, C4BA2B07299BDAE000CB4FBA /* LeCountdown.0.6.xcdatamodel */, @@ -1905,7 +1915,7 @@ C418A14F298428CB00C22230 /* LeCountdown.0.1.xcdatamodel */, C4060DCB297AE73D003FAB80 /* LeCountdown.xcdatamodel */, ); - currentVersion = C4BA2B6B29A4C47100CB4FBA /* LeCountdown.0.6.2.xcdatamodel */; + currentVersion = C4A16DBD29D1C9DE00143D5E /* LeCountdown.0.6.3.xcdatamodel */; path = LeCountdown.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/LeCountdown/AppDelegate.swift b/LeCountdown/AppDelegate.swift index f0d9551..caee8fa 100644 --- a/LeCountdown/AppDelegate.swift +++ b/LeCountdown/AppDelegate.swift @@ -81,8 +81,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate { print("didReceive notification") let timerId = self._timerId(notificationId: response.notification.request.identifier) - Conductor.maestro.cancelCountdown(id: timerId) - +// Conductor.maestro.cancelCountdown(id: timerId) + Conductor.maestro.cancelSoundPlayer(id: timerId) } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { @@ -93,14 +93,13 @@ extension AppDelegate: UNUserNotificationCenterDelegate { Conductor.maestro.notifyUser(countdownId: timerId) } - fileprivate func _timerId(notificationId: String) -> String { + fileprivate func _timerId(notificationId: String) -> TimerID { let components = notificationId.components(separatedBy: CountdownScheduler.notificationIdSeparator) if components.count == 2 { return components[0] } else { fatalError("bad notification format : \(notificationId)") } - } } diff --git a/LeCountdown/Conductor.swift b/LeCountdown/Conductor.swift index ed4b69a..696ad85 100644 --- a/LeCountdown/Conductor.swift +++ b/LeCountdown/Conductor.swift @@ -174,7 +174,8 @@ class Conductor: ObservableObject { self.removeLiveTimer(id: countdown.stringId) - let soundFile = try SoundFile(fullName: countdown.soundName) + let sound = countdown.someSound + let soundFile = try SoundFile(fullName: sound.fileName) let soundPlayer = try DelaySoundPlayer(timerID: countdown.stringId, soundFile: soundFile) self._delayedSoundPlayers[countdown.stringId] = soundPlayer try soundPlayer.start(in: countdown.duration, @@ -184,7 +185,7 @@ class Conductor: ObservableObject { self.currentCountdowns[countdown.stringId] = dateInterval if Preferences.playConfirmationSound { - self._playConfirmationSound() + self._playConfirmationSound(timer: countdown) } handler(.success(date)) @@ -234,7 +235,8 @@ class Conductor: ObservableObject { if let countdown = context.object(stringId: countdownId) as? Countdown { do { - let soundFile = try SoundFile(fullName: countdown.soundName) + let sound: Sound = countdown.someSound + let soundFile: SoundFile = try SoundFile(fullName: sound.fileName) let soundPlayer = try DelaySoundPlayer(timerID: countdownId, soundFile: soundFile) self._delayedSoundPlayers[countdown.stringId] = soundPlayer try soundPlayer.restore(for: interval.end, repeatCount: Int(countdown.repeatCount)) @@ -288,8 +290,14 @@ class Conductor: ObservableObject { AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate)) } - fileprivate func _playConfirmationSound() { - self._playSound(Const.confirmationSound.rawValue) + fileprivate func _playConfirmationSound(timer: AbstractSoundTimer) { + let fileName: String + if let confirmationSound = timer.confirmationSounds.randomElement() { + fileName = confirmationSound.fileName + } else { + fileName = Const.confirmationSound.rawValue + } + self._playSound(fileName) } fileprivate func _playCancellationSound() { diff --git a/LeCountdown/CountdownScheduler.swift b/LeCountdown/CountdownScheduler.swift index ec3ce97..5906088 100644 --- a/LeCountdown/CountdownScheduler.swift +++ b/LeCountdown/CountdownScheduler.swift @@ -36,9 +36,7 @@ class CountdownScheduler { content.body = body - let sound = countdown.soundName self._createNotification(countdown: countdown, content: content, handler: handler) - print("Selected sound = \(sound)") // content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0) content.interruptionLevel = .critical @@ -92,13 +90,10 @@ class CountdownScheduler { } func cancelCurrentNotifications(countdownId: String) { - UNUserNotificationCenter.current().getPendingNotificationRequests { requests in let ids = requests.map { $0.identifier }.filter { $0.hasPrefix(countdownId) } UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ids) } - -// Conductor.maestro.cancelCountdown(id: countdownId) } } diff --git a/LeCountdown/Model/Generation/AbstractSoundTimer+CoreDataProperties.swift b/LeCountdown/Model/Generation/AbstractSoundTimer+CoreDataProperties.swift index 17605e3..5a64584 100644 --- a/LeCountdown/Model/Generation/AbstractSoundTimer+CoreDataProperties.swift +++ b/LeCountdown/Model/Generation/AbstractSoundTimer+CoreDataProperties.swift @@ -2,7 +2,7 @@ // AbstractSoundTimer+CoreDataProperties.swift // LeCountdown // -// Created by Laurent Morvillier on 10/02/2023. +// Created by Laurent Morvillier on 27/03/2023. // // @@ -18,5 +18,6 @@ extension AbstractSoundTimer { @NSManaged public var repeatCount: Int16 @NSManaged public var soundList: String? + @NSManaged public var confirmationSoundList: String? } diff --git a/LeCountdown/Model/LeCountdown.xcdatamodeld/.xccurrentversion b/LeCountdown/Model/LeCountdown.xcdatamodeld/.xccurrentversion index 51d83d9..d18b0c0 100644 --- a/LeCountdown/Model/LeCountdown.xcdatamodeld/.xccurrentversion +++ b/LeCountdown/Model/LeCountdown.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - LeCountdown.0.6.2.xcdatamodel + LeCountdown.0.6.3.xcdatamodel diff --git a/LeCountdown/Model/LeCountdown.xcdatamodeld/LeCountdown.0.6.3.xcdatamodel/contents b/LeCountdown/Model/LeCountdown.xcdatamodeld/LeCountdown.0.6.3.xcdatamodel/contents new file mode 100644 index 0000000..be50a9c --- /dev/null +++ b/LeCountdown/Model/LeCountdown.xcdatamodeld/LeCountdown.0.6.3.xcdatamodel/contents @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LeCountdown/Model/Model+Extensions.swift b/LeCountdown/Model/Model+Extensions.swift index 8b23666..03a5295 100644 --- a/LeCountdown/Model/Model+Extensions.swift +++ b/LeCountdown/Model/Model+Extensions.swift @@ -17,11 +17,22 @@ extension AbstractSoundTimer { } return [] } - + func setSounds(_ sounds: Set) { self.soundList = sounds.stringRepresentation } + var confirmationSounds: Set { + if let confirmationSoundList { + return Set(confirmationSoundList.enumItems()) + } + return [] + } + + func setConfirmationSounds(_ sounds: Set) { + self.confirmationSoundList = sounds.stringRepresentation + } + var someSound: Sound { var sounds = self.sounds @@ -39,11 +50,7 @@ extension AbstractSoundTimer { return Sound.default } - - var soundName: String { - return self.someSound.fileName - } - + } extension Stopwatch { diff --git a/LeCountdown/Sound/Sound.swift b/LeCountdown/Sound/Sound.swift index 0b8149a..9783256 100644 --- a/LeCountdown/Sound/Sound.swift +++ b/LeCountdown/Sound/Sound.swift @@ -23,37 +23,50 @@ class SoundCatalog { } func sounds(for playlist: Playlist) -> [Sound] { - return self._soundsByPlaylist[playlist] ?? [] + switch playlist { + case .shorts: + return [.FF_SH_bowl_drone_tap_hold_E, .FF_SH_bowl_drone_tapping_C, .EX_ATSM_20_Inch_Highwall_Bowl_Hit_Ring_Ab, .ESM_Ambient_Game_Menu_Soft_Wood] + default: + return self._soundsByPlaylist[playlist] ?? [] + } } } +enum Catalog { + case ring + case confirmation + + var playlists: [Playlist] { + switch self { + case .ring: return [.nature, .stephanBodzin, .relax] + case .confirmation: return [.shorts] + } + } +} + enum Playlist: Int, CaseIterable, Identifiable, Localized { var id: Int { return self.rawValue } case custom case nature -// case fun case stephanBodzin case relax - - static var selectable: [Playlist] { - return Playlist.allCases.filter { $0 != .custom } - } + case shorts var localizedString: String { switch self { case .nature: return NSLocalizedString("Nature", comment: "") -// case .fun: -// return NSLocalizedString("Fun", comment: "") case .stephanBodzin: return "Stephan Bodzin" case .custom: return NSLocalizedString("Custom", comment: "") case .relax: return NSLocalizedString("Relax", comment: "") + case .shorts: + return NSLocalizedString("Confirmation", comment: "") } } @@ -75,12 +88,12 @@ enum Sound: Int, CaseIterable, Identifiable, Localized { // Relax case FF_SH_bowl_drone_tapping_C case FF_SH_bowl_drone_tap_hold_E + case EX_ATSM_20_Inch_Highwall_Bowl_Hit_Ring_Ab case EX_ATSM_Koshi_Chimes_Aria_Tuning_Texture_Longer_Dm + case EX_ATSM_140_Koshi_Chimes_Aria_Tuning_Loop_Wondering_Am case EX_ATSM_Bell_Binaural_Flam_Eb case EX_ATSM_160_Metal_Tonal_Percussion_Sansula_Loop_Call_Am case EX_ATSM_125_Metal_Percussion_Wing_Loop_Chimey_Dm - case EX_ATSM_140_Koshi_Chimes_Aria_Tuning_Loop_Wondering_Am - case EX_ATSM_20_Inch_Highwall_Bowl_Hit_Ring_Ab // Nature case rain_soft case stream1 @@ -91,6 +104,8 @@ enum Sound: Int, CaseIterable, Identifiable, Localized { case deciduousForestMorning case wetland case riparianZone + // Shorts + case ESM_Ambient_Game_Menu_Soft_Wood static var `default`: Sound { .sbSEM_Synths_Loop4_Nothing_Like_You } @@ -120,6 +135,7 @@ enum Sound: Int, CaseIterable, Identifiable, Localized { case .deciduousForestMorning: return "Forest morning 2" case .wetland: return "Wetland" case .riparianZone: return "Riparian Zone" + case .ESM_Ambient_Game_Menu_Soft_Wood: return "Wood percussion" } } @@ -149,6 +165,7 @@ enum Sound: Int, CaseIterable, Identifiable, Localized { case .deciduousForestMorning: return "QP01 0050 Deciduous forest morning songbirds robin.wav" case .wetland: return "QP01 0096 Wetland lake early morning.wav" case .riparianZone: return "QP01 0096 Wetland lake early morning.wav" + case .ESM_Ambient_Game_Menu_Soft_Wood: return "ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav" } } @@ -160,6 +177,8 @@ enum Sound: Int, CaseIterable, Identifiable, Localized { return .relax case .rain_soft, .stream1, .stream2, .surf1, .crickets, .tropicalForestMorning, .deciduousForestMorning, .wetland, .riparianZone: return .nature + case .ESM_Ambient_Game_Menu_Soft_Wood: + return .shorts } } diff --git a/LeCountdown/Sound_Assets/ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav b/LeCountdown/Sound_Assets/Shorts/ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav similarity index 100% rename from LeCountdown/Sound_Assets/ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav rename to LeCountdown/Sound_Assets/Shorts/ESM_Ambient_Game_Menu_Soft_Wood_Confirm_1_Notification_Button_Settings_UI.wav diff --git a/LeCountdown/Sound_Assets/ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav b/LeCountdown/Sound_Assets/Shorts/ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav similarity index 100% rename from LeCountdown/Sound_Assets/ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav rename to LeCountdown/Sound_Assets/Shorts/ESM_MVG_fx_ui_one_shot_cancel_clicky_reverb_digital_long.wav diff --git a/LeCountdown/Sound_Assets/ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav b/LeCountdown/Sound_Assets/Shorts/ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav similarity index 100% rename from LeCountdown/Sound_Assets/ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav rename to LeCountdown/Sound_Assets/Shorts/ESM_One_Shot_FX_Interface_Glitch_Spaceship_Console_18_Interface_Button_Alert_System_Cm.wav diff --git a/LeCountdown/Sound_Assets/MRKRSTPHR_synth_one_shot_bleep_G.wav b/LeCountdown/Sound_Assets/Shorts/MRKRSTPHR_synth_one_shot_bleep_G.wav similarity index 100% rename from LeCountdown/Sound_Assets/MRKRSTPHR_synth_one_shot_bleep_G.wav rename to LeCountdown/Sound_Assets/Shorts/MRKRSTPHR_synth_one_shot_bleep_G.wav diff --git a/LeCountdown/Sound_Assets/PVP_Stab_Oneshot_Bleep_Em.wav b/LeCountdown/Sound_Assets/Shorts/PVP_Stab_Oneshot_Bleep_Em.wav similarity index 100% rename from LeCountdown/Sound_Assets/PVP_Stab_Oneshot_Bleep_Em.wav rename to LeCountdown/Sound_Assets/Shorts/PVP_Stab_Oneshot_Bleep_Em.wav diff --git a/LeCountdown/TimerRouter.swift b/LeCountdown/TimerRouter.swift index c734d68..335ea18 100644 --- a/LeCountdown/TimerRouter.swift +++ b/LeCountdown/TimerRouter.swift @@ -45,17 +45,6 @@ class TimerRouter { } - static func stopTimer(timer: AbstractTimer) { - switch timer { - case let countdown as Countdown: - Conductor.maestro.cancelCountdown(id: countdown.stringId) - case let stopwatch as Stopwatch: - self._stopStopwatch(stopwatch) - default: - print("missing launcher for \(self)") - } - } - fileprivate static func _launchCountdown(_ countdown: Countdown, handler: @escaping (Result) -> Void) { UNUserNotificationCenter.current().getNotificationSettings { settings in diff --git a/LeCountdown/Views/Countdown/CountdownFormView.swift b/LeCountdown/Views/Countdown/CountdownFormView.swift index 4655c49..16d9138 100644 --- a/LeCountdown/Views/Countdown/CountdownFormView.swift +++ b/LeCountdown/Views/Countdown/CountdownFormView.swift @@ -19,6 +19,7 @@ struct CountdownFormView : View { @FocusState private var focusedField: CountdownField? @EnvironmentObject var model: TimerModel + @EnvironmentObject var confirmationModel: TimerModel var secondsBinding: Binding var minutesBinding: Binding @@ -55,9 +56,9 @@ struct CountdownFormView : View { } SoundFormView( + model: self.model, imageBinding: imageBinding, repeatCountBinding: repeatCountBinding) - .environmentObject(self.model) }.toolbar { ToolbarItemGroup(placement: .keyboard) { Button { diff --git a/LeCountdown/Views/Countdown/NewCountdownView.swift b/LeCountdown/Views/Countdown/NewCountdownView.swift index 7b53af6..4958089 100644 --- a/LeCountdown/Views/Countdown/NewCountdownView.swift +++ b/LeCountdown/Views/Countdown/NewCountdownView.swift @@ -193,7 +193,7 @@ struct CountdownEditView : View { } self.model.group = preset.intervalGroup - self.model.sounds = preset.sound + self.model.soundModel.sounds = preset.sound } @@ -214,7 +214,7 @@ struct CountdownEditView : View { self.nameString = name } - self.model.sounds = countdown.sounds + self.model.soundModel.sounds = countdown.sounds // if let sound = Sound(rawValue: Int(countdown.sound)) { // self.sound = sound @@ -262,9 +262,9 @@ struct CountdownEditView : View { } cd.image = self.image.rawValue - cd.setSounds(self.model.sounds) + cd.setSounds(self.model.soundModel.sounds) + cd.setConfirmationSounds(self.model.confirmationSoundModel.sounds) -// cd.setPlaylists(self.playlists) cd.repeatCount = self.soundRepeatCount if !self.nameString.isEmpty { diff --git a/LeCountdown/Views/Reusable/SoundFormView.swift b/LeCountdown/Views/Reusable/SoundFormView.swift index b6c7305..d99c918 100644 --- a/LeCountdown/Views/Reusable/SoundFormView.swift +++ b/LeCountdown/Views/Reusable/SoundFormView.swift @@ -9,9 +9,9 @@ import SwiftUI struct SoundFormView : View { + var model: TimerModel + var imageBinding: Binding - - @EnvironmentObject var model: TimerModel var repeatCountBinding: Binding? = nil var optionalSound: Binding? = nil @@ -25,26 +25,13 @@ struct SoundFormView : View { Section(header: Text("Properties")) { if self.optionalSound != nil { - Toggle("Play sound on end", isOn: optionalSound!) - if self.optionalSound?.wrappedValue == true { NavigationLink { - - PlaylistsView().environmentObject(self.model) - -// SoundSelectionView(playlistBinding: playlistBinding) + PlaylistsView(model: self.model.soundModel, catalog: .ring) } label: { Text("Sound") } - -// Picker(selection: soundBinding) { -// ForEach(Sound.allCases) { sound in -// Text(sound.localizedString).tag(sound) -// } -// } label: { -// Text("Sound") -// } } } else { @@ -57,17 +44,21 @@ struct SoundFormView : View { // } } - NavigationLink { - NavigationStack { - PlaylistsView().environmentObject(self.model) - } - } label: { - HStack { - Text("Sound") - Spacer() - Text(self.model.soundSelection) - } - } + SoundLinkView(soundModel: self.model.soundModel, + catalog: .ring, + title: "Sound") + +// NavigationLink { +// NavigationStack { +// PlaylistsView(model: self.model.soundModel, catalog: .ring) +// } +// } label: { +// HStack { +// Text("Sound") +// Spacer() +// Text(self.model.soundModel.soundSelection) +// } +// } if self.repeatCountBinding != nil { Picker("Repeat Count", selection: self.repeatCountBinding!) { @@ -79,28 +70,23 @@ struct SoundFormView : View { } } - } - -// Section(header: Text("Background")) { -// -// Button { -// self.imageSelectionSheetShown = true + SoundLinkView(soundModel: self.model.confirmationSoundModel, + catalog: .confirmation, + title: "Confirmation Sound") + +// NavigationLink { +// NavigationStack { +// PlaylistsView(model: self.model.confirmationSoundModel, catalog: .confirmation) +// } // } label: { -// Group { -// if let image = self.imageBinding.wrappedValue { -// Image(image.rawValue).resizable() -// } else { -// Image(imageBinding.wrappedValue.rawValue).resizable() -// } +// HStack { +// Text("Confirmation Sound") +// Spacer() +// Text(self.confirmationModel.soundSelection) // } -// .font(Font.system(size: 90.0)) -// .aspectRatio(1, contentMode: .fit) -// .frame(width: 100.0, height: 100.0) -// .cornerRadius(20.0) -// // } -// -// } + + } }.sheet(isPresented: self.$imageSelectionSheetShown) { ImageSelectionView(showBinding: self.$imageSelectionSheetShown, imageBinding: self.imageBinding) } @@ -108,14 +94,32 @@ struct SoundFormView : View { } +struct SoundLinkView: View { + + @StateObject var soundModel: SoundModel + var catalog: Catalog + var title: String + + var body: some View { + NavigationLink { + NavigationStack { + PlaylistsView(model: self.soundModel, + catalog: self.catalog) + } + } label: { + LabeledContent(self.title, value: self.soundModel.soundSelection) + } + } + +} + struct SoundImageFormView_Previews: PreviewProvider { static var previews: some View { Form { - SoundFormView( + SoundFormView(model: TimerModel(), imageBinding: .constant(.pic1), repeatCountBinding: .constant(2)) - .environmentObject(TimerModel()) } } } diff --git a/LeCountdown/Views/Reusable/SoundSelectionView.swift b/LeCountdown/Views/Reusable/SoundSelectionView.swift index 3652ec1..0a81338 100644 --- a/LeCountdown/Views/Reusable/SoundSelectionView.swift +++ b/LeCountdown/Views/Reusable/SoundSelectionView.swift @@ -9,14 +9,15 @@ import SwiftUI struct PlaylistsView: View { - @EnvironmentObject var model: TimerModel + @StateObject var model: SoundModel + + var catalog: Catalog var body: some View { Form { - ForEach(Playlist.selectable) { playlist in - PlaylistSectionView(playlist: playlist) - .environmentObject(self.model) + ForEach(catalog.playlists) { playlist in + PlaylistSectionView(model: self.model, playlist: playlist) } } .navigationTitle("Sounds") @@ -26,7 +27,7 @@ struct PlaylistsView: View { struct PlaylistSectionView: View { - @EnvironmentObject var model: TimerModel + @StateObject var model: SoundModel var playlist: Playlist @@ -101,15 +102,6 @@ struct RightAlignToggleRow: View { } }.onChange(of: self.selected, perform: handleSelection) -// if let keyPath { -// -// -// Toggle(item[keyPath: keyPath], isOn: $selected) -// .onChange(of: self.selected, perform: handleSelection) -// } else { -// Toggle(item.localizedString, isOn: $selected) -// .onChange(of: self.selected, perform: handleSelection) -// } } } @@ -129,66 +121,10 @@ struct ImageToggleRow: View { } -//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 { - -// 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) -// } -// }.navigationTitle("Sounds") - - } - - } -} - struct PlaylistsView_Previews: PreviewProvider { static var previews: some View { - PlaylistsView() - .environmentObject(TimerModel()) + PlaylistsView(model: SoundModel(), catalog: .ring) } } @@ -196,8 +132,7 @@ struct PlaylistSectionView_Previews: PreviewProvider { static var previews: some View { Form { - PlaylistSectionView(playlist: .stephanBodzin) - .environmentObject(TimerModel()) + PlaylistSectionView(model: SoundModel(), playlist: .stephanBodzin) } } } diff --git a/LeCountdown/Views/Reusable/TimerModel.swift b/LeCountdown/Views/Reusable/TimerModel.swift index 0265775..0288683 100644 --- a/LeCountdown/Views/Reusable/TimerModel.swift +++ b/LeCountdown/Views/Reusable/TimerModel.swift @@ -13,7 +13,17 @@ protocol SoundHolder { func selectPlaylist(_ playlist: Playlist, selected: Bool) } -class TimerModel : ObservableObject, SoundHolder { +class TimerModel: ObservableObject { + + @Published var soundModel: SoundModel = SoundModel() + @Published var confirmationSoundModel: SoundModel = SoundModel() + + @Published var group: CountdownIntervalGroup = + CountdownIntervalGroup(repeatCount: 0, intervals: []) + +} + +class SoundModel: ObservableObject, SoundHolder { @Published var playlists: Set = [] @Published var sounds: Set = [] { @@ -21,9 +31,7 @@ class TimerModel : ObservableObject, SoundHolder { self._selectPlaylists() } } - @Published var group: CountdownIntervalGroup = - CountdownIntervalGroup(repeatCount: 0, intervals: []) - + var soundSelection: String { if !sounds.isEmpty { if sounds.count == 1 { @@ -56,13 +64,11 @@ class TimerModel : ObservableObject, SoundHolder { // MARK: - SoundHolder func selectSound(_ sound: Sound, selected: Bool) { - if selected { self.sounds.insert(sound) } else { self.sounds.remove(sound) } - self._togglePlaylist(sound.playlist) } @@ -99,12 +105,4 @@ class TimerModel : ObservableObject, SoundHolder { } -// func isSelected(sound: Sound) -> Bool { -// self.sounds.contains(sound) -// } -// -// func isSelected(playlist: Playlist) -> Bool { -// self.playlists.contains(playlist) -// } - } diff --git a/LeCountdown/Views/Stopwatch/StopwatchFormView.swift b/LeCountdown/Views/Stopwatch/StopwatchFormView.swift index 8573685..02e88d9 100644 --- a/LeCountdown/Views/Stopwatch/StopwatchFormView.swift +++ b/LeCountdown/Views/Stopwatch/StopwatchFormView.swift @@ -34,9 +34,9 @@ struct StopwatchFormView: View { } } - SoundFormView(imageBinding: imageBinding, - optionalSound: playSoundBinding) - .environmentObject(self.model) + SoundFormView(model: self.model, + imageBinding: imageBinding, + optionalSound: playSoundBinding) }.toolbar { ToolbarItemGroup(placement: .keyboard) { diff --git a/LeCountdown/fr.lproj/Localizable.strings b/LeCountdown/fr.lproj/Localizable.strings index 1e13444..4d777e4 100644 --- a/LeCountdown/fr.lproj/Localizable.strings +++ b/LeCountdown/fr.lproj/Localizable.strings @@ -254,3 +254,4 @@ "Play confirmation sound" = "Jouer son de confirmation"; "Play cancellation sound" = "Jouer son d'annulation"; "Contact us" = "Contactez-nous"; +"Confirmation" = "Confirmation";