From 2e9bda7835e9974081420385abf341689d2b550f Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 13 Oct 2022 14:02:15 +0800 Subject: [PATCH 1/4] Dont instantiate TabView with zero items Seems this prevents the crash when the TabView tries to set its index to 0/0. --- .../App/Views/AudioPlayer/MiniPlayer.swift | 40 ++++++++++--------- .../AudioSession/AudioController.swift | 4 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index e8c5aa8a5..5ffd9020a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -153,27 +153,29 @@ var audioCards: some View { ZStack { let textItems = self.audioController.textItems ?? [] - TabView(selection: $tabIndex) { - ForEach(0 ..< textItems.count, id: \.self) { id in - SpeechCard(id: id) - .tag(id) - } - } - .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) - .onChange(of: tabIndex, perform: { index in - if index != audioController.currentAudioIndex, index < (audioController.textItems?.count ?? 0) { - audioController.seek(toUtterance: index) - } - }) - .onChange(of: audioController.currentAudioIndex, perform: { index in - if index >= textItems.count { - return + if textItems.count > 0 { + TabView(selection: $tabIndex) { + ForEach(0 ..< textItems.count, id: \.self) { id in + SpeechCard(id: id) + .tag(id) + } } + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) + .onChange(of: tabIndex, perform: { index in + if index != audioController.currentAudioIndex, index < (audioController.textItems?.count ?? 0) { + audioController.seek(toUtterance: index) + } + }) + .onChange(of: audioController.currentAudioIndex, perform: { index in + if index >= textItems.count { + return + } - if self.audioController.state != .reachedEnd { - tabIndex = index - } - }) + if self.audioController.state != .reachedEnd { + tabIndex = index + } + }) + } if audioController.state == .reachedEnd { // If we have reached the end display a replay button with an overlay behind diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index e1598bc75..40a40b773 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -207,6 +207,8 @@ @Published public var durationString: String? @Published public var voiceList: [(name: String, key: String, category: VoiceCategory, selected: Bool)]? + @Published public var textItems: [String]? + let dataService: DataService var timer: Timer? @@ -480,8 +482,6 @@ let body: String } - public var textItems: [String]? - func setTextItems() { if let document = self.document { textItems = document.utterances.map { utterance in From bafe1bd3388f70f4eddb2d460368faf8b22e4456 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 13 Oct 2022 15:04:57 +0800 Subject: [PATCH 2/4] Ensure the audio index is updated when items are changed --- .../Sources/App/Views/AudioPlayer/MiniPlayer.swift | 9 ++++----- .../Sources/Services/AudioSession/AudioController.swift | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index 5ffd9020a..cc7932915 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -434,7 +434,7 @@ Button(action: { audioController.currentVoice = voice.key self.showVoiceSheet = false - }) { + }, label: { HStack { Text(voice.name) @@ -445,8 +445,7 @@ } } .contentShape(Rectangle()) - } - .buttonStyle(PlainButtonStyle()) + }).buttonStyle(PlainButtonStyle()) } } .padding(.top, 32) @@ -455,11 +454,11 @@ } .navigationBarTitle("Voice") .navigationBarTitleDisplayMode(.inline) - .navigationBarItems(leading: Button(action: { self.showVoiceSheet = false }) { + .navigationBarItems(leading: Button(action: { self.showVoiceSheet = false }, label: { Image(systemName: "chevron.backward") .font(.appNavbarIcon) .tint(.appGrayTextContrast) - }) + })) } } diff --git a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift index 40a40b773..fdb4a686a 100644 --- a/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift +++ b/apple/OmnivoreKit/Sources/Services/AudioSession/AudioController.swift @@ -268,6 +268,7 @@ timeElapsed = 0 duration = 1 durations = nil + currentAudioIndex = 0 if let stoppedId = stoppedId { EventTracker.track( @@ -491,6 +492,7 @@ } return "" } + currentAudioIndex = 0 } else { textItems = nil } @@ -940,7 +942,7 @@ let str = String(decoding: data, as: UTF8.self) print("result speech file: ", str) - let document = try? JSONDecoder().decode(SpeechDocument.self, from: data) + document = try? JSONDecoder().decode(SpeechDocument.self, from: data) // Cache the file - if it exists if let document = document { From fe1f4ee51d7ef98efa501d9bc212d4ba1f77620f Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 13 Oct 2022 15:20:28 +0800 Subject: [PATCH 3/4] Update the scrubber max value when items change --- .../Sources/App/Views/AudioPlayer/MiniPlayer.swift | 2 +- .../Sources/App/Views/AudioPlayer/ScrubberView.swift | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index cc7932915..e0a1626ee 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -281,7 +281,7 @@ Group { ScrubberView(value: $audioController.timeElapsed, - minValue: 0, maxValue: self.audioController.duration, + maxValue: $audioController.duration, onEditingChanged: { scrubStarted in if scrubStarted { self.audioController.scrubState = .scrubStarted diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift index a8ddde418..e551eca40 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/ScrubberView.swift @@ -7,20 +7,18 @@ typealias UIViewType = UISlider @Binding var value: Double - var minValue: Double - var maxValue: Double + @Binding var maxValue: Double var onEditingChanged: (Bool) -> Void - init(value: Binding, minValue: Double, maxValue: Double, onEditingChanged: @escaping (Bool) -> Void) { + init(value: Binding, maxValue: Binding, onEditingChanged: @escaping (Bool) -> Void) { self._value = value - self.minValue = minValue - self.maxValue = maxValue + self._maxValue = maxValue self.onEditingChanged = onEditingChanged } func makeUIView(context: Context) -> UISlider { let slider = UISlider(frame: .zero) - slider.maximumValue = Float(minValue) + slider.minimumValue = Float(0.0) slider.maximumValue = Float(maxValue) let tintColor = UIColor(Color.appCtaYellow) @@ -43,6 +41,7 @@ func updateUIView(_ uiView: UISlider, context _: Context) { uiView.value = Float(value) + uiView.maximumValue = Float(maxValue) } func makeCoordinator() -> Coordinator { From d9c91c313719f7a91563f48fea9cf1c2b1322584 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 13 Oct 2022 15:37:34 +0800 Subject: [PATCH 4/4] Reset tabIndex when state is reset to loading We track the state of the current index in two places: the audio controller, which updates as audio as played, the tabView has its own binding of the current index, this is to allow scrolling. When the audio item being played changes, we need to reset the state of the tabIndex. We do this by watching for state loading which occurs when new audio is loaded. --- .../Sources/App/Views/AudioPlayer/MiniPlayer.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift index e0a1626ee..875555c23 100644 --- a/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift +++ b/apple/OmnivoreKit/Sources/App/Views/AudioPlayer/MiniPlayer.swift @@ -361,6 +361,12 @@ .shadow(color: expanded ? .clear : .gray.opacity(0.33), radius: 8, x: 0, y: 4) .mask(Rectangle().padding(.top, -20)) ) + .onChange(of: audioController.state, perform: { state in + // Reset the tabIndex when we load a new audio item + if state == .loading { + tabIndex = 0 + } + }) .onTapGesture { withAnimation(.easeIn(duration: 0.08)) { expanded = true } }.sheet(isPresented: $showVoiceSheet) {