@draftbit/core 54.0.4-11e01d.2 → 54.0.4-6c949a.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 (22) hide show
  1. package/lib/commonjs/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.js +1 -1
  2. package/lib/commonjs/components/MediaPlayer/MediaPlaybackWrapper.js +1 -1
  3. package/lib/commonjs/components/MediaPlayer/MediaPlayerCommon.js +1 -1
  4. package/lib/commonjs/components/MediaPlayer/VideoPlayer/VideoPlayer.js +1 -1
  5. package/lib/typescript/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.d.ts +1 -3
  6. package/lib/typescript/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.js +54 -69
  7. package/lib/typescript/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.js.map +1 -1
  8. package/lib/typescript/src/components/MediaPlayer/MediaPlaybackWrapper.d.ts +2 -3
  9. package/lib/typescript/src/components/MediaPlayer/MediaPlaybackWrapper.js +21 -19
  10. package/lib/typescript/src/components/MediaPlayer/MediaPlaybackWrapper.js.map +1 -1
  11. package/lib/typescript/src/components/MediaPlayer/MediaPlayerCommon.d.ts +4 -5
  12. package/lib/typescript/src/components/MediaPlayer/MediaPlayerCommon.js +26 -3
  13. package/lib/typescript/src/components/MediaPlayer/MediaPlayerCommon.js.map +1 -1
  14. package/lib/typescript/src/components/MediaPlayer/VideoPlayer/VideoPlayer.d.ts +4 -14
  15. package/lib/typescript/src/components/MediaPlayer/VideoPlayer/VideoPlayer.js +62 -125
  16. package/lib/typescript/src/components/MediaPlayer/VideoPlayer/VideoPlayer.js.map +1 -1
  17. package/lib/typescript/tsconfig.tsbuildinfo +1 -1
  18. package/package.json +4 -5
  19. package/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.tsx +72 -84
  20. package/src/components/MediaPlayer/MediaPlaybackWrapper.tsx +24 -21
  21. package/src/components/MediaPlayer/MediaPlayerCommon.ts +34 -8
  22. package/src/components/MediaPlayer/VideoPlayer/VideoPlayer.tsx +86 -213
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftbit/core",
3
- "version": "54.0.4-11e01d.2+11e01d6",
3
+ "version": "54.0.4-6c949a.2+6c949af",
4
4
  "description": "Core (non-native) Components",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "types": "lib/typescript/src/index.d.ts",
@@ -42,7 +42,7 @@
42
42
  "homepage": "https://github.com/draftbit/react-native-jigsaw#readme",
43
43
  "dependencies": {
44
44
  "@draftbit/react-theme-provider": "^2.1.1",
45
- "@draftbit/theme": "^54.0.4-11e01d.2+11e01d6",
45
+ "@draftbit/theme": "^54.0.4-6c949a.2+6c949af",
46
46
  "@emotion/react": "^11.13.5",
47
47
  "@emotion/styled": "^11.13.5",
48
48
  "@expo/vector-icons": "^15.0.3",
@@ -57,9 +57,8 @@
57
57
  "color": "^4.2.3",
58
58
  "date-fns": "^4.1.0",
59
59
  "dateformat": "^5.0.3",
60
- "expo-audio": "~1.1.1",
60
+ "expo-av": "~16.0.8",
61
61
  "expo-image": "~3.0.11",
62
- "expo-video": "~3.0.16",
63
62
  "lodash.isequal": "^4.5.0",
64
63
  "lodash.isnumber": "^3.0.3",
65
64
  "lodash.omit": "^4.5.0",
@@ -123,5 +122,5 @@
123
122
  ],
124
123
  "testEnvironment": "node"
125
124
  },
126
- "gitHead": "11e01d6ca5cc2e0e1cdeca926659e65fbe55cca2"
125
+ "gitHead": "6c949afa2c601852d436883343b61091d025d849"
127
126
  }
@@ -1,12 +1,17 @@
1
1
  import * as React from "react";
2
- import { useAudioPlayer, setAudioModeAsync, AudioStatus } from "expo-audio";
2
+ import {
3
+ Audio,
4
+ AVPlaybackStatus,
5
+ InterruptionModeIOS,
6
+ InterruptionModeAndroid,
7
+ } from "expo-av";
3
8
  import { HeadlessAudioPlayerProps } from "./AudioPlayerCommon";
