@draftbit/core 47.3.0 → 47.3.1-07a8e3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/lib/src/components/AudioPlayer/AudioPlayerCommon.d.ts +39 -0
  2. package/lib/src/components/AudioPlayer/AudioPlayerCommon.js +2 -0
  3. package/lib/src/components/AudioPlayer/AudioPlayerCommon.js.map +1 -0
  4. package/lib/src/components/AudioPlayer/AudioPlayerWithInterface.d.ts +6 -0
  5. package/lib/src/components/AudioPlayer/AudioPlayerWithInterface.js +126 -0
  6. package/lib/src/components/AudioPlayer/AudioPlayerWithInterface.js.map +1 -0
  7. package/lib/src/components/AudioPlayer/HeadlessAudioPlayer.d.ts +8 -0
  8. package/lib/src/components/AudioPlayer/HeadlessAudioPlayer.js +127 -0
  9. package/lib/src/components/AudioPlayer/HeadlessAudioPlayer.js.map +1 -0
  10. package/lib/src/components/AudioPlayer/index.d.ts +10 -0
  11. package/lib/src/components/AudioPlayer/index.js +15 -0
  12. package/lib/src/components/AudioPlayer/index.js.map +1 -0
  13. package/lib/src/index.d.ts +1 -0
  14. package/lib/src/index.js +1 -0
  15. package/lib/src/index.js.map +1 -1
  16. package/lib/tsconfig.tsbuildinfo +1 -1
  17. package/package.json +5 -3
  18. package/src/components/AudioPlayer/AudioPlayerCommon.js +2 -0
  19. package/src/components/AudioPlayer/AudioPlayerCommon.js.map +1 -0
  20. package/src/components/AudioPlayer/AudioPlayerCommon.ts +44 -0
  21. package/src/components/AudioPlayer/AudioPlayerWithInterface.js +126 -0
  22. package/src/components/AudioPlayer/AudioPlayerWithInterface.js.map +1 -0
  23. package/src/components/AudioPlayer/AudioPlayerWithInterface.tsx +217 -0
  24. package/src/components/AudioPlayer/HeadlessAudioPlayer.js +127 -0
  25. package/src/components/AudioPlayer/HeadlessAudioPlayer.js.map +1 -0
  26. package/src/components/AudioPlayer/HeadlessAudioPlayer.tsx +180 -0
  27. package/src/components/AudioPlayer/index.js +15 -0
  28. package/src/components/AudioPlayer/index.js.map +1 -0
  29. package/src/components/AudioPlayer/index.tsx +30 -0
  30. package/src/index.js +1 -0
  31. package/src/index.js.map +1 -1
  32. package/src/index.tsx +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftbit/core",
3
- "version": "47.3.0",
3
+ "version": "47.3.1-07a8e3.2+07a8e3c",
4
4
  "description": "Core (non-native) Components",
5
5
  "main": "lib/src/index.js",
6
6
  "types": "lib/src/index.d.ts",
@@ -39,7 +39,8 @@
39
39
  "dependencies": {
40
40
  "@date-io/date-fns": "^1.3.13",
41
41
  "@draftbit/react-theme-provider": "^2.1.1",
42
- "@draftbit/types": "47.3.0",
42
+ "@draftbit/types": "^47.3.1-07a8e3.2+07a8e3c",
43
+ "@expo/vector-icons": "^13.0.0",
43
44
  "@material-ui/core": "^4.11.0",
44
45
  "@material-ui/pickers": "^3.2.10",
45
46
  "@react-native-community/slider": "4.2.4",
@@ -48,6 +49,7 @@
48
49
  "color": "^3.1.2",
49
50
  "date-fns": "^2.16.1",
50
51
  "dateformat": "^3.0.3",
52
+ "expo-av": "~13.0.3",
51
53
  "lodash.isnumber": "^3.0.3",
52
54
  "lodash.omit": "^4.5.0",
53
55
  "lodash.tonumber": "^4.0.3",
@@ -77,5 +79,5 @@
77
79
  "node_modules/",
78
80
  "lib/"
79
81
  ],
