@draftbit/core 47.2.14-f3390b.2 → 47.3.1-382a1d.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 (37) 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 +125 -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/components/SwipeableItem/SwipeableItem.js +2 -2
  14. package/lib/src/components/SwipeableItem/SwipeableItem.js.map +1 -1
  15. package/lib/src/index.d.ts +1 -0
  16. package/lib/src/index.js +1 -0
  17. package/lib/src/index.js.map +1 -1
  18. package/lib/tsconfig.tsbuildinfo +1 -1
  19. package/package.json +4 -3
  20. package/src/components/AudioPlayer/AudioPlayerCommon.js +2 -0
  21. package/src/components/AudioPlayer/AudioPlayerCommon.js.map +1 -0
  22. package/src/components/AudioPlayer/AudioPlayerCommon.ts +44 -0
  23. package/src/components/AudioPlayer/AudioPlayerWithInterface.js +125 -0
  24. package/src/components/AudioPlayer/AudioPlayerWithInterface.js.map +1 -0
  25. package/src/components/AudioPlayer/AudioPlayerWithInterface.tsx +216 -0
  26. package/src/components/AudioPlayer/HeadlessAudioPlayer.js +127 -0
  27. package/src/components/AudioPlayer/HeadlessAudioPlayer.js.map +1 -0
  28. package/src/components/AudioPlayer/HeadlessAudioPlayer.tsx +180 -0
  29. package/src/components/AudioPlayer/index.js +15 -0
  30. package/src/components/AudioPlayer/index.js.map +1 -0
  31. package/src/components/AudioPlayer/index.tsx +30 -0
  32. package/src/components/SwipeableItem/SwipeableItem.js +2 -2
  33. package/src/components/SwipeableItem/SwipeableItem.js.map +1 -1
  34. package/src/components/SwipeableItem/SwipeableItem.tsx +8 -4
  35. package/src/index.js +1 -0
  36. package/src/index.js.map +1 -1
  37. package/src/index.tsx +5 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftbit/core",
3
- "version": "47.2.14-f3390b.2+f3390b8",
3
+ "version": "47.3.1-382a1d.2+382a1da",
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,7 @@
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.2.14-f3390b.2+f3390b8",
42
+ "@draftbit/types": "^47.3.1-382a1d.2+382a1da",
43
43
  "@material-ui/core": "^4.11.0",
44
44
  "@material-ui/pickers": "^3.2.10",
45
45
  "@react-native-community/slider": "4.2.4",
@@ -48,6 +48,7 @@
48
48
  "color": "^3.1.2",
49
49
  "date-fns": "^2.16.1",
50
50
  "dateformat": "^3.0.3",
51
+ "expo-av": "~13.0.3",
51
52
  "lodash.isnumber": "^3.0.3",
52
53
  "lodash.omit": "^4.5.0",
53
54
  "lodash.tonumber": "^4.0.3",
@@ -77,5 +78,5 @@
77
78
  "node_modules/",
78
79
  "lib/"
79
80
  ],
80
- "gitHead": "f3390b8110a7c417399c1015311a4c2dae37111e"
81
+ "gitHead": "382a1da98daadafb33035bc3cb1c6e9a17522bfb"
81
82
  }
