@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.
- package/lib/commonjs/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.js +1 -1
- package/lib/commonjs/components/MediaPlayer/MediaPlaybackWrapper.js +1 -1
- package/lib/commonjs/components/MediaPlayer/MediaPlayerCommon.js +1 -1
- package/lib/commonjs/components/MediaPlayer/VideoPlayer/VideoPlayer.js +1 -1
- package/lib/typescript/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.d.ts +1 -3
- package/lib/typescript/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.js +54 -69
- package/lib/typescript/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.js.map +1 -1
- package/lib/typescript/src/components/MediaPlayer/MediaPlaybackWrapper.d.ts +2 -3
- package/lib/typescript/src/components/MediaPlayer/MediaPlaybackWrapper.js +21 -19
- package/lib/typescript/src/components/MediaPlayer/MediaPlaybackWrapper.js.map +1 -1
- package/lib/typescript/src/components/MediaPlayer/MediaPlayerCommon.d.ts +4 -5
- package/lib/typescript/src/components/MediaPlayer/MediaPlayerCommon.js +26 -3
- package/lib/typescript/src/components/MediaPlayer/MediaPlayerCommon.js.map +1 -1
- package/lib/typescript/src/components/MediaPlayer/VideoPlayer/VideoPlayer.d.ts +4 -14
- package/lib/typescript/src/components/MediaPlayer/VideoPlayer/VideoPlayer.js +62 -125
- package/lib/typescript/src/components/MediaPlayer/VideoPlayer/VideoPlayer.js.map +1 -1
- package/lib/typescript/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -5
- package/src/components/MediaPlayer/AudioPlayer/HeadlessAudioPlayer.tsx +72 -84
- package/src/components/MediaPlayer/MediaPlaybackWrapper.tsx +24 -21
- package/src/components/MediaPlayer/MediaPlayerCommon.ts +34 -8
- 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-
|
|
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-
|
|
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-
|
|
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": "
|
|
125
|
+
"gitHead": "6c949afa2c601852d436883343b61091d025d849"
|
|
127
126
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
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
|
|
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
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
88
|
-
}, [source]);
|
|
55
|
+
}, [currentSound, volume]);
|
|
89
56
|
|
|
90
57
|
const updateAudioMode = React.useCallback(async () => {
|
|
91
58
|
try {
|
|
92
|
-
await setAudioModeAsync({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
interruptionMode === "lower volume"
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
>(({
|
|
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
|
-
|
|
23
|
+
await media?.pauseAsync();
|
|
25
24
|
} else {
|
|
26
|
-
|
|
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
|
-
|
|
33
|
-
}, [
|
|
31
|
+
await media?.pauseAsync();
|
|
32
|
+
}, [media, onTogglePlayback]);
|
|
34
33
|
|
|
35
|
-
const play = React.useCallback(() => {
|
|
34
|
+
const play = React.useCallback(async () => {
|
|
36
35
|
onTogglePlayback?.();
|
|
37
|
-
|
|
38
|
-
}, [
|
|
36
|
+
await media?.playAsync();
|
|
37
|
+
}, [media, onTogglePlayback]);
|
|
39
38
|
|
|
40
39
|
const seekToPosition = React.useCallback(
|
|
41
|
-
(positionMillis: number) => {
|
|
42
|
-
|
|
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
|
-
[
|
|
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 {
|
|
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:
|
|
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:
|
|
63
|
+
export async function normalizeBase64Source(
|
|
64
|
+
source: AVPlaybackSource,
|
|
39
65
|
type: "audio" | "video"
|
|
40
|
-
):
|
|
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 } =
|
|
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
|
-
|
|
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;
|