UNPKG

wix-style-react

Version:
190 lines (186 loc) • 6.26 kB
"use strict"; exports.__esModule = true; exports.useAudioManager = void 0; var _react = require("react"); var _howler = require("howler"); // A callback that keeps its instance and won't make Howler to re create every time var useStableCallback = callback => { var callbackRef = (0, _react.useRef)(); callbackRef.current = callback; return (0, _react.useCallback)(function () { return callbackRef.current && callbackRef.current(...arguments); }, []); }; var useRaf = _ref => { var { callback, enabled } = _ref; var stableCallback = useStableCallback(callback); // we use useLayoutEffect here because it fires synchronously after all DOM mutations // and before the next requestAnimationFrame. If we use useEffect (which runs after requestAnimationFrame) // we'll get an endless loop of frames that causes a memory leak. (0, _react.useLayoutEffect)(() => { if (enabled) { var id; var loop = () => { stableCallback(); id = requestAnimationFrame(loop); }; id = requestAnimationFrame(loop); return () => { cancelAnimationFrame(id); }; } }, [enabled, stableCallback]); }; var useAudioManager = _ref2 => { var { src, playing, format, preload, webAudioAPI, allowSeekLoop, onEnd, onLoad, onPlay, onPause, onSeek, onLoadError, onDestroy } = _ref2; var stableOnEnd = useStableCallback(onEnd); var stableOnLoad = useStableCallback(onLoad); var stableOnPlay = useStableCallback(onPlay); var stableOnPause = useStableCallback(onPause); var stableOnSeek = useStableCallback(onSeek); var stableOnLoadError = useStableCallback(onLoadError); var stableOnDestroy = useStableCallback(onDestroy); var audioManager = (0, _react.useRef)(); var [_seek, _setSeek] = (0, _react.useState)(0); var [loadingState, setLoadingState] = (0, _react.useState)('unloaded'); var [wasEverPlayed, setWasEverPlayed] = (0, _react.useState)(false); var duration = (0, _react.useMemo)(() => { if (!audioManager.current || loadingState !== 'loaded') { return 0; } return audioManager.current.duration(); }, [loadingState]); var _destroy = (0, _react.useCallback)(() => { if (audioManager.current) { setLoadingState('unloaded'); setWasEverPlayed(false); _setSeek(0); audioManager.current.stop(); audioManager.current.unload(); audioManager.current = null; stableOnDestroy(); } }, [setLoadingState, setWasEverPlayed, _setSeek, stableOnDestroy]); var _onPlay = (0, _react.useCallback)(() => { setWasEverPlayed(true); stableOnPlay(); }, [stableOnPlay, setWasEverPlayed]); var _onLoad = (0, _react.useCallback)(() => { // Keeping a duplicate state because when Howler state changes it won't cause a render // and we want to react to this change. setLoadingState(audioManager.current.state()); stableOnLoad(); }, [stableOnLoad, setLoadingState]); var _onEnd = (0, _react.useCallback)(() => { _setSeek(0); stableOnEnd(); }, [stableOnEnd, _setSeek]); var _onLoadError = (0, _react.useCallback)((_, errorMsg) => { stableOnLoadError(errorMsg); }, [stableOnLoadError]); var _load = (0, _react.useCallback)(() => { if (audioManager.current) { audioManager.current.load(); setLoadingState(audioManager.current.state()); } }, [setLoadingState]); var _play = (0, _react.useCallback)(() => { if (audioManager.current) { if (loadingState === 'unloaded') { _load(); } if (loadingState === 'loaded' && !audioManager.current.playing()) { audioManager.current.play(); } } }, [loadingState, _load]); var _pause = (0, _react.useCallback)(() => { if (audioManager.current) { audioManager.current.pause(); } }, []); // There is an open bug in howler that if play is locked it returns the howler object when // getting "seek" instead of a number. this solves the issue until they fix it,. // https://github.com/goldfire/howler.js/issues/1189 var _readSeek = (0, _react.useCallback)(() => { if (!audioManager.current || loadingState !== 'loaded') { return 0; } var currentSeek = audioManager.current.seek(); if (typeof currentSeek !== 'number') { var internalSeek = audioManager.current._sounds[0]._seek; if (typeof internalSeek !== 'number') { currentSeek = 0; } currentSeek = internalSeek; } return currentSeek; }, [loadingState]); var _updateSeek = (0, _react.useCallback)(() => { // Keeping a duplicate seek state because when Howler seek changes it won't cause a render // and we want to react to this change (expose an updated seek to audioManager consumer). _setSeek(_readSeek()); }, [_readSeek]); var setSeek = (0, _react.useCallback)(pos => { if (audioManager.current && loadingState === 'loaded') { audioManager.current.seek(pos); _setSeek(pos); } }, [loadingState]); // starts a request animation frame loop that updates the seek every frame. // stops the loop if paused or if slider is being dragged useRaf({ callback: _updateSeek, enabled: playing && allowSeekLoop }); (0, _react.useEffect)(() => { if (src) { audioManager.current = new _howler.Howl({ src, format, preload: preload === 'none' ? false : preload === 'auto' ? true : preload, onload: _onLoad, html5: !webAudioAPI, onend: _onEnd, onplay: _onPlay, onloaderror: _onLoadError, onpause: stableOnPause, onseek: stableOnSeek }); } return () => _destroy(); }, [audioManager, _destroy, format, src, _onEnd, _onLoad, _onLoadError, stableOnPlay, stableOnPause, stableOnSeek, preload, webAudioAPI, _onPlay]); (0, _react.useEffect)(() => { if (playing) { _play(); } if (!playing && wasEverPlayed) { _pause(); } }, [_pause, _play, playing, wasEverPlayed]); return (0, _react.useMemo)(() => ({ loadingState, duration, seek: _seek, setSeek }), [loadingState, duration, _seek, setSeek]); }; exports.useAudioManager = useAudioManager; //# sourceMappingURL=AudioManager.js.map