80
- "gitHead": "decb6ca75b7298c9bf2bd375600015f4f489c2fe"
82
+ "gitHead": "07a8e3c7f8cf7f5e2001de814cbe4cc751a446cb"
81
83
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AudioPlayerCommon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioPlayerCommon.js","sourceRoot":"","sources":["AudioPlayerCommon.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ import { AVPlaybackSource } from "expo-av";
2
+ import { StyleProp, ViewStyle, TextStyle } from "react-native";
3
+ import type { Theme } from "../../styles/DefaultTheme";
4
+
5
+ export type AudioInterruptionMode = "lower volume" | "stop";
6
+
7
+ export interface AudioPlayerStatus {
8
+ isPlaying: boolean;
9
+ isLoading: boolean;
10
+ isBuffering: boolean;
11
+ currentPositionMillis: number;
12
+ durationMillis: number;
13
+ bufferedDurationMillis: number;
14
+ isError: boolean;
15
+ error?: string;
16
+ }
17
+
18
+ export interface HeadlessAudioPlayerRef {
19
+ seekToPosition: (positionMillis: number) => void;
20
+ togglePlayback: () => void;
21
+ }
22
+
23
+ export interface HeadlessAudioPlayerProps {
24
+ onPlaybackStatusUpdate?: (status: AudioPlayerStatus) => void;
25
+ onPlaybackFinish?: () => void;
26
+ source: AVPlaybackSource;
27
+ interruptionMode?: AudioInterruptionMode;
28
+ playsInBackground?: boolean;
29
+ playsInSilentModeIOS?: boolean;
30
+ playThroughEarpieceAndroid?: boolean;
31
+ }
32
+
33
+ export interface AudioPlayerInterfaceProps {
34
+ style?: StyleProp<ViewStyle & TextStyle>;
35
+ thumbColor?: string;
36
+ completedTrackColor?: string;
37
+ remainingTrackColor?: string;
38
+ togglePlaybackIconSize?: number;
39
+ togglePlaybackIconColor?: string;
40
+ hidePlaybackIcon?: boolean;
41
+ hideDuration?: boolean;
42
+ hideSlider?: boolean;
43
+ theme: Theme;
44
+ }
@@ -0,0 +1,126 @@
1
+ import * as React from "react";
2
+ import { Text, View, StyleSheet } from "react-native";
3
+ import { AntDesign } from "@expo/vector-icons";
4
+ import { withTheme } from "../../theming";
5
+ import Slider from "@react-native-community/slider";
6
+ import HeadlessAudioPlayer from "./HeadlessAudioPlayer";
7
+ import Pressable from "../Pressable";
8
+ /**
9
+ * Built on top of HeadlessAudioPlayer to provide a simple interface for playing audio
10
+ */
11
+ const AudioPlayerWithInterface = React.forwardRef(({ style, theme, thumbColor = theme.colors.primary, completedTrackColor = theme.colors.primary, remainingTrackColor = theme.colors.divider, togglePlaybackIconSize = 24, togglePlaybackIconColor = theme.colors.primary, onPlaybackStatusUpdate: onPlaybackStatusUpdateProp, onPlaybackFinish: onPlaybackFinishProp, hidePlaybackIcon = false, hideDuration = false, hideSlider = false, ...rest }, ref) => {
12
+ const [isPlaying, setIsPlaying] = React.useState(false);
13
+ const [isLoading, setIsLoading] = React.useState(false);
14
+ const [durationMillis, setDurationMillis] = React.useState(1);
15
+ const [isDraggingSlider, setIsDraggingSlider] = React.useState(false);
16
+ const [sliderPositionMillis, setSliderPositionMillis] = React.useState(0);
17
+ const newHeadlessAudioPlayerRef = React.useRef(null);
18
+ // Use the provided ref or default to new ref when not provided
19
+ const headlessAudioPlayerRef = ref
20
+ ? ref
21
+ : newHeadlessAudioPlayerRef;
22
+ const { color, fontFamily, fontWeight, fontSize, lineHeight, letterSpacing, textTransform, textAlign, textDecorationLine, textDecorationColor, textDecorationStyle, ...viewStyles } = StyleSheet.flatten(style || {});
23
+ const textStyles = {
24
+ color,
25
+ fontFamily,
26
+ fontWeight,
27
+ fontSize,
28
+ lineHeight,
29
+ letterSpacing,
30
+ textTransform,
31
+ textAlign,
32
+ textDecorationLine,
33
+ textDecorationColor,
34
+ textDecorationStyle,
35
+ };
36
+ const onPlaybackStatusUpdate = (status) => {
37
+ setIsLoading(status.isLoading);
38
+ setDurationMillis(status.durationMillis);
39
+ setSliderPositionMillis(status.currentPositionMillis);
40
+ setIsPlaying(status.isPlaying);
41
+ onPlaybackStatusUpdateProp === null || onPlaybackStatusUpdateProp === void 0 ? void 0 : onPlaybackStatusUpdateProp(status);
42
+ };
43
+ const onPlaybackFinish = () => {
44
+ var _a, _b;
45
+ setIsPlaying(false);
46
+ setSliderPositionMillis(0);
47
+ (_a = headlessAudioPlayerRef.current) === null || _a === void 0 ? void 0 : _a.togglePlayback();
48
+ (_b = headlessAudioPlayerRef.current) === null || _b === void 0 ? void 0 : _b.seekToPosition(0);
49
+ onPlaybackFinishProp === null || onPlaybackFinishProp === void 0 ? void 0 : onPlaybackFinishProp();
50
+ };
51
+ const onSlidingComplete = (sliderValue) => {
52
+ var _a;
53
+ if (isDraggingSlider) {
54
+ setIsDraggingSlider(false);
55
+ }
56
+ (_a = headlessAudioPlayerRef.current) === null || _a === void 0 ? void 0 : _a.seekToPosition(sliderValue);
57
+ };
58
+ const onSliderChange = () => {
59
+ if (!isDraggingSlider) {
60
+ setIsDraggingSlider(true);
61
+ }
62
+ };
63
+ let iconName;
64
+ if (isLoading) {
65
+ iconName = "loading1";
66
+ }
67
+ else if (isPlaying) {
68
+ iconName = "pause";
69
+ }
70
+ else {
71
+ iconName = "play";
72
+ }
73
+ return (React.createElement(React.Fragment, null,
74
+ React.createElement(HeadlessAudioPlayer, { ...rest, ref: headlessAudioPlayerRef, onPlaybackStatusUpdate: onPlaybackStatusUpdate, onPlaybackFinish: onPlaybackFinish }),
75
+ React.createElement(View, { style: [
76
+ {
77
+ backgroundColor: theme.colors.background,
78
+ borderColor: theme.colors.divider,
79
+ },
80
+ styles.container,
81
+ viewStyles,
82
+ ] },
83
+ !hidePlaybackIcon && (React.createElement(Pressable, { onPress: () => { var _a; return (_a = headlessAudioPlayerRef.current) === null || _a === void 0 ? void 0 : _a.togglePlayback(); }, style: styles.spacingEnd },
84
+ React.createElement(AntDesign, { name: iconName, size: togglePlaybackIconSize, color: togglePlaybackIconColor }))),
85
+ !hideDuration && (React.createElement(Text, { style: [
86
+ { color: theme.colors.strong },
87
+ styles.spacingEnd,
88
+ { ...textStyles },
89
+ ] },
90
+ formatDuration(sliderPositionMillis !== null && sliderPositionMillis !== void 0 ? sliderPositionMillis : 0),
91
+ " /",
92
+ " ",
93
+ formatDuration(durationMillis || 0))),
94
+ !hideSlider && (React.createElement(Slider, { style: styles.slider, minimumTrackTintColor: completedTrackColor, maximumTrackTintColor: remainingTrackColor, thumbTintColor: thumbColor, minimumValue: 0, value: sliderPositionMillis, maximumValue: durationMillis, onValueChange: onSliderChange, onSlidingComplete: onSlidingComplete })))));
95
+ });
96
+ const styles = StyleSheet.create({
97
+ container: {
98
+ padding: 8,
99
+ flexDirection: "row",
100
+ alignItems: "center",
101
+ borderRadius: 8,
102
+ borderWidth: 1,
103
+ },
104
+ spacingEnd: {
105
+ marginEnd: 8,
106
+ },
107
+ slider: {
108
+ flex: 1,
109
+ },
110
+ });
111
+ function formatDuration(duration) {
112
+ if (duration === 0)
113
+ return "00:00";
114
+ const seconds = Math.floor((duration / 1000) % 60);
115
+ const minutes = Math.floor((duration / (1000 * 60)) % 60);
116
+ const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
117
+ const renderedHours = hours < 10 ? "0" + hours : hours;
118
+ const renderedMinutes = minutes < 10 ? "0" + minutes : minutes;
119
+ const renderedSeconds = seconds < 10 ? "0" + seconds : seconds;
120
+ if (hours > 0) {
121
+ return renderedHours + ":" + renderedMinutes + ":" + renderedSeconds;
122
+ }
123
+ return renderedMinutes + ":" + renderedSeconds;
124
+ }
125
+ export default withTheme(AudioPlayerWithInterface);
126
+ //# sourceMappingURL=AudioPlayerWithInterface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioPlayerWithInterface.js","sourceRoot":"","sources":["AudioPlayerWithInterface.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,217 @@
1
+ import * as React from "react";
2
+ import { Text, View, StyleSheet } from "react-native";
3
+ import { AntDesign } from "@expo/vector-icons";
4
+ import { withTheme } from "../../theming";
5
+ import Slider from "@react-native-community/slider";
6
+ import HeadlessAudioPlayer from "./HeadlessAudioPlayer";
7
+ import {
8
+ AudioPlayerInterfaceProps,
9
+ AudioPlayerStatus,
10
+ HeadlessAudioPlayerProps,
11
+ HeadlessAudioPlayerRef,
12
+ } from "./AudioPlayerCommon";
13
+ import Pressable from "../Pressable";
14
+
15
+ /**
16
+ * Built on top of HeadlessAudioPlayer to provide a simple interface for playing audio
17
+ */
18
+ const AudioPlayerWithInterface = React.forwardRef<
19
+ HeadlessAudioPlayerRef,
20
+ AudioPlayerInterfaceProps & HeadlessAudioPlayerProps
21
+ >(
22
+ (
23
+ {
24
+ style,
25
+ theme,
26
+ thumbColor = theme.colors.primary,
27
+ completedTrackColor = theme.colors.primary,
28
+ remainingTrackColor = theme.colors.divider,
29
+ togglePlaybackIconSize = 24,
30
+ togglePlaybackIconColor = theme.colors.primary,
31
+ onPlaybackStatusUpdate: onPlaybackStatusUpdateProp,
32
+ onPlaybackFinish: onPlaybackFinishProp,
33
+ hidePlaybackIcon = false,
34
+ hideDuration = false,
35
+ hideSlider = false,
36
+ ...rest
37
+ },
38
+ ref
39
+ ) => {
40
+ const [isPlaying, setIsPlaying] = React.useState(false);
41
+ const [isLoading, setIsLoading] = React.useState(false);
42
+ const [durationMillis, setDurationMillis] = React.useState<
43
+ number | undefined
44
+ >(1);
45
+ const [isDraggingSlider, setIsDraggingSlider] = React.useState(false);
46
+ const [sliderPositionMillis, setSliderPositionMillis] = React.useState(0);
47
+ const newHeadlessAudioPlayerRef =
48
+ React.useRef<HeadlessAudioPlayerRef>(null);
49
+
50
+ // Use the provided ref or default to new ref when not provided
51
+ const headlessAudioPlayerRef = ref
52
+ ? (ref as React.RefObject<HeadlessAudioPlayerRef>)
53
+ : newHeadlessAudioPlayerRef;
54
+
55
+ const {
56
+ color,
57
+ fontFamily,
58
+ fontWeight,
59
+ fontSize,
60
+ lineHeight,
61
+ letterSpacing,
62
+ textTransform,
63
+ textAlign,
64
+ textDecorationLine,
65
+ textDecorationColor,
66
+ textDecorationStyle,
67
+ ...viewStyles
68
+ } = StyleSheet.flatten(style || {});
69
+
70
+ const textStyles = {
71
+ color,
72
+ fontFamily,
73
+ fontWeight,
74
+ fontSize,
75
+ lineHeight,
76
+ letterSpacing,
77
+ textTransform,
78
+ textAlign,
79
+ textDecorationLine,
80
+ textDecorationColor,
81
+ textDecorationStyle,
82
+ };
83
+
84
+ const onPlaybackStatusUpdate = (status: AudioPlayerStatus) => {
85
+ setIsLoading(status.isLoading);
86
+ setDurationMillis(status.durationMillis);
87
+ setSliderPositionMillis(status.currentPositionMillis);
88
+ setIsPlaying(status.isPlaying);
89
+ onPlaybackStatusUpdateProp?.(status);
90
+ };
91
+
92
+ const onPlaybackFinish = () => {
93
+ setIsPlaying(false);
94
+ setSliderPositionMillis(0);
95
+ headlessAudioPlayerRef.current?.togglePlayback();
96
+ headlessAudioPlayerRef.current?.seekToPosition(0);
97
+ onPlaybackFinishProp?.();
98
+ };
99
+
100
+ const onSlidingComplete = (sliderValue: number) => {
101
+ if (isDraggingSlider) {
102
+ setIsDraggingSlider(false);
103
+ }
104
+ headlessAudioPlayerRef.current?.seekToPosition(sliderValue);
105
+ };
106
+
107
+ const onSliderChange = () => {
108
+ if (!isDraggingSlider) {
109
+ setIsDraggingSlider(true);
110
+ }
111
+ };
112
+
113
+ let iconName;
114
+ if (isLoading) {
115
+ iconName = "loading1";
116
+ } else if (isPlaying) {
117
+ iconName = "pause";
118
+ } else {
119
+ iconName = "play";
120
+ }
121
+
122
+ return (
123
+ <>
124
+ <HeadlessAudioPlayer
125
+ {...rest}
126
+ ref={headlessAudioPlayerRef}
127
+ onPlaybackStatusUpdate={onPlaybackStatusUpdate}
128
+ onPlaybackFinish={onPlaybackFinish}
129
+ />
130
+ <View
131
+ style={[
132
+ {
133
+ backgroundColor: theme.colors.background,
134
+ borderColor: theme.colors.divider,
135
+ },
136
+ styles.container,
137
+ viewStyles,
138
+ ]}
139
+ >
140
+ {!hidePlaybackIcon && (
141
+ <Pressable
142
+ onPress={() => headlessAudioPlayerRef.current?.togglePlayback()}
143
+ style={styles.spacingEnd}
144
+ >
145
+ <AntDesign
146
+ name={iconName as any}
147
+ size={togglePlaybackIconSize}
148
+ color={togglePlaybackIconColor}
149
+ />
150
+ </Pressable>
151
+ )}
152
+ {!hideDuration && (
153
+ <Text
154
+ style={[
155
+ { color: theme.colors.strong },
156
+ styles.spacingEnd,
157
+ { ...textStyles },
158
+ ]}
159
+ >
160
+ {formatDuration(sliderPositionMillis ?? 0)} /{" "}
161
+ {formatDuration(durationMillis || 0)}
162
+ </Text>
163
+ )}
164
+ {!hideSlider && (
165
+ <Slider
166
+ style={styles.slider}
167
+ minimumTrackTintColor={completedTrackColor}
168
+ maximumTrackTintColor={remainingTrackColor}
169
+ thumbTintColor={thumbColor}
170
+ minimumValue={0}
171
+ value={sliderPositionMillis}
172
+ maximumValue={durationMillis}
173
+ onValueChange={onSliderChange}
174
+ onSlidingComplete={onSlidingComplete}
175
+ />
176
+ )}
177
+ </View>
178
+ </>
179
+ );
180
+ }
181
+ );
182
+
183
+ const styles = StyleSheet.create({
184
+ container: {
185
+ padding: 8,
186
+ flexDirection: "row",
187
+ alignItems: "center",
188
+ borderRadius: 8,
189
+ borderWidth: 1,
190
+ },
191
+ spacingEnd: {
192
+ marginEnd: 8,
193
+ },
194
+ slider: {
195
+ flex: 1,
196
+ },
197
+ });
198
+
199
+ function formatDuration(duration: number) {
200
+ if (duration === 0) return "00:00";
201
+
202
+ const seconds = Math.floor((duration / 1000) % 60);
203
+ const minutes = Math.floor((duration / (1000 * 60)) % 60);
204
+ const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
205
+
206
+ const renderedHours = hours < 10 ? "0" + hours : hours;
207
+ const renderedMinutes = minutes < 10 ? "0" + minutes : minutes;
208
+ const renderedSeconds = seconds < 10 ? "0" + seconds : seconds;
209
+
210
+ if (hours > 0) {
211
+ return renderedHours + ":" + renderedMinutes + ":" + renderedSeconds;
212
+ }
213
+
214
+ return renderedMinutes + ":" + renderedSeconds;
215
+ }
216
+
217
+ export default withTheme(AudioPlayerWithInterface);
@@ -0,0 +1,127 @@
1
+ import * as React from "react";
2
+ import { Audio, InterruptionModeIOS, InterruptionModeAndroid, } from "expo-av";
3
+ /**
4
+ * Audio Player component without an interface (UI).
5
+ * Only handles playing of the audio and provides callbacks and ref functions
6
+ */
7
+ const HeadlessAudioPlayer = React.forwardRef(({ source, interruptionMode = "lower volume", playsInBackground = false, playsInSilentModeIOS = false, playThroughEarpieceAndroid = false, onPlaybackStatusUpdate: onPlaybackStatusUpdateProp, onPlaybackFinish, }, ref) => {
8
+ const [currentSound, setCurrentSound] = React.useState();
9
+ const [isPlaying, setIsPlaying] = React.useState(false);
10
+ const updateAudioMode = React.useCallback(async () => {
11
+ await Audio.setAudioModeAsync({
12
+ staysActiveInBackground: playsInBackground,
13
+ interruptionModeIOS: interruptionMode === "lower volume"
14
+ ? InterruptionModeIOS.DuckOthers
15
+ : InterruptionModeIOS.DoNotMix,
16
+ interruptionModeAndroid: interruptionMode === "lower volume"
17
+ ? InterruptionModeAndroid.DuckOthers
18
+ : InterruptionModeAndroid.DoNotMix,
19
+ playsInSilentModeIOS,
20
+ playThroughEarpieceAndroid,
21
+ });
22
+ }, [
23
+ interruptionMode,
24
+ playsInBackground,
25
+ playsInSilentModeIOS,
26
+ playThroughEarpieceAndroid,
27
+ ]);
28
+ const onPlaybackStatusUpdate = (status) => {
29
+ if (status.isLoaded) {
30
+ onPlaybackStatusUpdateProp === null || onPlaybackStatusUpdateProp === void 0 ? void 0 : onPlaybackStatusUpdateProp({
31
+ isPlaying: status.isPlaying,
32
+ isLoading: false,
33
+ isBuffering: status.isBuffering,
34
+ currentPositionMillis: status.positionMillis || 0,
35
+ durationMillis: status.durationMillis || 0,
36
+ bufferedDurationMillis: status.playableDurationMillis || 0,
37
+ isError: false,
38
+ });
39
+ if (status.didJustFinish) {
40
+ onPlaybackFinish === null || onPlaybackFinish === void 0 ? void 0 : onPlaybackFinish();
41
+ }
42
+ setIsPlaying(status.isPlaying);
43
+ }
44
+ else if (status.error) {
45
+ onPlaybackStatusUpdateProp === null || onPlaybackStatusUpdateProp === void 0 ? void 0 : onPlaybackStatusUpdateProp({
46
+ isPlaying: false,
47
+ isLoading: false,
48
+ isBuffering: false,
49
+ currentPositionMillis: 0,
50
+ durationMillis: 0,
51
+ bufferedDurationMillis: 0,
52
+ isError: true,
53
+ error: status.error,
54
+ });
55
+ }
56
+ };
57
+ const loadAudio = async () => {
58
+ onPlaybackStatusUpdateProp === null || onPlaybackStatusUpdateProp === void 0 ? void 0 : onPlaybackStatusUpdateProp({
59
+ isPlaying: false,
60
+ isLoading: true,
61
+ isBuffering: false,
62
+ currentPositionMillis: 0,
63
+ durationMillis: 0,
64
+ bufferedDurationMillis: 0,
65
+ isError: false,
66
+ });
67
+ const { sound } = await Audio.Sound.createAsync(source);
68
+ setCurrentSound(sound);
69
+ sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
70
+ };
71
+ const togglePlayback = React.useCallback(async () => {
72
+ //Has to be called everytime a player is played to reconfigure the global Audio config based on each player's configuration
73
+ await updateAudioMode();
74
+ if (isPlaying) {
75
+ await (currentSound === null || currentSound === void 0 ? void 0 : currentSound.pauseAsync());
76
+ }
77
+ else {
78
+ await (currentSound === null || currentSound === void 0 ? void 0 : currentSound.playAsync());
79
+ }
80
+ }, [currentSound, updateAudioMode, isPlaying]);
81
+ const seekToPosition = React.useCallback(async (positionMillis) => {
82
+ await (currentSound === null || currentSound === void 0 ? void 0 : currentSound.setPositionAsync(positionMillis));
83
+ }, [currentSound]);
84
+ useSourceDeepCompareEffect(() => {
85
+ loadAudio();
86
+ // Ignore dependency of loadAudio
87
+ }, [source]);
88
+ React.useEffect(() => {
89
+ return currentSound
90
+ ? () => {
91
+ currentSound.unloadAsync();
92
+ }
93
+ : undefined;
94
+ }, [currentSound]);
95
+ React.useImperativeHandle(ref, () => {
96
+ return {
97
+ seekToPosition,
98
+ togglePlayback,
99
+ };
100
+ }, [seekToPosition, togglePlayback]);
101
+ return null;
102
+ });
103
+ // The source provided into the AudioPlayer can be of type {uri: "some uri"}
104
+ // In the case that this object is created inline, each rerender provides a new source object because a new object is initialized everytime
105
+ // This creates an issue with being a useEffect dependency
106
+ //
107
+ // This creates variants of useEffect that checks deep equality of 'uri' to determine if dependency changed or not
108
+ // Follows: https://stackoverflow.com/a/54096391
109
+ function sourceDeepCompareEquals(a, b) {
110
+ if ((a === null || a === void 0 ? void 0 : a.uri) && (b === null || b === void 0 ? void 0 : b.uri)) {
111
+ return a.uri === b.uri;
112
+ }
113
+ return a === b;
114
+ }
115
+ function useSourceDeepCompareMemoize(value) {
116
+ const ref = React.useRef();
117
+ if (!sourceDeepCompareEquals(value, ref.current)) {
118
+ ref.current = value;
119
+ }
120
+ return ref.current;
121
+ }
122
+ function useSourceDeepCompareEffect(callback, dependencies) {
123
+ // eslint-disable-next-line react-hooks/exhaustive-deps
124
+ React.useEffect(callback, dependencies.map(useSourceDeepCompareMemoize));
125
+ }
126
+ export default HeadlessAudioPlayer;
127
+ //# sourceMappingURL=HeadlessAudioPlayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeadlessAudioPlayer.js","sourceRoot":"","sources":["HeadlessAudioPlayer.tsx"],"names":[],"mappings":"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"}