@granite-js/react-native 0.1.1 → 0.1.3
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/CHANGELOG.md +28 -0
- package/dist/index.d.ts +3 -0
- package/dist/keyboard/index.d.ts +1 -0
- package/dist/status-bar/StatusBar.android.d.ts +3 -0
- package/dist/status-bar/StatusBar.ios.d.ts +3 -0
- package/dist/status-bar/index.d.ts +2 -0
- package/dist/status-bar/types.d.ts +20 -0
- package/dist/status-bar/utils.d.ts +3 -0
- package/dist/video/Video.d.ts +67 -0
- package/dist/video/index.d.ts +1 -0
- package/dist/video/instance.d.ts +9 -0
- package/package.json +10 -8
- package/src/index.ts +4 -0
- package/src/keyboard/index.ts +1 -0
- package/src/status-bar/StatusBar.android.tsx +36 -0
- package/src/status-bar/StatusBar.d.ts +4 -0
- package/src/status-bar/StatusBar.ios.tsx +34 -0
- package/src/status-bar/index.ts +2 -0
- package/src/status-bar/types.ts +21 -0
- package/src/status-bar/utils.ts +20 -0
- package/src/video/Video.tsx +104 -0
- package/src/video/index.ts +1 -0
- package/src/video/instance.tsx +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @granite-js/react-native
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1adad4c: add StatusBar API
|
|
8
|
+
- a4783c8: expose keyboard utils
|
|
9
|
+
- @granite-js/cli@0.1.3
|
|
10
|
+
- @granite-js/image@0.1.3
|
|
11
|
+
- @granite-js/jest@0.1.3
|
|
12
|
+
- @granite-js/lottie@0.1.3
|
|
13
|
+
- @granite-js/mpack@0.1.3
|
|
14
|
+
- @granite-js/native@0.1.3
|
|
15
|
+
- @granite-js/style-utils@0.1.3
|
|
16
|
+
|
|
17
|
+
## 0.1.2
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- e1384cf: re-add previously excluded native module packages
|
|
22
|
+
- Updated dependencies [e1384cf]
|
|
23
|
+
- @granite-js/lottie@0.1.2
|
|
24
|
+
- @granite-js/native@0.1.2
|
|
25
|
+
- @granite-js/image@0.1.2
|
|
26
|
+
- @granite-js/jest@0.1.2
|
|
27
|
+
- @granite-js/style-utils@0.1.2
|
|
28
|
+
- @granite-js/cli@0.1.2
|
|
29
|
+
- @granite-js/mpack@0.1.2
|
|
30
|
+
|
|
3
31
|
## 0.1.1
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import './types/global';
|
|
|
2
2
|
export { Granite } from './app';
|
|
3
3
|
export * from '@granite-js/style-utils';
|
|
4
4
|
export * from '@granite-js/image';
|
|
5
|
+
export * from '@granite-js/lottie';
|
|
5
6
|
export * from './dev-entrypoint';
|
|
6
7
|
export * from './native-modules/natives';
|
|
7
8
|
export * from './visibility';
|
|
@@ -13,5 +14,7 @@ export * from './scroll-view-inertial-background';
|
|
|
13
14
|
export * from './react';
|
|
14
15
|
export * from './router/createRoute';
|
|
15
16
|
export * from './event';
|
|
17
|
+
export * from './video';
|
|
18
|
+
export * from './status-bar';
|
|
16
19
|
export type { InitialProps, ColorPreference } from './initial-props';
|
|
17
20
|
export type { GraniteProps } from './app';
|
package/dist/keyboard/index.d.ts
CHANGED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { StatusBarProps } from './types';
|
|
2
|
+
export declare function StatusBar({ style, animated, hidden, backgroundColor, translucent }: StatusBarProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function useStatusBar({ style, animated, hidden, backgroundColor, translucent }: StatusBarProps): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type StatusBarStyle = 'light' | 'dark' | 'auto' | 'inverted';
|
|
2
|
+
export type StatusBarProps = {
|
|
3
|
+
/**
|
|
4
|
+
* @default 'auto'
|
|
5
|
+
*/
|
|
6
|
+
style?: StatusBarStyle;
|
|
7
|
+
hidden?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* @platform android
|
|
10
|
+
*/
|
|
11
|
+
animated?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* @platform android
|
|
14
|
+
*/
|
|
15
|
+
backgroundColor?: string;
|
|
16
|
+
/**
|
|
17
|
+
* @platform android
|
|
18
|
+
*/
|
|
19
|
+
translucent?: boolean;
|
|
20
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { default as VideoRef } from '@granite-js/native/react-native-video';
|
|
2
|
+
import { ComponentProps } from 'react';
|
|
3
|
+
import { Animated } from 'react-native';
|
|
4
|
+
import * as instance from './instance';
|
|
5
|
+
declare const AnimatedRNVideo: Animated.AnimatedComponent<import("react").ComponentType<instance.VideoNativeProps>>;
|
|
6
|
+
type VideoProps = ComponentProps<typeof AnimatedRNVideo>;
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
* @name Video
|
|
10
|
+
* @category UI
|
|
11
|
+
* @description
|
|
12
|
+
* The Video component implements audio focus control logic to prevent the sandbox app from stopping music playing in other apps. It automatically plays or pauses based on the app's state. For example, when the app transitions to the background, the video automatically pauses.
|
|
13
|
+
*
|
|
14
|
+
* ::: warning
|
|
15
|
+
* The Video component uses [react-native-video version (6.0.0-alpha.6)](https://github.com/TheWidlarzGroup/react-native-video/tree/v6.0.0-alpha.6). Therefore, some types or features may not be compatible with the latest version.
|
|
16
|
+
* :::
|
|
17
|
+
*
|
|
18
|
+
* @property {boolean} [isAvailable] Value to check if the `Video` component can be used. You can check this value to determine if the user can render the video or if video functionality is unavailable due to environmental constraints (e.g., network connection issues, unsupported devices). If this value is `false`, you should handle it by not rendering the video or providing alternative content.
|
|
19
|
+
*
|
|
20
|
+
* @param {VideoProperties} [props] Properties provided by [`react-native-video`](https://github.com/TheWidlarzGroup/react-native-video/tree/v6.0.0-alpha.6).
|
|
21
|
+
* @param {string} [props.source.uri] Source of the video to play. Can be set to a file path or URL.
|
|
22
|
+
* @param {boolean} [props.muted=false] Controls the mute state of the video. If `true`, the video's audio is muted; if `false`, the audio plays. Default is `false`.
|
|
23
|
+
* @param {boolean} [props.paused=false] Property to control video playback. If `true`, the video is paused; if `false`, the video plays. Default is `false`, and it autoplays.
|
|
24
|
+
* @param {OnAudioFocusChanged} [props.onAudioFocusChanged] Callback function called when audio focus changes. Must be implemented when `muted` is `false`. For more details, see [OnAudioFocusChanged](/reference/react-native/Types/OnAudioFocusChanged.html).
|
|
25
|
+
* @param {Ref<VideoRef>} ref Reference object to access the video instance. Through this ref, you can access various methods of the video instance.
|
|
26
|
+
*
|
|
27
|
+
* @returns {JSX.Element} Returns a JSX element that renders the video. Uses `Animated` to provide smooth animation effects during video playback.
|
|
28
|
+
*
|
|
29
|
+
* @see [react-native-video] https://github.com/react-native-video/react-native-video
|
|
30
|
+
* For detailed properties of the video component, please refer to the official documentation.
|
|
31
|
+
* @see [react-native-video-6.0.0] https://github.com/TheWidlarzGroup/react-native-video/releases/tag/v6.0.0
|
|
32
|
+
* This is the source code of the version currently installed in the sandbox app.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
*
|
|
36
|
+
* ### Video Autoplay Example
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* import { useRef } from 'react';
|
|
40
|
+
* import { View } from 'react-native';
|
|
41
|
+
* import { Video } from '@granite-js/react-native';
|
|
42
|
+
*
|
|
43
|
+
* export function VideoExample() {
|
|
44
|
+
* const videoRef = useRef(null);
|
|
45
|
+
*
|
|
46
|
+
* return (
|
|
47
|
+
* <View>
|
|
48
|
+
* <Video
|
|
49
|
+
* ref={videoRef}
|
|
50
|
+
* source={{ uri: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' }}
|
|
51
|
+
* muted={true}
|
|
52
|
+
* paused={false}
|
|
53
|
+
* resizeMode="cover"
|
|
54
|
+
* style={{ width: 300, height: 200, borderWidth: 1 }}
|
|
55
|
+
* />
|
|
56
|
+
* </View>
|
|
57
|
+
* );
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare const Video: import("react").ForwardRefExoticComponent<Omit<import("react-native-video").VideoProperties, "onAudioFocusChanged"> & {
|
|
62
|
+
onAudioFocusChanged?: (event: {
|
|
63
|
+
hasAudioFocus: boolean;
|
|
64
|
+
}) => void;
|
|
65
|
+
} & import("react").RefAttributes<VideoRef>>;
|
|
66
|
+
export { Video };
|
|
67
|
+
export type { VideoRef, VideoProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Video';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VideoProperties } from '@granite-js/native/react-native-video';
|
|
2
|
+
import { type ComponentType } from 'react';
|
|
3
|
+
export type VideoNativeProps = Omit<VideoProperties, 'onAudioFocusChanged'> & {
|
|
4
|
+
onAudioFocusChanged?: (event: {
|
|
5
|
+
hasAudioFocus: boolean;
|
|
6
|
+
}) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare const Component: ComponentType<VideoNativeProps>;
|
|
9
|
+
export declare const isAvailable: boolean;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@granite-js/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "The Granite Framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"granite": "./bin/cli.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"typecheck": "tsc --noEmit",
|
|
10
|
-
"test": "vitest --no-watch",
|
|
10
|
+
"test": "vitest --no-watch --coverage",
|
|
11
11
|
"lint": "eslint .",
|
|
12
12
|
"build": "tsup && tsc -p tsconfig.build.json",
|
|
13
13
|
"prepack": "yarn build"
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@babel/core": "^7.24.9",
|
|
57
57
|
"@babel/preset-env": "^7.24.8",
|
|
58
58
|
"@babel/preset-typescript": "^7.24.7",
|
|
59
|
-
"@granite-js/native": "0.1.
|
|
59
|
+
"@granite-js/native": "0.1.3",
|
|
60
60
|
"@testing-library/dom": "^10.4.0",
|
|
61
61
|
"@testing-library/react": "^16.1.0",
|
|
62
62
|
"@types/babel__core": "^7",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"@types/node": "^22.10.2",
|
|
65
65
|
"@types/react": "18.3.3",
|
|
66
66
|
"@types/react-dom": "^18",
|
|
67
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
67
68
|
"esbuild": "^0.25.4",
|
|
68
69
|
"eslint": "^9.7.0",
|
|
69
70
|
"jsdom": "^25.0.1",
|
|
@@ -81,11 +82,12 @@
|
|
|
81
82
|
"react-native": "*"
|
|
82
83
|
},
|
|
83
84
|
"dependencies": {
|
|
84
|
-
"@granite-js/cli": "0.1.
|
|
85
|
-
"@granite-js/image": "0.1.
|
|
86
|
-
"@granite-js/jest": "0.1.
|
|
87
|
-
"@granite-js/
|
|
88
|
-
"@granite-js/
|
|
85
|
+
"@granite-js/cli": "0.1.3",
|
|
86
|
+
"@granite-js/image": "0.1.3",
|
|
87
|
+
"@granite-js/jest": "0.1.3",
|
|
88
|
+
"@granite-js/lottie": "0.1.3",
|
|
89
|
+
"@granite-js/mpack": "0.1.3",
|
|
90
|
+
"@granite-js/style-utils": "0.1.3",
|
|
89
91
|
"es-toolkit": "^1.34.1",
|
|
90
92
|
"react-native-url-polyfill": "1.3.0"
|
|
91
93
|
}
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ import './types/global';
|
|
|
3
3
|
export { Granite } from './app';
|
|
4
4
|
export * from '@granite-js/style-utils';
|
|
5
5
|
export * from '@granite-js/image';
|
|
6
|
+
export * from '@granite-js/lottie';
|
|
7
|
+
|
|
6
8
|
export * from './dev-entrypoint';
|
|
7
9
|
export * from './native-modules/natives';
|
|
8
10
|
export * from './visibility';
|
|
@@ -14,6 +16,8 @@ export * from './scroll-view-inertial-background';
|
|
|
14
16
|
export * from './react';
|
|
15
17
|
export * from './router/createRoute';
|
|
16
18
|
export * from './event';
|
|
19
|
+
export * from './video';
|
|
20
|
+
export * from './status-bar';
|
|
17
21
|
|
|
18
22
|
export type { InitialProps, ColorPreference } from './initial-props';
|
|
19
23
|
export type { GraniteProps } from './app';
|
package/src/keyboard/index.ts
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react';
|
|
2
|
+
import { StatusBar as BaseStatusBar, useColorScheme } from 'react-native';
|
|
3
|
+
import type { StatusBarProps } from './types';
|
|
4
|
+
import { toStatusBarContentStyle } from './utils';
|
|
5
|
+
|
|
6
|
+
export function StatusBar({ style, animated, hidden, backgroundColor, translucent = true }: StatusBarProps) {
|
|
7
|
+
const colorScheme = useColorScheme() ?? 'light';
|
|
8
|
+
const translucentWithNoBackgroundColor = translucent && backgroundColor == null;
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<BaseStatusBar
|
|
12
|
+
translucent={translucent}
|
|
13
|
+
barStyle={toStatusBarContentStyle(style, colorScheme)}
|
|
14
|
+
backgroundColor={translucentWithNoBackgroundColor ? 'transparent' : backgroundColor}
|
|
15
|
+
animated={animated}
|
|
16
|
+
hidden={hidden}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useStatusBar({ style, animated, hidden, backgroundColor, translucent = true }: StatusBarProps) {
|
|
22
|
+
const colorScheme = useColorScheme() ?? 'light';
|
|
23
|
+
const translucentWithNoBackgroundColor = translucent && backgroundColor == null;
|
|
24
|
+
|
|
25
|
+
useLayoutEffect(() => {
|
|
26
|
+
BaseStatusBar.setTranslucent(translucent);
|
|
27
|
+
BaseStatusBar.setBarStyle(toStatusBarContentStyle(style, colorScheme));
|
|
28
|
+
BaseStatusBar.setBackgroundColor(
|
|
29
|
+
translucentWithNoBackgroundColor ? 'transparent' : (backgroundColor ?? 'transparent')
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (hidden !== undefined) {
|
|
33
|
+
BaseStatusBar.setHidden(hidden);
|
|
34
|
+
}
|
|
35
|
+
}, [colorScheme, translucentWithNoBackgroundColor, style, animated, hidden, backgroundColor, translucent]);
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react';
|
|
2
|
+
import { useColorScheme } from 'react-native';
|
|
3
|
+
import type { StatusBarProps } from './types';
|
|
4
|
+
import { toStatusBarContentStyle } from './utils';
|
|
5
|
+
import { useNavigation } from '../router/createRoute';
|
|
6
|
+
|
|
7
|
+
export function StatusBar(props: StatusBarProps) {
|
|
8
|
+
useStatusBar(props);
|
|
9
|
+
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useStatusBar({ style, hidden }: StatusBarProps) {
|
|
14
|
+
const navigation = useNavigation();
|
|
15
|
+
const colorScheme = useColorScheme() ?? 'light';
|
|
16
|
+
|
|
17
|
+
useLayoutEffect(() => {
|
|
18
|
+
let statusBarStyle: 'light' | 'dark';
|
|
19
|
+
|
|
20
|
+
switch (toStatusBarContentStyle(style, colorScheme)) {
|
|
21
|
+
case 'light-content':
|
|
22
|
+
statusBarStyle = 'light';
|
|
23
|
+
break;
|
|
24
|
+
case 'dark-content':
|
|
25
|
+
statusBarStyle = 'dark';
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
navigation.setOptions({
|
|
30
|
+
statusBarStyle,
|
|
31
|
+
statusBarHidden: hidden,
|
|
32
|
+
});
|
|
33
|
+
}, [navigation, colorScheme, style, hidden]);
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type StatusBarStyle = 'light' | 'dark' | 'auto' | 'inverted';
|
|
2
|
+
|
|
3
|
+
export type StatusBarProps = {
|
|
4
|
+
/**
|
|
5
|
+
* @default 'auto'
|
|
6
|
+
*/
|
|
7
|
+
style?: StatusBarStyle;
|
|
8
|
+
hidden?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* @platform android
|
|
11
|
+
*/
|
|
12
|
+
animated?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* @platform android
|
|
15
|
+
*/
|
|
16
|
+
backgroundColor?: string;
|
|
17
|
+
/**
|
|
18
|
+
* @platform android
|
|
19
|
+
*/
|
|
20
|
+
translucent?: boolean;
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StatusBarStyle } from './types';
|
|
2
|
+
import type { ColorPreference } from '../initial-props';
|
|
3
|
+
|
|
4
|
+
export function toStatusBarContentStyle(
|
|
5
|
+
statusBarStyle: StatusBarStyle = 'auto',
|
|
6
|
+
colorPreference: ColorPreference = 'light'
|
|
7
|
+
): 'light-content' | 'dark-content' {
|
|
8
|
+
const resolvedStyle = (() => {
|
|
9
|
+
switch (statusBarStyle) {
|
|
10
|
+
case 'auto':
|
|
11
|
+
return colorPreference === 'light' ? 'dark' : 'light';
|
|
12
|
+
case 'inverted':
|
|
13
|
+
return colorPreference === 'light' ? 'light' : 'dark';
|
|
14
|
+
default:
|
|
15
|
+
return statusBarStyle;
|
|
16
|
+
}
|
|
17
|
+
})();
|
|
18
|
+
|
|
19
|
+
return resolvedStyle === 'light' ? 'light-content' : 'dark-content';
|
|
20
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { default as VideoRef } from '@granite-js/native/react-native-video';
|
|
2
|
+
import { ComponentProps, forwardRef, useMemo, useState } from 'react';
|
|
3
|
+
import { Animated, Platform } from 'react-native';
|
|
4
|
+
import * as instance from './instance';
|
|
5
|
+
import { useVisibility } from '../visibility';
|
|
6
|
+
|
|
7
|
+
const AnimatedRNVideo = Animated.createAnimatedComponent(instance.Component);
|
|
8
|
+
|
|
9
|
+
type VideoProps = ComponentProps<typeof AnimatedRNVideo>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
* @name Video
|
|
14
|
+
* @category UI
|
|
15
|
+
* @description
|
|
16
|
+
* The Video component implements audio focus control logic to prevent the sandbox app from stopping music playing in other apps. It automatically plays or pauses based on the app's state. For example, when the app transitions to the background, the video automatically pauses.
|
|
17
|
+
*
|
|
18
|
+
* ::: warning
|
|
19
|
+
* The Video component uses [react-native-video version (6.0.0-alpha.6)](https://github.com/TheWidlarzGroup/react-native-video/tree/v6.0.0-alpha.6). Therefore, some types or features may not be compatible with the latest version.
|
|
20
|
+
* :::
|
|
21
|
+
*
|
|
22
|
+
* @property {boolean} [isAvailable] Value to check if the `Video` component can be used. You can check this value to determine if the user can render the video or if video functionality is unavailable due to environmental constraints (e.g., network connection issues, unsupported devices). If this value is `false`, you should handle it by not rendering the video or providing alternative content.
|
|
23
|
+
*
|
|
24
|
+
* @param {VideoProperties} [props] Properties provided by [`react-native-video`](https://github.com/TheWidlarzGroup/react-native-video/tree/v6.0.0-alpha.6).
|
|
25
|
+
* @param {string} [props.source.uri] Source of the video to play. Can be set to a file path or URL.
|
|
26
|
+
* @param {boolean} [props.muted=false] Controls the mute state of the video. If `true`, the video's audio is muted; if `false`, the audio plays. Default is `false`.
|
|
27
|
+
* @param {boolean} [props.paused=false] Property to control video playback. If `true`, the video is paused; if `false`, the video plays. Default is `false`, and it autoplays.
|
|
28
|
+
* @param {OnAudioFocusChanged} [props.onAudioFocusChanged] Callback function called when audio focus changes. Must be implemented when `muted` is `false`. For more details, see [OnAudioFocusChanged](/reference/react-native/Types/OnAudioFocusChanged.html).
|
|
29
|
+
* @param {Ref<VideoRef>} ref Reference object to access the video instance. Through this ref, you can access various methods of the video instance.
|
|
30
|
+
*
|
|
31
|
+
* @returns {JSX.Element} Returns a JSX element that renders the video. Uses `Animated` to provide smooth animation effects during video playback.
|
|
32
|
+
*
|
|
33
|
+
* @see [react-native-video] https://github.com/react-native-video/react-native-video
|
|
34
|
+
* For detailed properties of the video component, please refer to the official documentation.
|
|
35
|
+
* @see [react-native-video-6.0.0] https://github.com/TheWidlarzGroup/react-native-video/releases/tag/v6.0.0
|
|
36
|
+
* This is the source code of the version currently installed in the sandbox app.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
*
|
|
40
|
+
* ### Video Autoplay Example
|
|
41
|
+
*
|
|
42
|
+
* ```tsx
|
|
43
|
+
* import { useRef } from 'react';
|
|
44
|
+
* import { View } from 'react-native';
|
|
45
|
+
* import { Video } from '@granite-js/react-native';
|
|
46
|
+
*
|
|
47
|
+
* export function VideoExample() {
|
|
48
|
+
* const videoRef = useRef(null);
|
|
49
|
+
*
|
|
50
|
+
* return (
|
|
51
|
+
* <View>
|
|
52
|
+
* <Video
|
|
53
|
+
* ref={videoRef}
|
|
54
|
+
* source={{ uri: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' }}
|
|
55
|
+
* muted={true}
|
|
56
|
+
* paused={false}
|
|
57
|
+
* resizeMode="cover"
|
|
58
|
+
* style={{ width: 300, height: 200, borderWidth: 1 }}
|
|
59
|
+
* />
|
|
60
|
+
* </View>
|
|
61
|
+
* );
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
const Video = forwardRef<VideoRef, instance.VideoNativeProps>((props, ref) => {
|
|
66
|
+
const [isFocused, setIsFocused] = useState(props.muted || props.paused);
|
|
67
|
+
const visible = useVisibility();
|
|
68
|
+
|
|
69
|
+
// If focus state is not managed directly by the service through onAudioFocusChanged, control the paused value based on internal state.
|
|
70
|
+
const paused = useMemo(
|
|
71
|
+
() => !visible || props.paused || (!props.onAudioFocusChanged && !isFocused),
|
|
72
|
+
[props.onAudioFocusChanged, props.paused, visible, isFocused]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const disableFocus = props.muted || props.paused;
|
|
76
|
+
const mixWithOthers = props.muted ? 'mix' : undefined;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<AnimatedRNVideo
|
|
80
|
+
ref={ref as any}
|
|
81
|
+
progressUpdateInterval={16}
|
|
82
|
+
disableFocus={Platform.OS === 'ios' ? false : disableFocus}
|
|
83
|
+
playWhenInactive
|
|
84
|
+
onAudioFocusChanged={(event: any) => {
|
|
85
|
+
setIsFocused(event.hasAudioFocus);
|
|
86
|
+
props.onAudioFocusChanged?.(event);
|
|
87
|
+
}}
|
|
88
|
+
{...props}
|
|
89
|
+
// Internal state is used to control the component's states.
|
|
90
|
+
paused={paused}
|
|
91
|
+
mixWithOthers={mixWithOthers}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
Object.defineProperty(Video, 'isAvailable', {
|
|
97
|
+
value: instance.isAvailable,
|
|
98
|
+
enumerable: true,
|
|
99
|
+
writable: false,
|
|
100
|
+
configurable: false,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
export { Video };
|
|
104
|
+
export type { VideoRef, VideoProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Video';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { VideoProperties } from '@granite-js/native/react-native-video';
|
|
2
|
+
import { Component as ReactComponent, forwardRef, type ComponentType, type ForwardedRef } from 'react';
|
|
3
|
+
import { View } from 'react-native';
|
|
4
|
+
|
|
5
|
+
export type VideoNativeProps = Omit<VideoProperties, 'onAudioFocusChanged'> & {
|
|
6
|
+
onAudioFocusChanged?: (event: { hasAudioFocus: boolean }) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
class FallbackComponent extends ReactComponent<VideoNativeProps & { innerRef: ForwardedRef<View> }> {
|
|
10
|
+
render() {
|
|
11
|
+
return <View ref={this.props.innerRef} />;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ForwardedComponent = forwardRef<View, VideoNativeProps>((props: VideoNativeProps, ref) => (
|
|
16
|
+
<FallbackComponent {...props} innerRef={ref} />
|
|
17
|
+
));
|
|
18
|
+
|
|
19
|
+
function getVideoComponent(): ComponentType<VideoNativeProps> {
|
|
20
|
+
try {
|
|
21
|
+
return require('@granite-js/native/react-native-video')?.default;
|
|
22
|
+
} catch {
|
|
23
|
+
return ForwardedComponent;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Component = getVideoComponent();
|
|
28
|
+
export const isAvailable = Component !== ForwardedComponent;
|