@@ -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
+ sliderColor?: 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,125 @@
1
+ import * as React from "react";
2
+ import { Text, View, TouchableHighlight, 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
+ /**
8
+ * Built on top of HeadlessAudioPlayer to provide a simple interface for playing audio
9
+ */
10
+ const AudioPlayerWithInterface = React.forwardRef(({ style, theme, sliderColor = theme.colors.primary, completedTrackColor = theme.colors.primary, remainingTrackColor = theme.colors.disabled, togglePlaybackIconSize = 24, togglePlaybackIconColor = theme.colors.primary, onPlaybackStatusUpdate: onPlaybackStatusUpdateProp, onPlaybackFinish: onPlaybackFinishProp, hidePlaybackIcon = false, hideDuration = false, hideSlider = false, ...rest }, ref) => {
11
+ const [isPlaying, setIsPlaying] = React.useState(false);
12
+ const [isLoading, setIsLoading] = React.useState(false);
13
+ const [durationMillis, setDurationMillis] = React.useState(1);
14
+ const [isDraggingSlider, setIsDraggingSlider] = React.useState(false);
15
+ const [sliderPositionMillis, setSliderPositionMillis] = React.useState(0);
16
+ const newHeadlessAudioPlayerRef = React.useRef(null);
17
+ // Use the provided ref or default to new ref when not provided
18
+ const headlessAudioPlayerRef = ref
19
+ ? ref
20
+ : newHeadlessAudioPlayerRef;
21
+ const { color, fontFamily, fontWeight, fontSize, lineHeight, letterSpacing, textTransform, textAlign, textDecorationLine, textDecorationColor, textDecorationStyle, ...viewStyles } = StyleSheet.flatten(style || {});
22
+ const textStyles = {
23
+ color,
24
+ fontFamily,
25
+ fontWeight,
26
+ fontSize,
27
+ lineHeight,
28
+ letterSpacing,
29
+ textTransform,
30
+ textAlign,
31
+ textDecorationLine,
32
+ textDecorationColor,
33
+ textDecorationStyle,
34
+ };
35
+ const onPlaybackStatusUpdate = (status) => {
36
+ setIsLoading(status.isLoading);
37
+ setDurationMillis(status.durationMillis);
38
+ setSliderPositionMillis(status.currentPositionMillis);
39
+ setIsPlaying(status.isPlaying);
40
+ onPlaybackStatusUpdateProp === null || onPlaybackStatusUpdateProp === void 0 ? void 0 : onPlaybackStatusUpdateProp(status);
41
+ };
42
+ const onPlaybackFinish = () => {
43
+ var _a, _b;
44
+ setIsPlaying(false);
45
+ setSliderPositionMillis(0);
46
+ (_a = headlessAudioPlayerRef.current) === null || _a === void 0 ? void 0 : _a.togglePlayback();
47
+ (_b = headlessAudioPlayerRef.current) === null || _b === void 0 ? void 0 : _b.seekToPosition(0);
48
+ onPlaybackFinishProp === null || onPlaybackFinishProp === void 0 ? void 0 : onPlaybackFinishProp();
49
+ };
50
+ const onSlidingComplete = (sliderValue) => {
51
+ var _a;
52
+ if (isDraggingSlider) {
53
+ setIsDraggingSlider(false);
54
+ }
55
+ (_a = headlessAudioPlayerRef.current) === null || _a === void 0 ? void 0 : _a.seekToPosition(sliderValue);
56
+ };
57
+ const onSliderChange = () => {
58
+ if (!isDraggingSlider) {
59
+ setIsDraggingSlider(true);
60
+ }
61
+ };
62
+ let iconName;
63
+ if (isLoading) {
64
+ iconName = "loading1";
65
+ }
66
+ else if (isPlaying) {
67
+ iconName = "pause";
68
+ }
69
+ else {
70
+ iconName = "play";
71
+ }
72
+ return (React.createElement(React.Fragment, null,
73
+ React.createElement(HeadlessAudioPlayer, { ...rest, ref: headlessAudioPlayerRef, onPlaybackStatusUpdate: onPlaybackStatusUpdate, onPlaybackFinish: onPlaybackFinish }),
74
+ React.createElement(View, { style: [
75
+ {
76
+ backgroundColor: theme.colors.background,
77
+ borderColor: theme.colors.disabled,
78
+ },
79
+ styles.container,
80
+ viewStyles,
81
+ ] },
82
+ !hidePlaybackIcon && (React.createElement(TouchableHighlight, { onPress: () => { var _a; return (_a = headlessAudioPlayerRef.current) === null || _a === void 0 ? void 0 : _a.togglePlayback(); }, style: styles.spacingEnd },
83
+ React.createElement(AntDesign, { name: iconName, size: togglePlaybackIconSize, color: togglePlaybackIconColor }))),
84
+ !hideDuration && (React.createElement(Text, { style: [
85
+ { color: theme.colors.strong },
86
+ styles.spacingEnd,
87
+ { ...textStyles },
88
+ ] },
89
+ formatDuration(sliderPositionMillis !== null && sliderPositionMillis !== void 0 ? sliderPositionMillis : 0),
90
+ " /",
91
+ " ",
92
+ formatDuration(durationMillis || 0))),
93
+ !hideSlider && (React.createElement(Slider, { style: styles.slider, minimumTrackTintColor: completedTrackColor, maximumTrackTintColor: remainingTrackColor, thumbTintColor: sliderColor, minimumValue: 0, value: sliderPositionMillis, maximumValue: durationMillis, onValueChange: onSliderChange, onSlidingComplete: onSlidingComplete })))));
94
+ });
95
+ const styles = StyleSheet.create({
96
+ container: {
97
+ padding: 8,
98
+ flexDirection: "row",
99
+ alignItems: "center",
100
+ borderRadius: 8,
101
+ borderWidth: 1,
102
+ },
103
+ spacingEnd: {
104
+ marginEnd: 8,
105
+ },
106
+ slider: {
107
+ flex: 1,
108
+ },
109
+ });
110
+ function formatDuration(duration) {
111
+ if (duration === 0)
112
+ return "00:00";
113
+ const seconds = Math.floor((duration / 1000) % 60);
114
+ const minutes = Math.floor((duration / (1000 * 60)) % 60);
115
+ const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
116
+ const renderedHours = hours < 10 ? "0" + hours : hours;
117
+ const renderedMinutes = minutes < 10 ? "0" + minutes : minutes;
118
+ const renderedSeconds = seconds < 10 ? "0" + seconds : seconds;
119
+ if (hours > 0) {
120
+ return renderedHours + ":" + renderedMinutes + ":" + renderedSeconds;
121
+ }
122
+ return renderedMinutes + ":" + renderedSeconds;
123
+ }
124
+ export default withTheme(AudioPlayerWithInterface);
125
+ //# 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,216 @@
1
+ import * as React from "react";
2
+ import { Text, View, TouchableHighlight, 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
+
14
+ /**
15
+ * Built on top of HeadlessAudioPlayer to provide a simple interface for playing audio
16
+ */
17
+ const AudioPlayerWithInterface = React.forwardRef<
18
+ HeadlessAudioPlayerRef,
19
+ AudioPlayerInterfaceProps & HeadlessAudioPlayerProps
20
+ >(
21
+ (
22
+ {
23
+ style,
24
+ theme,
25
+ sliderColor = theme.colors.primary,
26
+ completedTrackColor = theme.colors.primary,
27
+ remainingTrackColor = theme.colors.disabled,
28
+ togglePlaybackIconSize = 24,
29
+ togglePlaybackIconColor = theme.colors.primary,
30
+ onPlaybackStatusUpdate: onPlaybackStatusUpdateProp,
31
+ onPlaybackFinish: onPlaybackFinishProp,
32
+ hidePlaybackIcon = false,
33
+ hideDuration = false,
34
+ hideSlider = false,
35
+ ...rest
36
+ },
37
+ ref
38
+ ) => {
39
+ const [isPlaying, setIsPlaying] = React.useState(false);
40
+ const [isLoading, setIsLoading] = React.useState(false);
41
+ const [durationMillis, setDurationMillis] = React.useState<
42
+ number | undefined
43
+ >(1);
44
+ const [isDraggingSlider, setIsDraggingSlider] = React.useState(false);
45
+ const [sliderPositionMillis, setSliderPositionMillis] = React.useState(0);
46
+ const newHeadlessAudioPlayerRef =
47
+ React.useRef<HeadlessAudioPlayerRef>(null);
48
+
49
+ // Use the provided ref or default to new ref when not provided
50
+ const headlessAudioPlayerRef = ref
51
+ ? (ref as React.RefObject<HeadlessAudioPlayerRef>)
52
+ : newHeadlessAudioPlayerRef;
53
+
54
+ const {
55
+ color,
56
+ fontFamily,
57
+ fontWeight,
58
+ fontSize,
59
+ lineHeight,
60
+ letterSpacing,
61
+ textTransform,
62
+ textAlign,
63
+ textDecorationLine,
64
+ textDecorationColor,
65
+ textDecorationStyle,
66
+ ...viewStyles
67
+ } = StyleSheet.flatten(style || {});
68
+
69
+ const textStyles = {
70
+ color,
71
+ fontFamily,
72
+ fontWeight,
73
+ fontSize,
74
+ lineHeight,
75
+ letterSpacing,
76
+ textTransform,
77
+ textAlign,
78
+ textDecorationLine,
79
+ textDecorationColor,
80
+ textDecorationStyle,
81
+ };
82
+
83
+ const onPlaybackStatusUpdate = (status: AudioPlayerStatus) => {
84
+ setIsLoading(status.isLoading);
85
+ setDurationMillis(status.durationMillis);
86
+ setSliderPositionMillis(status.currentPositionMillis);
87
+ setIsPlaying(status.isPlaying);
88
+ onPlaybackStatusUpdateProp?.(status);
89
+ };
90
+
91
+ const onPlaybackFinish = () => {
92
+ setIsPlaying(false);
93
+ setSliderPositionMillis(0);
94
+ headlessAudioPlayerRef.current?.togglePlayback();
95
+ headlessAudioPlayerRef.current?.seekToPosition(0);
96
+ onPlaybackFinishProp?.();
97
+ };
98
+
99
+ const onSlidingComplete = (sliderValue: number) => {
100
+ if (isDraggingSlider) {
101
+ setIsDraggingSlider(false);
102
+ }
103
+ headlessAudioPlayerRef.current?.seekToPosition(sliderValue);
104
+ };
105
+
106
+ const onSliderChange = () => {
107
+ if (!isDraggingSlider) {
108
+ setIsDraggingSlider(true);
109
+ }
110
+ };
111
+
112
+ let iconName;
113
+ if (isLoading) {
114
+ iconName = "loading1";
115
+ } else if (isPlaying) {
116
+ iconName = "pause";
117
+ } else {
118
+ iconName = "play";
119
+ }
120
+
121
+ return (
122
+ <>
123
+ <HeadlessAudioPlayer
124
+ {...rest}
125
+ ref={headlessAudioPlayerRef}
126
+ onPlaybackStatusUpdate={onPlaybackStatusUpdate}
127
+ onPlaybackFinish={onPlaybackFinish}
128
+ />
129
+ <View
130
+ style={[
131
+ {
132
+ backgroundColor: theme.colors.background,
133
+ borderColor: theme.colors.disabled,
134
+ },
135
+ styles.container,
136
+ viewStyles,
137
+ ]}
138
+ >
139
+ {!hidePlaybackIcon && (
140
+ <TouchableHighlight
141
+ onPress={() => headlessAudioPlayerRef.current?.togglePlayback()}
142
+ style={styles.spacingEnd}
143
+ >
144
+ <AntDesign
145
+ name={iconName as any}
146
+ size={togglePlaybackIconSize}
147
+ color={togglePlaybackIconColor}
148
+ />
149
+ </TouchableHighlight>
150
+ )}
151
+ {!hideDuration && (
152
+ <Text
153
+ style={[
154
+ { color: theme.colors.strong },
155
+ styles.spacingEnd,
156
+ { ...textStyles },
157
+ ]}
158
+ >
159
+ {formatDuration(sliderPositionMillis ?? 0)} /{" "}
160
+ {formatDuration(durationMillis || 0)}
161
+ </Text>
162
+ )}
163
+ {!hideSlider && (
164
+ <Slider
165
+ style={styles.slider}
166
+ minimumTrackTintColor={completedTrackColor}
167
+ maximumTrackTintColor={remainingTrackColor}
168
+ thumbTintColor={sliderColor}
169
+ minimumValue={0}
170
+ value={sliderPositionMillis}
171
+ maximumValue={durationMillis}
172
+ onValueChange={onSliderChange}
173
+ onSlidingComplete={onSlidingComplete}
174
+ />
175
+ )}
176
+ </View>
177
+ </>
178
+ );
179
+ }
180
+ );
181
+
182
+ const styles = StyleSheet.create({
183
+ container: {
184
+ padding: 8,
185
+ flexDirection: "row",
186
+ alignItems: "center",
187
+ borderRadius: 8,
188
+ borderWidth: 1,
189
+ },
190
+ spacingEnd: {
191
+ marginEnd: 8,
192
+ },
193
+ slider: {
194
+ flex: 1,
195
+ },
196
+ });
197
+
198
+ function formatDuration(duration: number) {
199
+ if (duration === 0) return "00:00";
200
+
201
+ const seconds = Math.floor((duration / 1000) % 60);
202
+ const minutes = Math.floor((duration / (1000 * 60)) % 60);
203
+ const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
204
+
205
+ const renderedHours = hours < 10 ? "0" + hours : hours;
206
+ const renderedMinutes = minutes < 10 ? "0" + minutes : minutes;
207
+ const renderedSeconds = seconds < 10 ? "0" + seconds : seconds;
208
+
209
+ if (hours > 0) {
210
+ return renderedHours + ":" + renderedMinutes + ":" + renderedSeconds;
211
+ }
212
+
213
+ return renderedMinutes + ":" + renderedSeconds;
214
+ }
215
+
216
+ 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":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EACL,KAAK,EAEL,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AAMjB;;;GAGG;AACH,MAAM,mBAAmB,GAAG,KAAK,CAAC,UAAU,CAI1C,CACE,EACE,MAAM,EACN,gBAAgB,GAAG,cAAc,EACjC,iBAAiB,GAAG,KAAK,EACzB,oBAAoB,GAAG,KAAK,EAC5B,0BAA0B,GAAG,KAAK,EAClC,sBAAsB,EAAE,0BAA0B,EAClD,gBAAgB,GACjB,EACD,GAAG,EACH,EAAE;IACF,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAe,CAAC;IACtE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAExD,MAAM,eAAe,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE;QACnD,MAAM,KAAK,CAAC,iBAAiB,CAAC;YAC5B,uBAAuB,EAAE,iBAAiB;YAC1C,mBAAmB,EACjB,gBAAgB,KAAK,cAAc;gBACjC,CAAC,CAAC,mBAAmB,CAAC,UAAU;gBAChC,CAAC,CAAC,mBAAmB,CAAC,QAAQ;YAClC,uBAAuB,EACrB,gBAAgB,KAAK,cAAc;gBACjC,CAAC,CAAC,uBAAuB,CAAC,UAAU;gBACpC,CAAC,CAAC,uBAAuB,CAAC,QAAQ;YACtC,oBAAoB;YACpB,0BAA0B;SAC3B,CAAC,CAAC;IACL,CAAC,EAAE;QACD,gBAAgB;QAChB,iBAAiB;QACjB,oBAAoB;QACpB,0BAA0B;KAC3B,CAAC,CAAC;IAEH,MAAM,sBAAsB,GAAG,CAAC,MAAwB,EAAE,EAAE;QAC1D,IAAI,MAAM,CAAC,QAAQ,EAAE;YACnB,0BAA0B,aAA1B,0BAA0B,uBAA1B,0BAA0B,CAAG;gBAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,SAAS,EAAE,KAAK;gBAChB,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,qBAAqB,EAAE,MAAM,CAAC,cAAc,IAAI,CAAC;gBACjD,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,CAAC;gBAC1C,sBAAsB,EAAE,MAAM,CAAC,sBAAsB,IAAI,CAAC;gBAC1D,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,aAAa,EAAE;gBACxB,gBAAgB,aAAhB,gBAAgB,uBAAhB,gBAAgB,EAAI,CAAC;aACtB;YAED,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;SAChC;aAAM,IAAI,MAAM,CAAC,KAAK,EAAE;YACvB,0BAA0B,aAA1B,0BAA0B,uBAA1B,0BAA0B,CAAG;gBAC3B,SAAS,EAAE,KAAK;gBAChB,SAAS,EAAE,KAAK;gBAChB,WAAW,EAAE,KAAK;gBAClB,qBAAqB,EAAE,CAAC;gBACxB,cAAc,EAAE,CAAC;gBACjB,sBAAsB,EAAE,CAAC;gBACzB,OAAO,EAAE,IAAI;gBACb,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC,CAAC;SACJ;IACH,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;QAC3B,0BAA0B,aAA1B,0BAA0B,uBAA1B,0BAA0B,CAAG;YAC3B,SAAS,EAAE,KAAK;YAChB,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,KAAK;YAClB,qBAAqB,EAAE,CAAC;YACxB,cAAc,EAAE,CAAC;YACjB,sBAAsB,EAAE,CAAC;YACzB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACxD,eAAe,CAAC,KAAK,CAAC,CAAC;QACvB,KAAK,CAAC,yBAAyB,CAAC,sBAAsB,CAAC,CAAC;IAC1D,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE;QAClD,2HAA2H;QAC3H,MAAM,eAAe,EAAE,CAAC;QAExB,IAAI,SAAS,EAAE;YACb,MAAM,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,UAAU,EAAE,CAAA,CAAC;SAClC;aAAM;YACL,MAAM,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,SAAS,EAAE,CAAA,CAAC;SACjC;IACH,CAAC,EAAE,CAAC,YAAY,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;IAE/C,MAAM,cAAc,GAAG,KAAK,CAAC,WAAW,CACtC,KAAK,EAAE,cAAsB,EAAE,EAAE;QAC/B,MAAM,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA,CAAC;IACvD,CAAC,EACD,CAAC,YAAY,CAAC,CACf,CAAC;IAEF,0BAA0B,CAAC,GAAG,EAAE;QAC9B,SAAS,EAAE,CAAC;QAEZ,iCAAiC;IACnC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,OAAO,YAAY;YACjB,CAAC,CAAC,GAAG,EAAE;gBACH,YAAY,CAAC,WAAW,EAAE,CAAC;YAC7B,CAAC;YACH,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,KAAK,CAAC,mBAAmB,CACvB,GAAG,EACH,GAAG,EAAE;QACH,OAAO;YACL,cAAc;YACd,cAAc;SACf,CAAC;IACJ,CAAC,EACD,CAAC,cAAc,EAAE,cAAc,CAAC,CACjC,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC,CACF,CAAC;AAEF,4EAA4E;AAC5E,2IAA2I;AAC3I,0DAA0D;AAC1D,EAAE;AACF,kHAAkH;AAClH,gDAAgD;AAChD,SAAS,uBAAuB,CAAC,CAAM,EAAE,CAAM;IAC7C,IAAI,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,GAAG,MAAI,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,GAAG,CAAA,EAAE;QACpB,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC;KACxB;IACD,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAU;IAC7C,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,uBAAuB,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE;QAChD,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;KACrB;IACD,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC;AAED,SAAS,0BAA0B,CACjC,QAA8B,EAC9B,YAAkC;IAElC,uDAAuD;IACvD,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,eAAe,mBAAmB,CAAC"}