wix-style-react
Version:
237 lines (195 loc) • 7.36 kB
JavaScript
;
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;