4
9
  import {
10
+ mapToMediaPlayerStatus,
5
11
  normalizeBase64Source,
6
- useSourceDeepCompareMemoize,
7
12
  useSourceDeepCompareEffect,
8
13
  } from "../MediaPlayerCommon";
9
- import type { MediaPlayerRef, MediaPlayerStatus } from "../MediaPlayerCommon";
14
+ import type { MediaPlayerRef } from "../MediaPlayerCommon";
10
15
  import MediaPlaybackWrapper from "../MediaPlaybackWrapper";
11
16
 
12
17
  /**
@@ -31,70 +36,38 @@ const HeadlessAudioPlayer = React.forwardRef<
31
36
  },
32
37
  ref
33
38
  ) => {
34
- const stableSource = useSourceDeepCompareMemoize(
35
- normalizeBase64Source(source, "audio")
36
- );
37
- const player = useAudioPlayer(stableSource);
38
-
39
+ const [currentSound, setCurrentSound] = React.useState<Audio.Sound>();
39
40
  const [isPlaying, setIsPlaying] = React.useState(false);
40
41
 
41
42
  React.useEffect(() => {
42
- player.loop = isLooping;
43
- }, [player, isLooping]);
44
-
45
- React.useEffect(() => {
46
- player.volume = volume;
47
- }, [player, volume]);
48
-
49
- // Emit loading state immediately
50
- React.useEffect(() => {
51
- onPlaybackStatusUpdateProp?.({
52
- isPlaying: false,
53
- isLoading: true,
54
- isBuffering: false,
55
- currentPositionMillis: 0,
56
- durationMillis: 0,
57
- bufferedDurationMillis: 0,
58
- isError: false,
59
- });
60
- }, []);
43
+ if (
44
+ currentSound &&
45
+ typeof currentSound?.setIsLoopingAsync === "function"
46
+ ) {
47
+ currentSound.setIsLoopingAsync(isLooping);
48
+ }
49
+ }, [currentSound, isLooping]);
61
50
 
62
51
  React.useEffect(() => {
63
- const subscription = player.addListener(
64
- "playbackStatusUpdate",
65
- (status) => {
66
- const mappedStatus = mapToMediaPlayerStatus(status);
67
- onPlaybackStatusUpdateProp?.(mappedStatus);
68
-
69
- if (status.isLoaded) {
70
- if (status.didJustFinish && !isLooping) {
71
- onPlaybackFinish?.();
72
- }
73
- setIsPlaying(status.playing);
74
- }
75
- }
76
- );
77
- return () => subscription.remove();
78
- }, []);
79
-
80
- // Replace source when it changes (deep comparison on URI to avoid unnecessary reloads)
81
- const isFirstSourceRender = React.useRef(true);
82
- useSourceDeepCompareEffect(() => {
83
- if (isFirstSourceRender.current) {
84
- isFirstSourceRender.current = false;
85
- return;
52
+ if (currentSound && typeof currentSound?.setVolumeAsync === "function") {
53
+ currentSound.setVolumeAsync(volume);
86
54
  }
87
- player.replace(normalizeBase64Source(source, "audio") as any);
88
- }, [source]);
55
+ }, [currentSound, volume]);
89
56
 
90
57
  const updateAudioMode = React.useCallback(async () => {
91
58
  try {
92
- await setAudioModeAsync({
93
- shouldPlayInBackground: playsInBackground,
94
- interruptionMode:
95
- interruptionMode === "lower volume" ? "duckOthers" : "doNotMix",
96
- playsInSilentMode: playsInSilentModeIOS,
97
- shouldRouteThroughEarpiece: playThroughEarpieceAndroid,
59
+ await Audio.setAudioModeAsync({
60
+ staysActiveInBackground: playsInBackground,
61
+ interruptionModeIOS:
62
+ interruptionMode === "lower volume"
63
+ ? InterruptionModeIOS.DuckOthers
64
+ : InterruptionModeIOS.DoNotMix,
65
+ interruptionModeAndroid:
66
+ interruptionMode === "lower volume"
67
+ ? InterruptionModeAndroid.DuckOthers
68
+ : InterruptionModeAndroid.DoNotMix,
69
+ playsInSilentModeIOS,
70
+ playThroughEarpieceAndroid,
98
71
  });
99
72
  } catch (e) {
100
73
  if ((e as { code?: string })?.code === "E_AUDIO_AUDIOMODE") {
@@ -115,44 +88,59 @@ const HeadlessAudioPlayer = React.forwardRef<
115
88
  playThroughEarpieceAndroid,
116
89
  ]);
117
90
 
91
+ const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
92
+ const mappedStatus = mapToMediaPlayerStatus(status);
93
+ onPlaybackStatusUpdateProp?.(mappedStatus);
94
+
95
+ if (status.isLoaded) {
96
+ if (status.didJustFinish) {
97
+ if (isLooping) {
98
+ return;
99
+ }
100
+ onPlaybackFinish?.();
101
+ }
102
+ setIsPlaying(status.isPlaying);
103
+ }
104
+ };
105
+
118
106
  const onTogglePlayback = () => {
119
- // Has to be called everytime a player is played to reconfigure the global Audio config based on each player's configuration
107
+ //Has to be called everytime a player is played to reconfigure the global Audio config based on each player's configuration
120
108
  updateAudioMode();
121
109
  };
122
110
 
111
+ const loadAudio = async () => {
112
+ onPlaybackStatusUpdateProp?.({
113
+ isPlaying: false,
114
+ isLoading: true,
115
+ isBuffering: false,
116
+ currentPositionMillis: 0,
117
+ durationMillis: 0,
118
+ bufferedDurationMillis: 0,
119
+ isError: false,
120
+ });
121
+
122
+ const finalSource = await normalizeBase64Source(source, "audio");
123
+
124
+ const { sound } = await Audio.Sound.createAsync(finalSource);
125
+ setCurrentSound(sound);
126
+ sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
127
+ };
128
+
129
+ useSourceDeepCompareEffect(() => {
130
+ loadAudio();
131
+
132
+ // Ignore dependency of loadAudio
133
+ }, [source]);
134
+
123
135
  return (
124
136
  <MediaPlaybackWrapper
125
137
  ref={ref}
126
138
  isPlaying={isPlaying}
127
- player={player}
139
+ media={currentSound}
128
140
  onTogglePlayback={onTogglePlayback}
129
141
  />
130
142
  );
131
143
  }
132
144
  );
133
145
 
134
- export function mapToMediaPlayerStatus(status: AudioStatus): MediaPlayerStatus {
135
- if (status.isLoaded) {
136
- return {
137
- isPlaying: status.playing,
138
- isLoading: false,
139
- isBuffering: status.isBuffering,
140
- currentPositionMillis: status.currentTime * 1000,
141
- durationMillis: status.duration * 1000,
142
- bufferedDurationMillis: status.duration * 1000,
143
- isError: false,
144
- };
145
- }
146
-
147
- return {
148
- isPlaying: false,
149
- isLoading: true,
150
- isBuffering: false,
151
- currentPositionMillis: 0,
152
- durationMillis: 0,
153
- bufferedDurationMillis: 0,
154
- isError: false,
155
- };
156
- }
157
-
158
146
  export default HeadlessAudioPlayer;
@@ -1,11 +1,10 @@
1
1
  import * as React from "react";
2
- import type { AudioPlayer } from "expo-audio";
3
- import type { VideoPlayer } from "expo-video";
2
+ import type { Playback } from "expo-av/src/AV";
4
3
 
5
4
  import type { MediaPlayerRef } from "./MediaPlayerCommon";
6
5
 
7
6
  interface MediaPlaybackWrapperProps {
8
- player?: AudioPlayer | VideoPlayer;
7
+ media?: Playback;
9
8
  isPlaying?: boolean;
10
9
  onTogglePlayback?: () => void;
11
10
  }
@@ -16,38 +15,42 @@ interface MediaPlaybackWrapperProps {
16
15
  const MediaPlaybackWrapper = React.forwardRef<
17
16
  MediaPlayerRef,
18
17
  React.PropsWithChildren<MediaPlaybackWrapperProps>
19
- >(({ player, isPlaying, onTogglePlayback, children }, ref) => {
20
- const togglePlayback = React.useCallback(() => {
18
+ >(({ media, isPlaying, onTogglePlayback, children }, ref) => {
19
+ const togglePlayback = React.useCallback(async () => {
21
20
  onTogglePlayback?.();
22
21
 
23
22
  if (isPlaying) {
24
- player?.pause();
23
+ await media?.pauseAsync();
25
24
  } else {
26
- player?.play();
25
+ await media?.playAsync();
27
26
  }
28
- }, [isPlaying, onTogglePlayback]);
27
+ }, [media, isPlaying, onTogglePlayback]);
29
28
 
30
- const pause = React.useCallback(() => {
29
+ const pause = React.useCallback(async () => {
31
30
  onTogglePlayback?.();
32
- player?.pause();
33
- }, [player, onTogglePlayback]);
31
+ await media?.pauseAsync();
32
+ }, [media, onTogglePlayback]);
34
33
 
35
- const play = React.useCallback(() => {
34
+ const play = React.useCallback(async () => {
36
35
  onTogglePlayback?.();
37
- player?.play();
38
- }, [player, onTogglePlayback]);
36
+ await media?.playAsync();
37
+ }, [media, onTogglePlayback]);
39
38
 
40
39
  const seekToPosition = React.useCallback(
41
- (positionMillis: number) => {
42
- if (typeof (player as any)?.seekTo === "function") {
43
- (player as AudioPlayer).seekTo(positionMillis / 1000);
44
- } else if (player) {
45
- player.currentTime = positionMillis / 1000;
46
- }
40
+ async (positionMillis: number) => {
41
+ await media?.setPositionAsync(positionMillis);
47
42
  },
48
- [player]
43
+ [media]
49
44
  );
50
45
 
46
+ React.useEffect(() => {
47
+ return media
48
+ ? () => {
49
+ media.unloadAsync();
50
+ }
51
+ : undefined;
52
+ }, [media]);
53
+
51
54
  React.useImperativeHandle(
52
55
  ref,
53
56
  () => ({
@@ -1,5 +1,4 @@
1
- import { AudioSource } from "expo-audio";
2
- import { VideoSource } from "expo-video";
1
+ import { AVPlaybackSource, AVPlaybackStatus } from "expo-av";
3
2
  import { v4 as uuid } from "uuid";
4
3
  import { Platform } from "react-native";
5
4
  import React from "react";
@@ -25,7 +24,34 @@ export interface MediaPlayerRef {
25
24
  export interface MediaPlayerProps {
26
25
  onPlaybackStatusUpdate?: (status: MediaPlayerStatus) => void;
27
26
  onPlaybackFinish?: () => void;
28
- source: AudioSource | VideoSource;
27
+ source: AVPlaybackSource;
28
+ }
29
+
30
+ export function mapToMediaPlayerStatus(
31
+ status: AVPlaybackStatus
32
+ ): MediaPlayerStatus {
33
+ if (status.isLoaded) {
34
+ return {
35
+ isPlaying: status.isPlaying,
36
+ isLoading: false,
37
+ isBuffering: status.isBuffering,
38
+ currentPositionMillis: status.positionMillis || 0,
39
+ durationMillis: status.durationMillis || 0,
40
+ bufferedDurationMillis: status.playableDurationMillis || 0,
41
+ isError: false,
42
+ };
43
+ }
44
+
45
+ return {
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
+ };
29
55
  }
30
56
 
31
57
  const URL_REGEX =
@@ -34,14 +60,14 @@ const URL_REGEX =
34
60
  /**
35
61
  * Base64 strings are not playable on iOS and needs to be saved to a file before playing
36
62
  */
37
- export function normalizeBase64Source(
38
- source: AudioSource | VideoSource,
63
+ export async function normalizeBase64Source(
64
+ source: AVPlaybackSource,
39
65
  type: "audio" | "video"
40
- ): AudioSource | VideoSource {
66
+ ): Promise<AVPlaybackSource> {
41
67
  const uri: string | undefined = (source as any)?.uri;
42
68
 
43
69
  if (Platform.OS === "ios" && uri && !uri.match(URL_REGEX)) {
44
- const { File, Paths } = require("expo-file-system");
70
+ const { File, Paths } = await import("expo-file-system");
45
71
 
46
72
  const defaultMimeType = type === "audio" ? "wav" : "mp4";
47
73
  const mimeType = uri.startsWith(`data:${type}/`)
@@ -74,7 +100,7 @@ function sourceDeepCompareEquals(a: any, b: any) {
74
100
  return a === b;
75
101
  }
76
102
 
77
- export function useSourceDeepCompareMemoize(value: any) {
103
+ function useSourceDeepCompareMemoize(value: any) {
78
104
  const ref = React.useRef<any>(undefined);
79
105
  if (!sourceDeepCompareEquals(value, ref.current)) {
80
106
  ref.current = value;