UNPKG

wix-style-react

Version:
237 lines (195 loc) • 7.36 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useAudioManager = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); 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 = function useStableCallback(callback) { var callbackRef = (0, _react.useRef)(); callbackRef.current = callback; return (0, _react.useCallback)(function () { return callbackRef.current && callbackRef.current.apply(callbackRef, arguments); }, []); }; var useRaf = function useRaf(_ref) { var callback = _ref.callback, enabled = _ref.enabled; 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)(function () { if (enabled) { var id; var loop = function loop() { stableCallback(); id = requestAnimationFrame(loop); }; id = requestAnimationFrame(loop); return function () { cancelAnimationFrame(id); }; } }, [enabled, stableCallback]); }; var useAudioManager = function useAudioManager(_ref2) { var src = _ref2.src, playing = _ref2.playing, format = _ref2.format, preload = _ref2.preload, webAudioAPI = _ref2.webAudioAPI, allowSeekLoop = _ref2.allowSeekLoop, onEnd = _ref2.onEnd, onLoad = _ref2.onLoad, onPlay = _ref2.onPlay, onPause = _ref2.onPause, onSeek = _ref2.onSeek, onLoadError = _ref2.onLoadError, onDestroy = _ref2.onDestroy; 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 _useState = (0, _react.useState)(0), _useState2 = (0, _slicedToArray2["default"])(_useState, 2), _seek = _useState2[0], _setSeek = _useState2[1]; var _useState3 = (0, _react.useState)('unloaded'), _useState4 = (0, _slicedToArray2["default"])(_useState3, 2), loadingState = _useState4[0], setLoadingState = _useState4[1]; var _useState5 = (0, _react.useState)(false), _useState6 = (0, _slicedToArray2["default"])(_useState5, 2), wasEverPlayed = _useState6[0], setWasEverPlayed = _useState6[1]; var duration = (0, _react.useMemo)(function () { if (!audioManager.current || loadingState !== 'loaded') { return 0; } return audioManager.current.duration(); }, [loadingState]); var _destroy = (0, _react.useCallback)(function () { 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)(function () { setWasEverPlayed(true); stableOnPlay(); }, [stableOnPlay, setWasEverPlayed]); var _onLoad = (0, _react.useCallback)(function () { // 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)(function () { _setSeek(0); stableOnEnd(); }, [stableOnEnd, _setSeek]); var _onLoadError = (0, _react.useCallback)(function (_, errorMsg) { stableOnLoadError(errorMsg); }, [stableOnLoadError]); var _load = (0, _react.useCallback)(function () { if (audioManager.current) { audioManager.current.load(); setLoadingState(audioManager.current.state()); } }, [setLoadingState]); var _play = (0, _react.useCallback)(function () { if (audioManager.current) { if (loadingState === 'unloaded') { _load(); } if (loadingState === 'loaded' && !audioManager.current.playing()) { audioManager.current.play(); } } }, [loadingState, _load]); var _pause = (0, _react.useCallback)(function () { 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)(function () { 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)(function () { // 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)(function (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)(function () { if (src) { audioManager.current = new _howler.Howl({ src: src, format: format, preload: preload === 'none' ? false : preload === 'auto' ? true : preload, onload: _onLoad, html5: !webAudioAPI, onend: _onEnd, onplay: _onPlay, onloaderror: _onLoadError, onpause: stableOnPause, onseek: stableOnSeek }); } return function () { return _destroy(); }; }, [audioManager, _destroy, format, src, _onEnd, _onLoad, _onLoadError, stableOnPlay, stableOnPause, stableOnSeek, preload, webAudioAPI, _onPlay]); (0, _react.useEffect)(function () { if (playing) { _play(); } if (!playing && wasEverPlayed) { _pause(); } }, [_pause, _play, playing, wasEverPlayed]); return (0, _react.useMemo)(function () { return { loadingState: loadingState, duration: duration, seek: _seek, setSeek: setSeek }; }, [loadingState, duration, _seek, setSeek]); }; exports.useAudioManager = useAudioManager;