UNPKG

react-native-audio-api

Version:

react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification

243 lines (242 loc) • 8.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _reactNativeGestureHandler = require("react-native-gesture-handler"); var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated")); var _AudioTagContext = require("../AudioTagContext"); var _audioControlUtils = require("./audioControlUtils"); var _play = _interopRequireDefault(require("./icons/play.png")); var _pause = _interopRequireDefault(require("./icons/pause.png")); var _speaker = _interopRequireDefault(require("./icons/speaker.png")); var _speakerX = _interopRequireDefault(require("./icons/speaker-x.png")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const TRACK_BAR_HEIGHT = 12; const TRACK_BAR_HEIGHT_PRESSED = 18; const TRACK_BAR_ANIM_MS = 150; const SCRUB_PAN_MIN_DISTANCE = 8; const AudioControls = () => { const { ready, play, pause, seekToTime, playbackState, muted, setMuted, currentTime, duration } = (0, _AudioTagContext.useAudioTagContext)(); const progressTrackAnim = (0, _audioControlUtils.useExpandableTrackHeight)(TRACK_BAR_HEIGHT, TRACK_BAR_HEIGHT_PRESSED, TRACK_BAR_ANIM_MS); const [scrubTime, setScrubTime] = (0, _react.useState)(null); const progressTrackRef = (0, _reactNativeReanimated.useAnimatedRef)(); const progressMetricsWidth = (0, _reactNativeReanimated.useSharedValue)(0); const durationRef = (0, _react.useRef)(duration); durationRef.current = duration; const onStart = (0, _react.useCallback)(x => { progressTrackAnim.expand(); const d = durationRef.current; progressTrackRef.current?.measureInWindow((_left, _y, width, _h) => { progressMetricsWidth.value = width; setScrubTime((0, _audioControlUtils.timeFromLocationX)(x, width, d)); }); }, [progressTrackAnim, progressMetricsWidth, setScrubTime, progressTrackRef]); const onUpdate = (0, _react.useCallback)(x => { const d = durationRef.current; const w = progressMetricsWidth.value; setScrubTime((0, _audioControlUtils.timeFromLocationX)(x, w, d)); }, [progressMetricsWidth]); const seekTo = (0, _react.useCallback)(x => { const d = durationRef.current; const w = progressMetricsWidth.value; const t = (0, _audioControlUtils.timeFromLocationX)(x, w, d); seekToTime(t); }, [progressMetricsWidth, seekToTime]); const onEnd = (0, _react.useCallback)(x => { seekTo(x); progressTrackAnim.collapse(); setScrubTime(null); }, [progressTrackAnim, seekTo]); const onCancel = (0, _react.useCallback)(() => { progressTrackAnim.collapse(); setScrubTime(null); }, [progressTrackAnim]); const onTapSeek = (0, _react.useCallback)(x => { onEnd(x); }, [onEnd]); const scrubGesture = (0, _react.useMemo)(() => { const panGesture = _reactNativeGestureHandler.Gesture.Pan().runOnJS(true).minDistance(SCRUB_PAN_MIN_DISTANCE).onStart(e => { onStart(e.x); }).onUpdate(e => { onUpdate(e.x); }).onEnd(e => { onEnd(e.x); }).onFinalize((_e, success) => { if (!success) { onCancel(); } }); const tapGesture = _reactNativeGestureHandler.Gesture.Tap().runOnJS(true).maxDistance(14).onEnd((e, success) => { if (success) { onTapSeek(e.x); } }); return _reactNativeGestureHandler.Gesture.Race(panGesture, tapGesture); }, [onStart, onUpdate, onEnd, onCancel, onTapSeek]); const onPlayPausePress = (0, _react.useCallback)(() => { if (playbackState === 'playing') { pause(); } else { play(); } }, [playbackState, pause, play]); const onProgressTrackLayout = (0, _react.useCallback)(() => { progressTrackRef.current?.measureInWindow((_left, _y, width, _h) => { progressMetricsWidth.value = width; }); }, [progressMetricsWidth, progressTrackRef]); if (!ready) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.container, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { color: "#333", size: "small" }) }); } const displayTime = scrubTime ?? currentTime; const progress = duration > 0 ? displayTime / duration : 0; return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.container, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.topRow, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, { style: styles.playPause, onPress: onPlayPausePress, children: playbackState === 'playing' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, { source: _pause.default, style: { width: 24, height: 24 } }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, { source: _play.default, style: { width: 24, height: 24 } }) }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, { style: styles.timeText, children: [(0, _audioControlUtils.formatTime)(displayTime), " / ", (0, _audioControlUtils.formatTime)(duration)] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, { gesture: scrubGesture, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { ref: progressTrackRef, onLayout: onProgressTrackLayout, style: styles.progressTrack, collapsable: false, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, { style: [styles.trackInner, progressTrackAnim.animatedStyle], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.trackFill, { width: `${progress * 100}%` }] }) }) }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, { style: styles.volumeIcon, onPress: () => setMuted(!muted), children: muted ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, { source: _speakerX.default, style: { width: 24, height: 24 } }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, { source: _speaker.default, style: { width: 24, height: 24 } }) })] }) }); }; const styles = _reactNative.StyleSheet.create({ container: { flexDirection: 'column', alignSelf: 'stretch', minWidth: 200, paddingVertical: 10, paddingHorizontal: 12, backgroundColor: '#f5f5f5', borderRadius: 8, borderWidth: 1, borderColor: '#333', ..._reactNative.Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.15, shadowRadius: 4 }, android: { elevation: 4 } }) }, topRow: { flexDirection: 'row', alignItems: 'center' }, playPause: { padding: 4, marginRight: 12 }, timeText: { color: '#000', fontSize: 12, marginRight: 10, minWidth: 48 }, progressTrack: { flex: 1, minWidth: 40, justifyContent: 'center', marginRight: 10 }, trackInner: { alignSelf: 'stretch', backgroundColor: '#ccc', overflow: 'hidden' }, trackFill: { height: '100%', backgroundColor: '#000' }, volumeIcon: { padding: 4, marginRight: 12 }, loadingRow: { flexDirection: 'row', alignItems: 'center' }, loadingText: { color: '#333', fontSize: 14 } }); var _default = exports.default = AudioControls; //# sourceMappingURL=AudioControls.js.map