diff --git a/LeCountdown/AppDelegate.swift b/LeCountdown/AppDelegate.swift index bf81cbe..fb87336 100644 --- a/LeCountdown/AppDelegate.swift +++ b/LeCountdown/AppDelegate.swift @@ -24,12 +24,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { print("didReceive notification") - let notificationId = response.notification.request.identifier - let components = notificationId.components(separatedBy: CountdownScheduler.notificationIdSeparator) - - if components.count == 2 { - Conductor.maestro.cancelCountdown(id: components[0]) - } + let timerId = self._timerId(notificationId: response.notification.request.identifier) + Conductor.maestro.cancelCountdown(id: timerId) } @@ -37,7 +33,19 @@ extension AppDelegate: UNUserNotificationCenterDelegate { print("willPresent notification") completionHandler([.banner]) - Conductor.maestro.notifyUser(countdownId: notification.request.identifier) + let timerId = self._timerId(notificationId: notification.request.identifier) + Conductor.maestro.notifyUser(countdownId: timerId) } + fileprivate func _timerId(notificationId: String) -> String { + 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 1d3f6bd..e81b195 100644 --- a/LeCountdown/Conductor.swift +++ b/LeCountdown/Conductor.swift @@ -52,7 +52,6 @@ class Conductor: ObservableObject { fileprivate var _cleanupTimers: [String : Timer] = [:] func removeLiveTimer(id: String) { - self.stopSoundIfPossible() self.liveTimers.removeAll(where: { $0.id == id }) } @@ -64,7 +63,9 @@ class Conductor: ObservableObject { // add countdown if not present for liveCountdown in liveCountdowns { - if self.liveTimers.first(where: { $0.id == liveCountdown.id }) == nil { + if let livetimer = self.liveTimers.first(where: { $0.id == liveCountdown.id }) { + self.liveTimers.replace([livetimer], with: [liveCountdown]) + } else { self.liveTimers.append(liveCountdown) } } @@ -180,7 +181,8 @@ class Conductor: ObservableObject { let context = PersistenceController.shared.container.viewContext var coolSound: Sound? = nil - switch context.object(stringId: timerId) { + let timer = context.object(stringId: timerId) + switch timer { case let cd as Countdown: coolSound = cd.coolSound case let sw as Stopwatch: @@ -199,6 +201,8 @@ class Conductor: ObservableObject { print("error = \(error)") // TODO: manage error } + } else { + print("No sound to play!") } } diff --git a/LeCountdown/CountdownScheduler.swift b/LeCountdown/CountdownScheduler.swift index a34d32c..fef8779 100644 --- a/LeCountdown/CountdownScheduler.swift +++ b/LeCountdown/CountdownScheduler.swift @@ -42,7 +42,11 @@ class CountdownScheduler { } content.body = body - content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: countdown.soundName)) + + let sound = countdown.soundName + print("Selected sound = \(sound)") + content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: sound)) + content.interruptionLevel = .critical content.relevanceScore = 1.0 diff --git a/LeCountdown/Model/Model+Extensions.swift b/LeCountdown/Model/Model+Extensions.swift index ec8bee1..d832d39 100644 --- a/LeCountdown/Model/Model+Extensions.swift +++ b/LeCountdown/Model/Model+Extensions.swift @@ -46,9 +46,9 @@ extension AbstractTimer { extension AbstractSoundTimer { - var sounds: [Sound] { + var sounds: Set { if let soundList { - return soundList.enumItems() + return Set(soundList.enumItems()) } return [] } @@ -60,7 +60,7 @@ extension AbstractSoundTimer { // return [] // } - func setSounds(_ sounds: [Sound]) { + func setSounds(_ sounds: Set) { self.soundList = sounds.stringRepresentation } @@ -160,7 +160,7 @@ extension String { } -extension Array where Element : RawRepresentable { +extension Sequence where Element : RawRepresentable { var stringRepresentation: String { let ids = self.compactMap { formatter.string(from: NSNumber(value: $0.rawValue)) } diff --git a/LeCountdown/Views/Alarm/NewAlarmView.swift b/LeCountdown/Views/Alarm/NewAlarmView.swift index 3cbf148..105a750 100644 --- a/LeCountdown/Views/Alarm/NewAlarmView.swift +++ b/LeCountdown/Views/Alarm/NewAlarmView.swift @@ -36,9 +36,6 @@ struct AlarmEditView: View { @State var nameString: String = "" - @State var sounds: [Sound] = [.trainhorn] - @State var playlists: [Playlist] = [] - @State var soundRepeatCount: Int16 = 0 @State var image: CoolPic = .pic1 @@ -152,7 +149,7 @@ struct AlarmEditView: View { self.nameString = name } - self.sounds = alarm.sounds +// self.sounds = alarm.sounds // self.playlists = alarm.playlists if let image = alarm.image, let coolpic = CoolPic(rawValue: image) { @@ -183,7 +180,7 @@ struct AlarmEditView: View { a.fireDate = self.time a.image = self.image.rawValue - a.setSounds(self.sounds) +// a.setSounds(self.sounds) // a.setPlaylists(self.playlists) a.repeatCount = self.soundRepeatCount diff --git a/LeCountdown/Views/Components/SoundImageFormView.swift b/LeCountdown/Views/Components/SoundImageFormView.swift index ea0f489..47859ce 100644 --- a/LeCountdown/Views/Components/SoundImageFormView.swift +++ b/LeCountdown/Views/Components/SoundImageFormView.swift @@ -60,7 +60,11 @@ struct SoundImageFormView : View { NavigationLink { PlaylistsView().environmentObject(self.model) } label: { - Text("Sound") + HStack { + Text("Sound") + Spacer() + Text(self.model.soundSelection) + } } Picker("Repeat Count", selection: self.repeatCountBinding!) { diff --git a/LeCountdown/Views/Components/SoundSelectionView.swift b/LeCountdown/Views/Components/SoundSelectionView.swift index d5630ee..ffb3a5f 100644 --- a/LeCountdown/Views/Components/SoundSelectionView.swift +++ b/LeCountdown/Views/Components/SoundSelectionView.swift @@ -18,7 +18,7 @@ struct PlaylistsView: View { PlaylistSectionView(playlist: playlist) .environmentObject(self.model) } - } + }.navigationTitle("Sounds") } @@ -34,7 +34,7 @@ struct PlaylistSectionView: View { Section { let sounds = SoundCatalog.main.sounds(for: self.playlist) - List(sounds) { sound in + ForEach(sounds) { sound in ToggleRow(item: sound, selected: self.model.binding(sound: sound)) { selected in self.model.selectSound(sound, selected: selected) } @@ -116,7 +116,7 @@ struct SoundSelectionView: View { } struct PlaylistsView_Previews: PreviewProvider { - + static func soundBinding(sound: Sound) -> Binding { return .constant(true) } @@ -126,23 +126,16 @@ struct PlaylistsView_Previews: PreviewProvider { static var previews: some View { PlaylistsView() - .environmentObject(SoundHolderPlaceholder()) + .environmentObject(TimerModel()) } } 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 { Form { PlaylistSectionView(playlist: .stephanBodzin) - .environmentObject(SoundHolderPlaceholder()) + .environmentObject(TimerModel()) } } } diff --git a/LeCountdown/Views/Components/TimerModel.swift b/LeCountdown/Views/Components/TimerModel.swift index 06e7320..526fa9a 100644 --- a/LeCountdown/Views/Components/TimerModel.swift +++ b/LeCountdown/Views/Components/TimerModel.swift @@ -21,6 +21,17 @@ class TimerModel : ObservableObject, SoundHolder { @Published var playlists: Set = [] @Published var sounds: Set = [] + var soundSelection: String { + if !sounds.isEmpty { + if sounds.count == 1 { + return sounds.first!.localizedString + } else { + return "\(sounds.count) \(NSLocalizedString("sounds", comment: "sounds"))" + } + } + return "" + } + func binding(sound: Sound) -> Binding { return Binding(get: { return self.sounds.contains(sound) diff --git a/LeCountdown/Views/Countdown/NewCountdownView.swift b/LeCountdown/Views/Countdown/NewCountdownView.swift index 84cf4a2..49d6e6a 100644 --- a/LeCountdown/Views/Countdown/NewCountdownView.swift +++ b/LeCountdown/Views/Countdown/NewCountdownView.swift @@ -38,9 +38,6 @@ struct CountdownEditView : View { @State var minutesString: String = "" @State var nameString: String = "" - @State var playlists: Set = [] - @State var sounds: Set = [] - @State var soundRepeatCount: Int16 = 0 @State var image: CoolPic = .pic1 @@ -140,26 +137,6 @@ 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() { @@ -170,6 +147,8 @@ struct CountdownEditView : View { if let countdown { + print("self.soundList = \(countdown.soundList)") + let minutes = Int(countdown.duration / 60.0) let seconds = countdown.duration - Double(minutes * 60) @@ -185,7 +164,7 @@ struct CountdownEditView : View { self.nameString = name } -// self.sounds = countdown.sounds + self.model.sounds = countdown.sounds // if let sound = Sound(rawValue: Int(countdown.sound)) { // self.sound = sound @@ -230,7 +209,8 @@ struct CountdownEditView : View { } cd.image = self.image.rawValue -// cd.setSounds(self.sounds) + cd.setSounds(self.model.sounds) + // cd.setPlaylists(self.playlists) cd.repeatCount = self.soundRepeatCount