From 77e30b0428a96b8a5309411d76a1726619b92caf Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Tue, 17 Mar 2026 11:51:17 +0100 Subject: [PATCH 1/2] refactor: remove deprecated useAudioController hook --- .../Attachment/hooks/useAudioController.ts | 162 ------------------ src/components/Attachment/index.ts | 1 - .../components/WaveProgressBar.tsx | 3 +- 3 files changed, 2 insertions(+), 164 deletions(-) delete mode 100644 src/components/Attachment/hooks/useAudioController.ts diff --git a/src/components/Attachment/hooks/useAudioController.ts b/src/components/Attachment/hooks/useAudioController.ts deleted file mode 100644 index ed881b4568..0000000000 --- a/src/components/Attachment/hooks/useAudioController.ts +++ /dev/null @@ -1,162 +0,0 @@ -import throttle from 'lodash.throttle'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useChannelActionContext, useTranslationContext } from '../../../context'; - -const isSeekable = (audioElement: HTMLAudioElement) => - !(audioElement.duration === Infinity || isNaN(audioElement.duration)); - -export const elementIsPlaying = (audioElement: HTMLAudioElement | null) => - audioElement && !(audioElement.paused || audioElement.ended); - -const logError = (e: Error) => console.error('[AUDIO PLAYER]', e); - -const DEFAULT_PLAYBACK_RATES = [1.0, 1.5, 2.0]; - -export type SeekFn = (params: { clientX: number; currentTarget: HTMLDivElement }) => void; - -type AudioControllerParams = { - /** Audio duration in seconds. */ - durationSeconds?: number; - /** The audio MIME type that is checked before the audio is played. If the type is not supported the controller registers error in playbackError. */ - mimeType?: string; - /** An array of fractional numeric values of playback speed to override the defaults (1.0, 1.5, 2.0) */ - playbackRates?: number[]; -}; - -/** @deprecated use useAudioPlayer instead */ -export const useAudioController = ({ - durationSeconds, - mimeType, - playbackRates = DEFAULT_PLAYBACK_RATES, -}: AudioControllerParams = {}) => { - const { addNotification } = useChannelActionContext('useAudioController'); - const { t } = useTranslationContext('useAudioController'); - const [isPlaying, setIsPlaying] = useState(false); - const [playbackError, setPlaybackError] = useState(); - const [canPlayRecord, setCanPlayRecord] = useState(true); - const [secondsElapsed, setSecondsElapsed] = useState(0); - const [playbackRateIndex, setPlaybackRateIndex] = useState(0); - const playTimeout = useRef>(undefined); - const audioRef = useRef(null); - - const registerError = useCallback( - (e: Error) => { - logError(e as Error); - setPlaybackError(e); - addNotification(e.message, 'error'); - }, - [addNotification], - ); - - const togglePlay = useCallback(async () => { - if (!audioRef.current) return; - clearTimeout(playTimeout.current); - playTimeout.current = undefined; - if (mimeType && !audioRef.current.canPlayType(mimeType)) { - registerError( - new Error(t('Recording format is not supported and cannot be reproduced')), - ); - setCanPlayRecord(false); - return; - } - if (elementIsPlaying(audioRef.current)) { - audioRef.current.pause(); - setIsPlaying(false); - } else { - playTimeout.current = setTimeout(() => { - if (!audioRef.current) return; - try { - audioRef.current.pause(); - setIsPlaying(false); - } catch (e) { - registerError(new Error(t('Failed to play the recording'))); - } - }, 2000); - - try { - await audioRef.current.play(); - setIsPlaying(true); - } catch (e) { - registerError(e as Error); - setIsPlaying(false); - } finally { - clearTimeout(playTimeout.current); - playTimeout.current = undefined; - } - } - }, [mimeType, registerError, t]); - - const increasePlaybackRate = () => { - setPlaybackRateIndex((prev) => { - if (!audioRef.current) return prev; - const nextIndex = prev === playbackRates.length - 1 ? 0 : prev + 1; - audioRef.current.playbackRate = playbackRates[nextIndex]; - return nextIndex; - }); - }; - - const seek = useMemo( - () => - throttle(({ clientX, currentTarget }) => { - if (!(currentTarget && audioRef.current)) return; - if (!isSeekable(audioRef.current)) { - registerError(new Error(t('Cannot seek in the recording'))); - return; - } - - const { width, x } = currentTarget.getBoundingClientRect(); - - const ratio = (clientX - x) / width; - if (ratio > 1 || ratio < 0) return; - const currentTime = ratio * audioRef.current.duration; - setSecondsElapsed(currentTime); - audioRef.current.currentTime = currentTime; - }, 16), - [registerError, t], - ); - - useEffect(() => { - if (!audioRef.current) return; - const audioElement = audioRef.current; - - const handleEnded = () => { - setSecondsElapsed(audioElement?.duration ?? durationSeconds ?? 0); - setIsPlaying(false); - }; - audioElement.addEventListener('ended', handleEnded); - - const handleError = () => { - addNotification(t('Error reproducing the recording'), 'error'); - setIsPlaying(false); - }; - audioElement.addEventListener('error', handleError); - - const handleTimeupdate = () => { - setSecondsElapsed(audioElement?.currentTime); - }; - audioElement.addEventListener('timeupdate', handleTimeupdate); - - return () => { - audioElement.pause(); - audioElement.removeEventListener('ended', handleEnded); - audioElement.removeEventListener('error', handleError); - audioElement.removeEventListener('timeupdate', handleTimeupdate); - }; - }, [addNotification, durationSeconds, t]); - - return { - audioRef, - canPlayRecord, - increasePlaybackRate, - isPlaying, - playbackError, - playbackRate: playbackRates[playbackRateIndex], - progress: - audioRef.current && secondsElapsed - ? (secondsElapsed / audioRef.current.duration) * 100 - : 0, - secondsElapsed, - seek, - togglePlay, - }; -}; diff --git a/src/components/Attachment/index.ts b/src/components/Attachment/index.ts index 44df0bb6d1..4f0a34a8e5 100644 --- a/src/components/Attachment/index.ts +++ b/src/components/Attachment/index.ts @@ -12,5 +12,4 @@ export * from './ModalGallery'; export * from './UnsupportedAttachment'; export * from './utils'; export * from './VoiceRecording'; -export { useAudioController } from './hooks/useAudioController'; export * from '../Location/hooks/useLiveLocationSharingManager'; diff --git a/src/components/AudioPlayback/components/WaveProgressBar.tsx b/src/components/AudioPlayback/components/WaveProgressBar.tsx index 4e8b6b83a7..5e88be0a90 100644 --- a/src/components/AudioPlayback/components/WaveProgressBar.tsx +++ b/src/components/AudioPlayback/components/WaveProgressBar.tsx @@ -10,7 +10,8 @@ import React, { } from 'react'; import clsx from 'clsx'; import { resampleWaveformData } from '../../Attachment/audioSampling'; -import type { SeekFn } from '../../Attachment/hooks/useAudioController'; + +export type SeekFn = (params: { clientX: number; currentTarget: HTMLDivElement }) => void; type WaveProgressBarProps = { /** Function that allows to change the track progress */ From daec57f889fb0d074127b1e4459984684a6ce97f Mon Sep 17 00:00:00 2001 From: Oliver Lazoroski Date: Tue, 17 Mar 2026 11:58:05 +0100 Subject: [PATCH 2/2] chore: fix build --- src/components/AudioPlayback/components/WaveProgressBar.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/AudioPlayback/components/WaveProgressBar.tsx b/src/components/AudioPlayback/components/WaveProgressBar.tsx index 5e88be0a90..bf7bb14f26 100644 --- a/src/components/AudioPlayback/components/WaveProgressBar.tsx +++ b/src/components/AudioPlayback/components/WaveProgressBar.tsx @@ -10,12 +10,13 @@ import React, { } from 'react'; import clsx from 'clsx'; import { resampleWaveformData } from '../../Attachment/audioSampling'; +import type { SeekFn as AudioPlayerSeekFn } from '../AudioPlayer'; -export type SeekFn = (params: { clientX: number; currentTarget: HTMLDivElement }) => void; +type SeekParams = Parameters[0]; type WaveProgressBarProps = { /** Function that allows to change the track progress */ - seek: SeekFn; + seek: (params: SeekParams) => void; /** The array of fractional number values between 0 and 1 representing the height of amplitudes */ waveformData: number[]; /** Progress expressed in fractional number value btw 0 and 100. */