From f54eeaee947e78b87b932b40b31014a54830a48d Mon Sep 17 00:00:00 2001 From: Dhruv Harsora Date: Fri, 24 Oct 2025 12:53:23 +0530 Subject: [PATCH] fix(UNT-T39468): fix stop all player issue --- .../com/audiowaveform/AudioWaveformModule.kt | 27 +++++++++++++++++-- .../src/main/java/com/audiowaveform/Utils.kt | 1 + example/ios/Podfile.lock | 22 +++++++-------- ios/AudioWaveform.swift | 16 +++++++++-- ios/EventEmitter.swift | 2 +- ios/Utils.swift | 1 + src/components/Waveform/Waveform.tsx | 14 ++++++++++ src/constants/index.ts | 1 + src/hooks/useAudioPlayer.tsx | 8 ++++++ 9 files changed, 76 insertions(+), 16 deletions(-) diff --git a/android/src/main/java/com/audiowaveform/AudioWaveformModule.kt b/android/src/main/java/com/audiowaveform/AudioWaveformModule.kt index 6e2f7db..6d4265c 100644 --- a/android/src/main/java/com/audiowaveform/AudioWaveformModule.kt +++ b/android/src/main/java/com/audiowaveform/AudioWaveformModule.kt @@ -236,7 +236,19 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav player -> player?.stop() } audioPlayers.clear() - promise.resolve(true) + + if (audioPlayers.isEmpty()) { + val args: WritableMap = Arguments.createMap() + args.putBoolean("clearedPlayers", true) + + reactApplicationContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + ?.emit(Constants.onResetAllWaveforms, args) + + promise.resolve(true) + } else { + promise.reject("REMOVE_FAILED", "Failed to clear all audio players") + } } catch (err: Exception) { promise.reject("stopAllPlayers Error", "Error while stopping all players") } @@ -249,7 +261,18 @@ class AudioWaveformModule(context: ReactApplicationContext): ReactContextBaseJav extractor -> extractor?.forceStop() } extractors.clear() - promise.resolve(true) + if (audioPlayers.isEmpty()) { + val args: WritableMap = Arguments.createMap() + args.putBoolean("clearedExtractors", true) + + reactApplicationContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + ?.emit(Constants.onResetAllWaveforms, args) + + promise.resolve(true) + } else { + promise.reject("REMOVE_FAILED", "Failed to clear all audio players") + } } catch (err: Exception) { promise.reject("stopAllExtractors Error", "Error while stopping all extractors") } diff --git a/android/src/main/java/com/audiowaveform/Utils.kt b/android/src/main/java/com/audiowaveform/Utils.kt index 852b606..1bc87e1 100644 --- a/android/src/main/java/com/audiowaveform/Utils.kt +++ b/android/src/main/java/com/audiowaveform/Utils.kt @@ -44,6 +44,7 @@ object Constants { const val bitRate = "bitRate" const val sampleRate = "sampleRate" const val speed = "speed" + const val onResetAllWaveforms = "onResetAllWaveforms" } enum class FinishMode(val value:Int) { diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 3445956..8f8e89a 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1222,7 +1222,7 @@ PODS: - React-jsiexecutor - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - - react-native-audio-waveform (2.1.5): + - react-native-audio-waveform (2.1.6): - DoubleConversion - glog - hermes-engine @@ -1243,7 +1243,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-safe-area-context (5.2.0): + - react-native-safe-area-context (5.6.1): - DoubleConversion - glog - hermes-engine @@ -1256,8 +1256,8 @@ PODS: - React-featureflags - React-graphics - React-ImageManager - - react-native-safe-area-context/common (= 5.2.0) - - react-native-safe-area-context/fabric (= 5.2.0) + - react-native-safe-area-context/common (= 5.6.1) + - react-native-safe-area-context/fabric (= 5.6.1) - React-NativeModulesApple - React-RCTFabric - React-rendererdebug @@ -1266,7 +1266,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-safe-area-context/common (5.2.0): + - react-native-safe-area-context/common (5.6.1): - DoubleConversion - glog - hermes-engine @@ -1287,7 +1287,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-safe-area-context/fabric (5.2.0): + - react-native-safe-area-context/fabric (5.6.1): - DoubleConversion - glog - hermes-engine @@ -1606,7 +1606,7 @@ PODS: - SDWebImageWebPCoder (~> 0.8.4) - RNFS (2.20.0): - React-Core - - RNGestureHandler (2.23.1): + - RNGestureHandler (2.25.0): - DoubleConversion - glog - hermes-engine @@ -1904,8 +1904,8 @@ SPEC CHECKSUMS: React-logger: 9a0c4e1e41cd640ac49d69aacadab783f7e0096b React-Mapbuffer: 6993c785c22a170c02489bc78ed207814cbd700f React-microtasksnativemodule: 19230cd0933df6f6dc1336c9a9edc382d62638ae - react-native-audio-waveform: 76e2a504df52d7b124699cc35fec3491303577a3 - react-native-safe-area-context: 9c33120e9eac7741a5364cc2d9f74665049b76b3 + react-native-audio-waveform: c253cf552518a830fb7876c9140f43c6c7415f6c + react-native-safe-area-context: ad7b31de8373577e003e700340b746fd5645b12c React-nativeconfig: cd0fbb40987a9658c24dab5812c14e5522a64929 React-NativeModulesApple: 45187d13c68d47250a7416b18ff082c7cc07bff7 React-perflogger: 15a7bcb6c46eae8a981f7add8c9f4172e2372324 @@ -1938,7 +1938,7 @@ SPEC CHECKSUMS: rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 - RNGestureHandler: 9ae308f1850d9c296a1230db9d1b52858911916b + RNGestureHandler: 66e593addd8952725107cfaa4f5e3378e946b541 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 @@ -1946,4 +1946,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 6f704d99bbe3053bd858eef7cb4caed0084ae50b -COCOAPODS: 1.16.2 +COCOAPODS: 1.14.3 diff --git a/ios/AudioWaveform.swift b/ios/AudioWaveform.swift index 6ccb19e..a5ca400 100644 --- a/ios/AudioWaveform.swift +++ b/ios/AudioWaveform.swift @@ -234,7 +234,13 @@ class AudioWaveform: RCTEventEmitter { audioPlayers[playerKey]?.stopPlayer() } audioPlayers.removeAll() - resolve(true) + + if audioPlayers.isEmpty { + EventEmitter.sharedInstance.dispatch(name: Constants.onResetAllWaveforms, body: ["clearedPlayers": true]) + resolve(true) + } else { + reject("REMOVE_FAILED", "Failed to clear all audio players", nil) + } } @objc func stopAllWaveFormExtractors(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void { @@ -242,7 +248,13 @@ class AudioWaveform: RCTEventEmitter { extractors[extractorKey]?.cancel() } extractors.removeAll() - resolve(true) + + if extractors.isEmpty { + EventEmitter.sharedInstance.dispatch(name: Constants.onResetAllWaveforms, body: ["clearedExtractors": true]) + resolve(true) + } else { + reject("REMOVE_FAILED", "Failed to clear all extractors", nil) + } } diff --git a/ios/EventEmitter.swift b/ios/EventEmitter.swift index 5d0d289..609c476 100644 --- a/ios/EventEmitter.swift +++ b/ios/EventEmitter.swift @@ -19,7 +19,7 @@ class EventEmitter { /// All Events which must be support by React Native. lazy var allEvents: [String] = { - var allEventNames: [String] = ["onDidFinishPlayingAudio", "onCurrentDuration", "onCurrentExtractedWaveformData", "onCurrentRecordingWaveformData"] + var allEventNames: [String] = ["onDidFinishPlayingAudio", "onCurrentDuration", "onCurrentExtractedWaveformData", "onCurrentRecordingWaveformData", "onResetAllWaveforms"] // Append all events here diff --git a/ios/Utils.swift b/ios/Utils.swift index 9a10019..bf1ef45 100644 --- a/ios/Utils.swift +++ b/ios/Utils.swift @@ -72,6 +72,7 @@ struct Constants { static let useLegacyNormalization = "useLegacyNormalization" static let updateFrequency = "updateFrequency" static let onGetAudioBuffers = "onGetAudioBuffers" + static let onResetAllWaveforms = "onResetAllWaveforms" } enum FinishMode : Int{ diff --git a/src/components/Waveform/Waveform.tsx b/src/components/Waveform/Waveform.tsx index cef932b..a274357 100644 --- a/src/components/Waveform/Waveform.tsx +++ b/src/components/Waveform/Waveform.tsx @@ -98,6 +98,7 @@ export const Waveform = forwardRef((props, ref) => { onCurrentRecordingWaveformData, setPlaybackSpeed, markPlayerAsUnmounted, + onResetAllWaveforms, } = useAudioPlayer(); const { startRecording, stopRecording, pauseRecording, resumeRecording } = @@ -533,6 +534,19 @@ export const Waveform = forwardRef((props, ref) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + const traceAllWaveformReset = onResetAllWaveforms(() => { + // This component (and maybe others) should reset progress to 0 + if (playerState !== PlayerState.stopped || currentProgress !== 0) { + stopPlayerAction(); + } + }); + return () => { + traceAllWaveformReset.remove(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [playerState, currentProgress]); + useEffect(() => { if (!isNil(onPlayerStateChange)) { (onPlayerStateChange as Function)(playerState); diff --git a/src/constants/index.ts b/src/constants/index.ts index 7955112..8078c39 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -8,6 +8,7 @@ export enum NativeEvents { onCurrentDuration = 'onCurrentDuration', onCurrentExtractedWaveformData = 'onCurrentExtractedWaveformData', onCurrentRecordingWaveformData = 'onCurrentRecordingWaveformData', + onResetAllWaveforms = 'onResetAllWaveforms', } export enum PermissionStatus { diff --git a/src/hooks/useAudioPlayer.tsx b/src/hooks/useAudioPlayer.tsx index c231c65..3e8f601 100644 --- a/src/hooks/useAudioPlayer.tsx +++ b/src/hooks/useAudioPlayer.tsx @@ -86,6 +86,13 @@ export const useAudioPlayer = () => { AudioWaveform.markPlayerAsUnmounted(); }; + const onResetAllWaveforms = ( + callback: (result: IOnCurrentRecordingWaveForm) => void + ) => + audioPlayerEmitter.addListener(NativeEvents.onResetAllWaveforms, result => + callback(result) + ); + return { extractWaveformData, pausePlayer, @@ -104,5 +111,6 @@ export const useAudioPlayer = () => { markPlayerAsUnmounted, stopAllWaveFormExtractors, stopPlayersAndExtractors, + onResetAllWaveforms, }; };