wix-style-react
Version:
wix-style-react
190 lines (186 loc) • 6.26 kB
JavaScript
;
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