@livekit/react-native 2.0.1 → 2.0.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/README.md +4 -0
- package/android/src/main/java/com/livekit/reactnative/LiveKitReactNative.kt +4 -4
- package/android/src/main/java/com/livekit/reactnative/video/CustomVideoDecoderFactory.kt +67 -0
- package/android/src/main/java/com/livekit/reactnative/video/CustomVideoEncoderFactory.kt +74 -0
- package/lib/commonjs/components/VideoView.js +12 -11
- package/lib/commonjs/components/VideoView.js.map +1 -1
- package/lib/commonjs/components/ViewPortDetector.js +134 -39
- package/lib/commonjs/components/ViewPortDetector.js.map +1 -1
- package/lib/module/components/VideoView.js +13 -12
- package/lib/module/components/VideoView.js.map +1 -1
- package/lib/module/components/ViewPortDetector.js +134 -40
- package/lib/module/components/ViewPortDetector.js.map +1 -1
- package/lib/typescript/components/ViewPortDetector.d.ts +11 -4
- package/package.json +1 -1
- package/src/components/VideoView.tsx +20 -13
- package/src/components/ViewPortDetector.tsx +112 -21
package/README.md
CHANGED
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
Use this SDK to add real-time video, audio and data features to your React Native app. By connecting to a self- or cloud-hosted <a href="https://livekit.io/">LiveKit</a> server, you can quickly build applications like interactive live streaming or video calls with just a few lines of code.
|
|
15
15
|
<!--END_DESCRIPTION-->
|
|
16
16
|
|
|
17
|
+
> [!NOTE]
|
|
18
|
+
> This is v2 of the React-Native SDK. When migrating from v1.x to v2.x you might encounter a small set of breaking changes.
|
|
19
|
+
> Read the [migration guide](https://docs.livekit.io/guides/migrate-from-v1/) for a detailed overview of what has changed.
|
|
20
|
+
|
|
17
21
|
## Installation
|
|
18
22
|
|
|
19
23
|
### NPM
|
|
@@ -4,8 +4,8 @@ import android.app.Application
|
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.os.Build
|
|
6
6
|
import com.livekit.reactnative.audio.AudioType
|
|
7
|
-
import com.livekit.reactnative.video.
|
|
8
|
-
import com.livekit.reactnative.video.
|
|
7
|
+
import com.livekit.reactnative.video.CustomVideoEncoderFactory
|
|
8
|
+
import com.livekit.reactnative.video.CustomVideoDecoderFactory
|
|
9
9
|
import com.oney.WebRTCModule.WebRTCModuleOptions
|
|
10
10
|
import org.webrtc.audio.JavaAudioDeviceModule
|
|
11
11
|
|
|
@@ -21,8 +21,8 @@ object LiveKitReactNative {
|
|
|
21
21
|
@JvmOverloads
|
|
22
22
|
fun setup(context: Context, audioType: AudioType = AudioType.CommunicationAudioType()) {
|
|
23
23
|
val options = WebRTCModuleOptions.getInstance()
|
|
24
|
-
options.videoEncoderFactory =
|
|
25
|
-
options.videoDecoderFactory =
|
|
24
|
+
options.videoEncoderFactory = CustomVideoEncoderFactory(null, true, true)
|
|
25
|
+
options.videoDecoderFactory = CustomVideoDecoderFactory()
|
|
26
26
|
|
|
27
27
|
val useHardwareAudioProcessing = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|
28
28
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023-2024 LiveKit, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
package com.livekit.reactnative.video
|
|
18
|
+
|
|
19
|
+
import com.oney.WebRTCModule.webrtcutils.SoftwareVideoDecoderFactoryProxy
|
|
20
|
+
import org.webrtc.EglBase
|
|
21
|
+
import org.webrtc.SoftwareVideoDecoderFactory
|
|
22
|
+
import org.webrtc.VideoCodecInfo
|
|
23
|
+
import org.webrtc.VideoDecoder
|
|
24
|
+
import org.webrtc.VideoDecoderFactory
|
|
25
|
+
import org.webrtc.WrappedVideoDecoderFactory
|
|
26
|
+
|
|
27
|
+
open class CustomVideoDecoderFactory(
|
|
28
|
+
private var forceSWCodec: Boolean = false,
|
|
29
|
+
private var forceSWCodecs: List<String> = listOf("VP9"),
|
|
30
|
+
) : VideoDecoderFactory {
|
|
31
|
+
private val softwareVideoDecoderFactory = SoftwareVideoDecoderFactoryProxy()
|
|
32
|
+
private val wrappedVideoDecoderFactory = WrappedVideoDecoderFactoryProxy()
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Set to true to force software codecs.
|
|
36
|
+
*/
|
|
37
|
+
fun setForceSWCodec(forceSWCodec: Boolean) {
|
|
38
|
+
this.forceSWCodec = forceSWCodec
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Set a list of codecs for which to use software codecs.
|
|
43
|
+
*/
|
|
44
|
+
fun setForceSWCodecList(forceSWCodecs: List<String>) {
|
|
45
|
+
this.forceSWCodecs = forceSWCodecs
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun createDecoder(videoCodecInfo: VideoCodecInfo): VideoDecoder? {
|
|
49
|
+
if (forceSWCodec) {
|
|
50
|
+
return softwareVideoDecoderFactory.createDecoder(videoCodecInfo)
|
|
51
|
+
}
|
|
52
|
+
if (forceSWCodecs.isNotEmpty()) {
|
|
53
|
+
if (forceSWCodecs.contains(videoCodecInfo.name)) {
|
|
54
|
+
return softwareVideoDecoderFactory.createDecoder(videoCodecInfo)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return wrappedVideoDecoderFactory.createDecoder(videoCodecInfo)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override fun getSupportedCodecs(): Array<VideoCodecInfo> {
|
|
61
|
+
return if (forceSWCodec && forceSWCodecs.isEmpty()) {
|
|
62
|
+
softwareVideoDecoderFactory.supportedCodecs
|
|
63
|
+
} else {
|
|
64
|
+
wrappedVideoDecoderFactory.supportedCodecs
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023-2024 LiveKit, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
package com.livekit.reactnative.video
|
|
18
|
+
|
|
19
|
+
import com.oney.WebRTCModule.webrtcutils.SoftwareVideoEncoderFactoryProxy
|
|
20
|
+
import org.webrtc.EglBase
|
|
21
|
+
import org.webrtc.SoftwareVideoEncoderFactory
|
|
22
|
+
import org.webrtc.VideoCodecInfo
|
|
23
|
+
import org.webrtc.VideoEncoder
|
|
24
|
+
import org.webrtc.VideoEncoderFactory
|
|
25
|
+
|
|
26
|
+
open class CustomVideoEncoderFactory(
|
|
27
|
+
sharedContext: EglBase.Context?,
|
|
28
|
+
enableIntelVp8Encoder: Boolean,
|
|
29
|
+
enableH264HighProfile: Boolean,
|
|
30
|
+
private var forceSWCodec: Boolean = false,
|
|
31
|
+
private var forceSWCodecs: List<String> = listOf("VP9"),
|
|
32
|
+
) : VideoEncoderFactory {
|
|
33
|
+
private val softwareVideoEncoderFactory = SoftwareVideoEncoderFactoryProxy()
|
|
34
|
+
private val simulcastVideoEncoderFactoryWrapper: SimulcastVideoEncoderFactoryWrapper
|
|
35
|
+
|
|
36
|
+
init {
|
|
37
|
+
simulcastVideoEncoderFactoryWrapper =
|
|
38
|
+
SimulcastVideoEncoderFactoryWrapper(sharedContext, enableIntelVp8Encoder, enableH264HighProfile)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Set to true to force software codecs.
|
|
43
|
+
*/
|
|
44
|
+
fun setForceSWCodec(forceSWCodec: Boolean) {
|
|
45
|
+
this.forceSWCodec = forceSWCodec
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set a list of codecs for which to use software codecs.
|
|
50
|
+
*/
|
|
51
|
+
fun setForceSWCodecList(forceSWCodecs: List<String>) {
|
|
52
|
+
this.forceSWCodecs = forceSWCodecs
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
override fun createEncoder(videoCodecInfo: VideoCodecInfo): VideoEncoder? {
|
|
56
|
+
if (forceSWCodec) {
|
|
57
|
+
return softwareVideoEncoderFactory.createEncoder(videoCodecInfo)
|
|
58
|
+
}
|
|
59
|
+
if (forceSWCodecs.isNotEmpty()) {
|
|
60
|
+
if (forceSWCodecs.contains(videoCodecInfo.name)) {
|
|
61
|
+
return softwareVideoEncoderFactory.createEncoder(videoCodecInfo)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return simulcastVideoEncoderFactoryWrapper.createEncoder(videoCodecInfo)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override fun getSupportedCodecs(): Array<VideoCodecInfo> {
|
|
68
|
+
return if (forceSWCodec && forceSWCodecs.isEmpty()) {
|
|
69
|
+
softwareVideoEncoderFactory.supportedCodecs
|
|
70
|
+
} else {
|
|
71
|
+
simulcastVideoEncoderFactoryWrapper.supportedCodecs
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -39,6 +39,11 @@ const VideoView = _ref => {
|
|
|
39
39
|
info.something = videoTrack;
|
|
40
40
|
return info;
|
|
41
41
|
});
|
|
42
|
+
const layoutOnChange = (0, React.useCallback)(event => elementInfo.onLayout(event), [elementInfo]);
|
|
43
|
+
const visibilityOnChange = (0, React.useCallback)(isVisible => elementInfo.onVisibility(isVisible), [elementInfo]);
|
|
44
|
+
const shouldObserveVisibility = (0, React.useMemo)(() => {
|
|
45
|
+
return videoTrack instanceof _livekitClient.RemoteVideoTrack && videoTrack.isAdaptiveStream;
|
|
46
|
+
}, [videoTrack]);
|
|
42
47
|
const [mediaStream, setMediaStream] = (0, React.useState)(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
|
|
43
48
|
(0, React.useEffect)(() => {
|
|
44
49
|
setMediaStream(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
|
|
@@ -70,18 +75,14 @@ const VideoView = _ref => {
|
|
|
70
75
|
style: { ...style,
|
|
71
76
|
...styles.container
|
|
72
77
|
},
|
|
73
|
-
onLayout:
|
|
74
|
-
elementInfo.onLayout(event);
|
|
75
|
-
}
|
|
78
|
+
onLayout: layoutOnChange
|
|
76
79
|
}, /*#__PURE__*/React.createElement(_ViewPortDetector.default, {
|
|
77
|
-
onChange:
|
|
78
|
-
style: styles.videoView
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
width: '100%'
|
|
84
|
-
},
|
|
80
|
+
onChange: visibilityOnChange,
|
|
81
|
+
style: styles.videoView,
|
|
82
|
+
disabled: !shouldObserveVisibility,
|
|
83
|
+
propKey: videoTrack
|
|
84
|
+
}, /*#__PURE__*/React.createElement(_reactNativeWebrtc.RTCView, {
|
|
85
|
+
style: styles.videoView,
|
|
85
86
|
streamURL: (_mediaStream$toURL = mediaStream === null || mediaStream === void 0 ? void 0 : mediaStream.toURL()) !== null && _mediaStream$toURL !== void 0 ? _mediaStream$toURL : '',
|
|
86
87
|
objectFit: objectFit,
|
|
87
88
|
zOrder: zOrder,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["VideoView.tsx"],"names":["VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","
|
|
1
|
+
{"version":3,"sources":["VideoView.tsx"],"names":["VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","layoutOnChange","event","onLayout","visibilityOnChange","isVisible","onVisibility","shouldObserveVisibility","RemoteVideoTrack","isAdaptiveStream","mediaStream","setMediaStream","LocalVideoTrack","onRestarted","track","on","TrackEvent","Restarted","off","observeElementInfo","stopObservingElementInfo","styles","container","videoView","toURL","StyleSheet","create","flex","width","_width","_height","observe","_observing","stopObserving","height","nativeEvent","layout","handleResize","visible","visibilityChangedAt","Date","now","handleVisibilityChanged"],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native';\nimport {\n ElementInfo,\n LocalVideoTrack,\n Track,\n TrackEvent,\n VideoTrack,\n} from 'livekit-client';\nimport { RTCView } from '@livekit/react-native-webrtc';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { RemoteVideoTrack } from 'livekit-client';\nimport ViewPortDetector from './ViewPortDetector';\n\nexport type Props = {\n videoTrack?: VideoTrack | undefined;\n style?: ViewStyle;\n objectFit?: 'cover' | 'contain' | undefined;\n mirror?: boolean;\n zOrder?: number;\n};\n\nexport const VideoView = ({\n style = {},\n videoTrack,\n objectFit = 'cover',\n zOrder,\n mirror,\n}: Props) => {\n const [elementInfo] = useState(() => {\n let info = new VideoViewElementInfo();\n info.id = videoTrack?.sid;\n info.something = videoTrack;\n return info;\n });\n\n const layoutOnChange = useCallback(\n (event: LayoutChangeEvent) => elementInfo.onLayout(event),\n [elementInfo]\n );\n const visibilityOnChange = useCallback(\n (isVisible: boolean) => elementInfo.onVisibility(isVisible),\n [elementInfo]\n );\n const shouldObserveVisibility = useMemo(() => {\n return (\n videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream\n );\n }, [videoTrack]);\n\n const [mediaStream, setMediaStream] = useState(videoTrack?.mediaStream);\n useEffect(() => {\n setMediaStream(videoTrack?.mediaStream);\n if (videoTrack instanceof LocalVideoTrack) {\n const onRestarted = (track: Track | null) => {\n setMediaStream(track?.mediaStream);\n };\n videoTrack.on(TrackEvent.Restarted, onRestarted);\n\n return () => {\n videoTrack.off(TrackEvent.Restarted, onRestarted);\n };\n } else {\n return () => {};\n }\n }, [videoTrack]);\n\n useEffect(() => {\n if (videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream) {\n videoTrack?.observeElementInfo(elementInfo);\n return () => {\n videoTrack?.stopObservingElementInfo(elementInfo);\n };\n } else {\n return () => {};\n }\n }, [videoTrack, elementInfo]);\n\n return (\n <View style={{ ...style, ...styles.container }} onLayout={layoutOnChange}>\n <ViewPortDetector\n onChange={visibilityOnChange}\n style={styles.videoView}\n disabled={!shouldObserveVisibility}\n propKey={videoTrack}\n >\n <RTCView\n style={styles.videoView}\n streamURL={mediaStream?.toURL() ?? ''}\n objectFit={objectFit}\n zOrder={zOrder}\n mirror={mirror}\n />\n </ViewPortDetector>\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {},\n videoView: {\n flex: 1,\n width: '100%',\n },\n});\n\nclass VideoViewElementInfo implements ElementInfo {\n element: object = {};\n something?: any;\n id?: string;\n _width = 0;\n _height = 0;\n _observing = false;\n visible: boolean = true;\n visibilityChangedAt: number | undefined;\n pictureInPicture = false;\n handleResize?: (() => void) | undefined;\n handleVisibilityChanged?: (() => void) | undefined;\n width = () => this._width;\n height = () => this._height;\n\n observe(): void {\n this._observing = true;\n }\n stopObserving(): void {\n this._observing = false;\n }\n\n onLayout(event: LayoutChangeEvent) {\n let { width, height } = event.nativeEvent.layout;\n this._width = width;\n this._height = height;\n\n if (this._observing) {\n this.handleResize?.();\n }\n }\n onVisibility(isVisible: boolean) {\n if (this.visible !== isVisible) {\n this.visible = isVisible;\n this.visibilityChangedAt = Date.now();\n if (this._observing) {\n this.handleVisibilityChanged?.();\n }\n }\n }\n}\n"]}
|
|
@@ -17,11 +17,56 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|
|
17
17
|
|
|
18
18
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
19
19
|
|
|
20
|
+
const DEFAULT_DELAY = 1000;
|
|
21
|
+
|
|
22
|
+
class TimeoutHandler {
|
|
23
|
+
constructor() {
|
|
24
|
+
_defineProperty(this, "handlerRef", {
|
|
25
|
+
id: -1
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get handler() {
|
|
30
|
+
return this.handlerRef.id;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
set handler(n) {
|
|
34
|
+
this.handlerRef.id = n;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
clear() {
|
|
38
|
+
clearTimeout(this.handlerRef.id);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function setIntervalWithTimeout(callback, intervalMs) {
|
|
44
|
+
let handleWrapper = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new TimeoutHandler();
|
|
45
|
+
let cleared = false;
|
|
46
|
+
|
|
47
|
+
const timeout = () => {
|
|
48
|
+
handleWrapper.handler = setTimeout(() => {
|
|
49
|
+
callback(() => {
|
|
50
|
+
cleared = true;
|
|
51
|
+
handleWrapper.clear();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!cleared) {
|
|
55
|
+
timeout();
|
|
56
|
+
}
|
|
57
|
+
}, intervalMs);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
timeout();
|
|
61
|
+
return handleWrapper;
|
|
62
|
+
}
|
|
20
63
|
/**
|
|
21
64
|
* Detects when this is in the viewport and visible.
|
|
22
65
|
*
|
|
23
66
|
* Will not fire visibility changes for zero width/height components.
|
|
24
67
|
*/
|
|
68
|
+
|
|
69
|
+
|
|
25
70
|
class ViewPortDetector extends _react.Component {
|
|
26
71
|
constructor(props) {
|
|
27
72
|
super(props);
|
|
@@ -32,6 +77,83 @@ class ViewPortDetector extends _react.Component {
|
|
|
32
77
|
|
|
33
78
|
_defineProperty(this, "view", null);
|
|
34
79
|
|
|
80
|
+
_defineProperty(this, "lastAppStateActive", false);
|
|
81
|
+
|
|
82
|
+
_defineProperty(this, "appStateSubscription", null);
|
|
83
|
+
|
|
84
|
+
_defineProperty(this, "hasValidTimeout", (disabled, delay) => {
|
|
85
|
+
let disabledValue = disabled !== null && disabled !== void 0 ? disabled : false;
|
|
86
|
+
let delayValue = delay !== null && delay !== void 0 ? delay : DEFAULT_DELAY;
|
|
87
|
+
return _reactNative.AppState.currentState === 'active' && !disabledValue && delayValue > 0;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
_defineProperty(this, "handleAppStateChange", nextAppState => {
|
|
91
|
+
let nextAppStateActive = nextAppState === 'active';
|
|
92
|
+
|
|
93
|
+
if (this.lastAppStateActive !== nextAppStateActive) {
|
|
94
|
+
this.checkVisibility();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.lastAppStateActive = nextAppStateActive;
|
|
98
|
+
|
|
99
|
+
if (!this.hasValidTimeout(this.props.disabled, this.props.delay)) {
|
|
100
|
+
this.stopWatching();
|
|
101
|
+
} else {
|
|
102
|
+
this.startWatching();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
_defineProperty(this, "startWatching", () => {
|
|
107
|
+
if (this.interval) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.interval = setIntervalWithTimeout(this.checkVisibility, this.props.delay || DEFAULT_DELAY);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
_defineProperty(this, "stopWatching", () => {
|
|
115
|
+
var _this$interval;
|
|
116
|
+
|
|
117
|
+
(_this$interval = this.interval) === null || _this$interval === void 0 ? void 0 : _this$interval.clear();
|
|
118
|
+
this.interval = null;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
_defineProperty(this, "checkVisibility", () => {
|
|
122
|
+
if (!this.view) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (_reactNative.AppState.currentState !== 'active') {
|
|
127
|
+
this.updateVisibility(false);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.view.measure((_x, _y, width, height, _pageX, _pageY) => {
|
|
132
|
+
this.checkInViewPort(width, height);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
_defineProperty(this, "checkInViewPort", (width, height) => {
|
|
137
|
+
let isVisible; // Not visible if any of these are missing.
|
|
138
|
+
|
|
139
|
+
if (!width || !height) {
|
|
140
|
+
isVisible = false;
|
|
141
|
+
} else {
|
|
142
|
+
isVisible = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.updateVisibility(isVisible);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
_defineProperty(this, "updateVisibility", isVisible => {
|
|
149
|
+
if (this.lastValue !== isVisible) {
|
|
150
|
+
var _this$props$onChange, _this$props;
|
|
151
|
+
|
|
152
|
+
this.lastValue = isVisible;
|
|
153
|
+
(_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, isVisible);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
35
157
|
this.state = {
|
|
36
158
|
rectTop: 0,
|
|
37
159
|
rectBottom: 0
|
|
@@ -39,58 +161,31 @@ class ViewPortDetector extends _react.Component {
|
|
|
39
161
|
}
|
|
40
162
|
|
|
41
163
|
componentDidMount() {
|
|
42
|
-
|
|
164
|
+
this.lastAppStateActive = _reactNative.AppState.currentState === 'active';
|
|
165
|
+
this.appStateSubscription = _reactNative.AppState.addEventListener('change', this.handleAppStateChange);
|
|
166
|
+
|
|
167
|
+
if (this.hasValidTimeout(this.props.disabled, this.props.delay)) {
|
|
43
168
|
this.startWatching();
|
|
44
169
|
}
|
|
45
170
|
}
|
|
46
171
|
|
|
47
172
|
componentWillUnmount() {
|
|
173
|
+
var _this$appStateSubscri;
|
|
174
|
+
|
|
175
|
+
(_this$appStateSubscri = this.appStateSubscription) === null || _this$appStateSubscri === void 0 ? void 0 : _this$appStateSubscri.remove();
|
|
176
|
+
this.appStateSubscription = null;
|
|
48
177
|
this.stopWatching();
|
|
49
178
|
}
|
|
50
179
|
|
|
51
180
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
52
|
-
if (nextProps.disabled) {
|
|
181
|
+
if (!this.hasValidTimeout(nextProps.disabled, nextProps.delay)) {
|
|
53
182
|
this.stopWatching();
|
|
54
183
|
} else {
|
|
55
|
-
this.
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
startWatching() {
|
|
61
|
-
if (this.interval) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
this.interval = setInterval(() => {
|
|
66
|
-
if (!this.view) {
|
|
67
|
-
return;
|
|
184
|
+
if (this.props.propKey !== nextProps.propKey) {
|
|
185
|
+
this.lastValue = null;
|
|
68
186
|
}
|
|
69
187
|
|
|
70
|
-
this.
|
|
71
|
-
this.checkInViewPort(width, height);
|
|
72
|
-
});
|
|
73
|
-
}, this.props.delay || 100);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
stopWatching() {
|
|
77
|
-
this.interval = clearInterval(this.interval);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
checkInViewPort(width, height) {
|
|
81
|
-
let isVisible; // Not visible if any of these are missing.
|
|
82
|
-
|
|
83
|
-
if (!width || !height) {
|
|
84
|
-
isVisible = false;
|
|
85
|
-
} else {
|
|
86
|
-
isVisible = true;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (this.lastValue !== isVisible) {
|
|
90
|
-
var _this$props$onChange, _this$props;
|
|
91
|
-
|
|
92
|
-
this.lastValue = isVisible;
|
|
93
|
-
(_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, isVisible);
|
|
188
|
+
this.startWatching();
|
|
94
189
|
}
|
|
95
190
|
}
|
|
96
191
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["ViewPortDetector.tsx"],"names":["ViewPortDetector","Component","constructor","props","
|
|
1
|
+
{"version":3,"sources":["ViewPortDetector.tsx"],"names":["DEFAULT_DELAY","TimeoutHandler","id","handler","handlerRef","n","clear","clearTimeout","setIntervalWithTimeout","callback","intervalMs","handleWrapper","cleared","timeout","setTimeout","ViewPortDetector","Component","constructor","props","disabled","delay","disabledValue","delayValue","AppState","currentState","nextAppState","nextAppStateActive","lastAppStateActive","checkVisibility","hasValidTimeout","stopWatching","startWatching","interval","view","updateVisibility","measure","_x","_y","width","height","_pageX","_pageY","checkInViewPort","isVisible","lastValue","onChange","state","rectTop","rectBottom","componentDidMount","appStateSubscription","addEventListener","handleAppStateChange","componentWillUnmount","remove","UNSAFE_componentWillReceiveProps","nextProps","propKey","render","component","children"],"mappings":"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","sourcesContent":["'use strict';\n\nimport React, { Component, PropsWithChildren } from 'react';\nimport {\n AppState,\n AppStateStatus,\n NativeEventSubscription,\n View,\n ViewStyle,\n} from 'react-native';\n\nconst DEFAULT_DELAY = 1000;\n\nexport type Props = {\n disabled?: boolean;\n style?: ViewStyle;\n onChange?: (isVisible: boolean) => void;\n delay?: number;\n propKey?: any;\n};\n\nclass TimeoutHandler {\n private handlerRef: { id: any } = { id: -1 };\n\n get handler(): any {\n return this.handlerRef.id;\n }\n set handler(n: any) {\n this.handlerRef.id = n;\n }\n\n clear() {\n clearTimeout(this.handlerRef.id as any);\n }\n}\n\nfunction setIntervalWithTimeout(\n callback: (clear: () => void) => any,\n intervalMs: number,\n handleWrapper = new TimeoutHandler()\n): TimeoutHandler {\n let cleared = false;\n\n const timeout = () => {\n handleWrapper.handler = setTimeout(() => {\n callback(() => {\n cleared = true;\n handleWrapper.clear();\n });\n if (!cleared) {\n timeout();\n }\n }, intervalMs);\n };\n timeout();\n return handleWrapper;\n}\n\n/**\n * Detects when this is in the viewport and visible.\n *\n * Will not fire visibility changes for zero width/height components.\n */\nexport default class ViewPortDetector extends Component<\n PropsWithChildren<Props>\n> {\n private lastValue: boolean | null = null;\n private interval: TimeoutHandler | null = null;\n private view: View | null = null;\n private lastAppStateActive = false;\n private appStateSubscription: NativeEventSubscription | null = null;\n\n constructor(props: Props) {\n super(props);\n this.state = { rectTop: 0, rectBottom: 0 };\n }\n\n componentDidMount() {\n this.lastAppStateActive = AppState.currentState === 'active';\n this.appStateSubscription = AppState.addEventListener(\n 'change',\n this.handleAppStateChange\n );\n if (this.hasValidTimeout(this.props.disabled, this.props.delay)) {\n this.startWatching();\n }\n }\n\n componentWillUnmount() {\n this.appStateSubscription?.remove();\n this.appStateSubscription = null;\n this.stopWatching();\n }\n\n hasValidTimeout = (disabled?: boolean, delay?: number): boolean => {\n let disabledValue = disabled ?? false;\n let delayValue = delay ?? DEFAULT_DELAY;\n return (\n AppState.currentState === 'active' && !disabledValue && delayValue > 0\n );\n };\n\n UNSAFE_componentWillReceiveProps(nextProps: Props) {\n if (!this.hasValidTimeout(nextProps.disabled, nextProps.delay)) {\n this.stopWatching();\n } else {\n if (this.props.propKey !== nextProps.propKey) {\n this.lastValue = null;\n }\n this.startWatching();\n }\n }\n handleAppStateChange = (nextAppState: AppStateStatus) => {\n let nextAppStateActive = nextAppState === 'active';\n if (this.lastAppStateActive !== nextAppStateActive) {\n this.checkVisibility();\n }\n this.lastAppStateActive = nextAppStateActive;\n\n if (!this.hasValidTimeout(this.props.disabled, this.props.delay)) {\n this.stopWatching();\n } else {\n this.startWatching();\n }\n };\n\n startWatching = () => {\n if (this.interval) {\n return;\n }\n this.interval = setIntervalWithTimeout(\n this.checkVisibility,\n this.props.delay || DEFAULT_DELAY\n );\n };\n\n stopWatching = () => {\n this.interval?.clear();\n this.interval = null;\n };\n\n checkVisibility = () => {\n if (!this.view) {\n return;\n }\n\n if (AppState.currentState !== 'active') {\n this.updateVisibility(false);\n return;\n }\n\n this.view.measure((_x, _y, width, height, _pageX, _pageY) => {\n this.checkInViewPort(width, height);\n });\n };\n checkInViewPort = (width?: number, height?: number) => {\n let isVisible: boolean;\n // Not visible if any of these are missing.\n if (!width || !height) {\n isVisible = false;\n } else {\n isVisible = true;\n }\n this.updateVisibility(isVisible);\n };\n\n updateVisibility = (isVisible: boolean) => {\n if (this.lastValue !== isVisible) {\n this.lastValue = isVisible;\n this.props.onChange?.(isVisible);\n }\n };\n render() {\n return (\n <View\n collapsable={false}\n ref={(component) => {\n this.view = component;\n }}\n {...this.props}\n >\n {this.props.children}\n </View>\n );\n }\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import * as React from 'react';
|
|
|
4
4
|
import { StyleSheet, View } from 'react-native';
|
|
5
5
|
import { LocalVideoTrack, TrackEvent } from 'livekit-client';
|
|
6
6
|
import { RTCView } from '@livekit/react-native-webrtc';
|
|
7
|
-
import { useEffect, useState } from 'react';
|
|
7
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
8
8
|
import { RemoteVideoTrack } from 'livekit-client';
|
|
9
9
|
import ViewPortDetector from './ViewPortDetector';
|
|
10
10
|
export const VideoView = _ref => {
|
|
@@ -23,6 +23,11 @@ export const VideoView = _ref => {
|
|
|
23
23
|
info.something = videoTrack;
|
|
24
24
|
return info;
|
|
25
25
|
});
|
|
26
|
+
const layoutOnChange = useCallback(event => elementInfo.onLayout(event), [elementInfo]);
|
|
27
|
+
const visibilityOnChange = useCallback(isVisible => elementInfo.onVisibility(isVisible), [elementInfo]);
|
|
28
|
+
const shouldObserveVisibility = useMemo(() => {
|
|
29
|
+
return videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream;
|
|
30
|
+
}, [videoTrack]);
|
|
26
31
|
const [mediaStream, setMediaStream] = useState(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
|
|
27
32
|
useEffect(() => {
|
|
28
33
|
setMediaStream(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
|
|
@@ -54,18 +59,14 @@ export const VideoView = _ref => {
|
|
|
54
59
|
style: { ...style,
|
|
55
60
|
...styles.container
|
|
56
61
|
},
|
|
57
|
-
onLayout:
|
|
58
|
-
elementInfo.onLayout(event);
|
|
59
|
-
}
|
|
62
|
+
onLayout: layoutOnChange
|
|
60
63
|
}, /*#__PURE__*/React.createElement(ViewPortDetector, {
|
|
61
|
-
onChange:
|
|
62
|
-
style: styles.videoView
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
width: '100%'
|
|
68
|
-
},
|
|
64
|
+
onChange: visibilityOnChange,
|
|
65
|
+
style: styles.videoView,
|
|
66
|
+
disabled: !shouldObserveVisibility,
|
|
67
|
+
propKey: videoTrack
|
|
68
|
+
}, /*#__PURE__*/React.createElement(RTCView, {
|
|
69
|
+
style: styles.videoView,
|
|
69
70
|
streamURL: (_mediaStream$toURL = mediaStream === null || mediaStream === void 0 ? void 0 : mediaStream.toURL()) !== null && _mediaStream$toURL !== void 0 ? _mediaStream$toURL : '',
|
|
70
71
|
objectFit: objectFit,
|
|
71
72
|
zOrder: zOrder,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["VideoView.tsx"],"names":["React","StyleSheet","View","LocalVideoTrack","TrackEvent","RTCView","useEffect","useState","RemoteVideoTrack","ViewPortDetector","VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","
|
|
1
|
+
{"version":3,"sources":["VideoView.tsx"],"names":["React","StyleSheet","View","LocalVideoTrack","TrackEvent","RTCView","useCallback","useEffect","useMemo","useState","RemoteVideoTrack","ViewPortDetector","VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","layoutOnChange","event","onLayout","visibilityOnChange","isVisible","onVisibility","shouldObserveVisibility","isAdaptiveStream","mediaStream","setMediaStream","onRestarted","track","on","Restarted","off","observeElementInfo","stopObservingElementInfo","styles","container","videoView","toURL","create","flex","width","_width","_height","observe","_observing","stopObserving","height","nativeEvent","layout","handleResize","visible","visibilityChangedAt","Date","now","handleVisibilityChanged"],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native';\nimport {\n ElementInfo,\n LocalVideoTrack,\n Track,\n TrackEvent,\n VideoTrack,\n} from 'livekit-client';\nimport { RTCView } from '@livekit/react-native-webrtc';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { RemoteVideoTrack } from 'livekit-client';\nimport ViewPortDetector from './ViewPortDetector';\n\nexport type Props = {\n videoTrack?: VideoTrack | undefined;\n style?: ViewStyle;\n objectFit?: 'cover' | 'contain' | undefined;\n mirror?: boolean;\n zOrder?: number;\n};\n\nexport const VideoView = ({\n style = {},\n videoTrack,\n objectFit = 'cover',\n zOrder,\n mirror,\n}: Props) => {\n const [elementInfo] = useState(() => {\n let info = new VideoViewElementInfo();\n info.id = videoTrack?.sid;\n info.something = videoTrack;\n return info;\n });\n\n const layoutOnChange = useCallback(\n (event: LayoutChangeEvent) => elementInfo.onLayout(event),\n [elementInfo]\n );\n const visibilityOnChange = useCallback(\n (isVisible: boolean) => elementInfo.onVisibility(isVisible),\n [elementInfo]\n );\n const shouldObserveVisibility = useMemo(() => {\n return (\n videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream\n );\n }, [videoTrack]);\n\n const [mediaStream, setMediaStream] = useState(videoTrack?.mediaStream);\n useEffect(() => {\n setMediaStream(videoTrack?.mediaStream);\n if (videoTrack instanceof LocalVideoTrack) {\n const onRestarted = (track: Track | null) => {\n setMediaStream(track?.mediaStream);\n };\n videoTrack.on(TrackEvent.Restarted, onRestarted);\n\n return () => {\n videoTrack.off(TrackEvent.Restarted, onRestarted);\n };\n } else {\n return () => {};\n }\n }, [videoTrack]);\n\n useEffect(() => {\n if (videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream) {\n videoTrack?.observeElementInfo(elementInfo);\n return () => {\n videoTrack?.stopObservingElementInfo(elementInfo);\n };\n } else {\n return () => {};\n }\n }, [videoTrack, elementInfo]);\n\n return (\n <View style={{ ...style, ...styles.container }} onLayout={layoutOnChange}>\n <ViewPortDetector\n onChange={visibilityOnChange}\n style={styles.videoView}\n disabled={!shouldObserveVisibility}\n propKey={videoTrack}\n >\n <RTCView\n style={styles.videoView}\n streamURL={mediaStream?.toURL() ?? ''}\n objectFit={objectFit}\n zOrder={zOrder}\n mirror={mirror}\n />\n </ViewPortDetector>\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {},\n videoView: {\n flex: 1,\n width: '100%',\n },\n});\n\nclass VideoViewElementInfo implements ElementInfo {\n element: object = {};\n something?: any;\n id?: string;\n _width = 0;\n _height = 0;\n _observing = false;\n visible: boolean = true;\n visibilityChangedAt: number | undefined;\n pictureInPicture = false;\n handleResize?: (() => void) | undefined;\n handleVisibilityChanged?: (() => void) | undefined;\n width = () => this._width;\n height = () => this._height;\n\n observe(): void {\n this._observing = true;\n }\n stopObserving(): void {\n this._observing = false;\n }\n\n onLayout(event: LayoutChangeEvent) {\n let { width, height } = event.nativeEvent.layout;\n this._width = width;\n this._height = height;\n\n if (this._observing) {\n this.handleResize?.();\n }\n }\n onVisibility(isVisible: boolean) {\n if (this.visible !== isVisible) {\n this.visible = isVisible;\n this.visibilityChangedAt = Date.now();\n if (this._observing) {\n this.handleVisibilityChanged?.();\n }\n }\n }\n}\n"]}
|
|
@@ -5,13 +5,57 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|
|
5
5
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
6
|
|
|
7
7
|
import React, { Component } from 'react';
|
|
8
|
-
import { View } from 'react-native';
|
|
8
|
+
import { AppState, View } from 'react-native';
|
|
9
|
+
const DEFAULT_DELAY = 1000;
|
|
10
|
+
|
|
11
|
+
class TimeoutHandler {
|
|
12
|
+
constructor() {
|
|
13
|
+
_defineProperty(this, "handlerRef", {
|
|
14
|
+
id: -1
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get handler() {
|
|
19
|
+
return this.handlerRef.id;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set handler(n) {
|
|
23
|
+
this.handlerRef.id = n;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
clear() {
|
|
27
|
+
clearTimeout(this.handlerRef.id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function setIntervalWithTimeout(callback, intervalMs) {
|
|
33
|
+
let handleWrapper = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new TimeoutHandler();
|
|
34
|
+
let cleared = false;
|
|
9
35
|
|
|
36
|
+
const timeout = () => {
|
|
37
|
+
handleWrapper.handler = setTimeout(() => {
|
|
38
|
+
callback(() => {
|
|
39
|
+
cleared = true;
|
|
40
|
+
handleWrapper.clear();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!cleared) {
|
|
44
|
+
timeout();
|
|
45
|
+
}
|
|
46
|
+
}, intervalMs);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
timeout();
|
|
50
|
+
return handleWrapper;
|
|
51
|
+
}
|
|
10
52
|
/**
|
|
11
53
|
* Detects when this is in the viewport and visible.
|
|
12
54
|
*
|
|
13
55
|
* Will not fire visibility changes for zero width/height components.
|
|
14
56
|
*/
|
|
57
|
+
|
|
58
|
+
|
|
15
59
|
export default class ViewPortDetector extends Component {
|
|
16
60
|
constructor(props) {
|
|
17
61
|
super(props);
|
|
@@ -22,6 +66,83 @@ export default class ViewPortDetector extends Component {
|
|
|
22
66
|
|
|
23
67
|
_defineProperty(this, "view", null);
|
|
24
68
|
|
|
69
|
+
_defineProperty(this, "lastAppStateActive", false);
|
|
70
|
+
|
|
71
|
+
_defineProperty(this, "appStateSubscription", null);
|
|
72
|
+
|
|
73
|
+
_defineProperty(this, "hasValidTimeout", (disabled, delay) => {
|
|
74
|
+
let disabledValue = disabled !== null && disabled !== void 0 ? disabled : false;
|
|
75
|
+
let delayValue = delay !== null && delay !== void 0 ? delay : DEFAULT_DELAY;
|
|
76
|
+
return AppState.currentState === 'active' && !disabledValue && delayValue > 0;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
_defineProperty(this, "handleAppStateChange", nextAppState => {
|
|
80
|
+
let nextAppStateActive = nextAppState === 'active';
|
|
81
|
+
|
|
82
|
+
if (this.lastAppStateActive !== nextAppStateActive) {
|
|
83
|
+
this.checkVisibility();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.lastAppStateActive = nextAppStateActive;
|
|
87
|
+
|
|
88
|
+
if (!this.hasValidTimeout(this.props.disabled, this.props.delay)) {
|
|
89
|
+
this.stopWatching();
|
|
90
|
+
} else {
|
|
91
|
+
this.startWatching();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
_defineProperty(this, "startWatching", () => {
|
|
96
|
+
if (this.interval) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.interval = setIntervalWithTimeout(this.checkVisibility, this.props.delay || DEFAULT_DELAY);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
_defineProperty(this, "stopWatching", () => {
|
|
104
|
+
var _this$interval;
|
|
105
|
+
|
|
106
|
+
(_this$interval = this.interval) === null || _this$interval === void 0 ? void 0 : _this$interval.clear();
|
|
107
|
+
this.interval = null;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
_defineProperty(this, "checkVisibility", () => {
|
|
111
|
+
if (!this.view) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (AppState.currentState !== 'active') {
|
|
116
|
+
this.updateVisibility(false);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.view.measure((_x, _y, width, height, _pageX, _pageY) => {
|
|
121
|
+
this.checkInViewPort(width, height);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
_defineProperty(this, "checkInViewPort", (width, height) => {
|
|
126
|
+
let isVisible; // Not visible if any of these are missing.
|
|
127
|
+
|
|
128
|
+
if (!width || !height) {
|
|
129
|
+
isVisible = false;
|
|
130
|
+
} else {
|
|
131
|
+
isVisible = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.updateVisibility(isVisible);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
_defineProperty(this, "updateVisibility", isVisible => {
|
|
138
|
+
if (this.lastValue !== isVisible) {
|
|
139
|
+
var _this$props$onChange, _this$props;
|
|
140
|
+
|
|
141
|
+
this.lastValue = isVisible;
|
|
142
|
+
(_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, isVisible);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
25
146
|
this.state = {
|
|
26
147
|
rectTop: 0,
|
|
27
148
|
rectBottom: 0
|
|
@@ -29,58 +150,31 @@ export default class ViewPortDetector extends Component {
|
|
|
29
150
|
}
|
|
30
151
|
|
|
31
152
|
componentDidMount() {
|
|
32
|
-
|
|
153
|
+
this.lastAppStateActive = AppState.currentState === 'active';
|
|
154
|
+
this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);
|
|
155
|
+
|
|
156
|
+
if (this.hasValidTimeout(this.props.disabled, this.props.delay)) {
|
|
33
157
|
this.startWatching();
|
|
34
158
|
}
|
|
35
159
|
}
|
|
36
160
|
|
|
37
161
|
componentWillUnmount() {
|
|
162
|
+
var _this$appStateSubscri;
|
|
163
|
+
|
|
164
|
+
(_this$appStateSubscri = this.appStateSubscription) === null || _this$appStateSubscri === void 0 ? void 0 : _this$appStateSubscri.remove();
|
|
165
|
+
this.appStateSubscription = null;
|
|
38
166
|
this.stopWatching();
|
|
39
167
|
}
|
|
40
168
|
|
|
41
169
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
42
|
-
if (nextProps.disabled) {
|
|
170
|
+
if (!this.hasValidTimeout(nextProps.disabled, nextProps.delay)) {
|
|
43
171
|
this.stopWatching();
|
|
44
172
|
} else {
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
startWatching() {
|
|
51
|
-
if (this.interval) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
this.interval = setInterval(() => {
|
|
56
|
-
if (!this.view) {
|
|
57
|
-
return;
|
|
173
|
+
if (this.props.propKey !== nextProps.propKey) {
|
|
174
|
+
this.lastValue = null;
|
|
58
175
|
}
|
|
59
176
|
|
|
60
|
-
this.
|
|
61
|
-
this.checkInViewPort(width, height);
|
|
62
|
-
});
|
|
63
|
-
}, this.props.delay || 100);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
stopWatching() {
|
|
67
|
-
this.interval = clearInterval(this.interval);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
checkInViewPort(width, height) {
|
|
71
|
-
let isVisible; // Not visible if any of these are missing.
|
|
72
|
-
|
|
73
|
-
if (!width || !height) {
|
|
74
|
-
isVisible = false;
|
|
75
|
-
} else {
|
|
76
|
-
isVisible = true;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (this.lastValue !== isVisible) {
|
|
80
|
-
var _this$props$onChange, _this$props;
|
|
81
|
-
|
|
82
|
-
this.lastValue = isVisible;
|
|
83
|
-
(_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, isVisible);
|
|
177
|
+
this.startWatching();
|
|
84
178
|
}
|
|
85
179
|
}
|
|
86
180
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["ViewPortDetector.tsx"],"names":["React","Component","View","ViewPortDetector","constructor","props","
|
|
1
|
+
{"version":3,"sources":["ViewPortDetector.tsx"],"names":["React","Component","AppState","View","DEFAULT_DELAY","TimeoutHandler","id","handler","handlerRef","n","clear","clearTimeout","setIntervalWithTimeout","callback","intervalMs","handleWrapper","cleared","timeout","setTimeout","ViewPortDetector","constructor","props","disabled","delay","disabledValue","delayValue","currentState","nextAppState","nextAppStateActive","lastAppStateActive","checkVisibility","hasValidTimeout","stopWatching","startWatching","interval","view","updateVisibility","measure","_x","_y","width","height","_pageX","_pageY","checkInViewPort","isVisible","lastValue","onChange","state","rectTop","rectBottom","componentDidMount","appStateSubscription","addEventListener","handleAppStateChange","componentWillUnmount","remove","UNSAFE_componentWillReceiveProps","nextProps","propKey","render","component","children"],"mappings":"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","sourcesContent":["'use strict';\n\nimport React, { Component, PropsWithChildren } from 'react';\nimport {\n AppState,\n AppStateStatus,\n NativeEventSubscription,\n View,\n ViewStyle,\n} from 'react-native';\n\nconst DEFAULT_DELAY = 1000;\n\nexport type Props = {\n disabled?: boolean;\n style?: ViewStyle;\n onChange?: (isVisible: boolean) => void;\n delay?: number;\n propKey?: any;\n};\n\nclass TimeoutHandler {\n private handlerRef: { id: any } = { id: -1 };\n\n get handler(): any {\n return this.handlerRef.id;\n }\n set handler(n: any) {\n this.handlerRef.id = n;\n }\n\n clear() {\n clearTimeout(this.handlerRef.id as any);\n }\n}\n\nfunction setIntervalWithTimeout(\n callback: (clear: () => void) => any,\n intervalMs: number,\n handleWrapper = new TimeoutHandler()\n): TimeoutHandler {\n let cleared = false;\n\n const timeout = () => {\n handleWrapper.handler = setTimeout(() => {\n callback(() => {\n cleared = true;\n handleWrapper.clear();\n });\n if (!cleared) {\n timeout();\n }\n }, intervalMs);\n };\n timeout();\n return handleWrapper;\n}\n\n/**\n * Detects when this is in the viewport and visible.\n *\n * Will not fire visibility changes for zero width/height components.\n */\nexport default class ViewPortDetector extends Component<\n PropsWithChildren<Props>\n> {\n private lastValue: boolean | null = null;\n private interval: TimeoutHandler | null = null;\n private view: View | null = null;\n private lastAppStateActive = false;\n private appStateSubscription: NativeEventSubscription | null = null;\n\n constructor(props: Props) {\n super(props);\n this.state = { rectTop: 0, rectBottom: 0 };\n }\n\n componentDidMount() {\n this.lastAppStateActive = AppState.currentState === 'active';\n this.appStateSubscription = AppState.addEventListener(\n 'change',\n this.handleAppStateChange\n );\n if (this.hasValidTimeout(this.props.disabled, this.props.delay)) {\n this.startWatching();\n }\n }\n\n componentWillUnmount() {\n this.appStateSubscription?.remove();\n this.appStateSubscription = null;\n this.stopWatching();\n }\n\n hasValidTimeout = (disabled?: boolean, delay?: number): boolean => {\n let disabledValue = disabled ?? false;\n let delayValue = delay ?? DEFAULT_DELAY;\n return (\n AppState.currentState === 'active' && !disabledValue && delayValue > 0\n );\n };\n\n UNSAFE_componentWillReceiveProps(nextProps: Props) {\n if (!this.hasValidTimeout(nextProps.disabled, nextProps.delay)) {\n this.stopWatching();\n } else {\n if (this.props.propKey !== nextProps.propKey) {\n this.lastValue = null;\n }\n this.startWatching();\n }\n }\n handleAppStateChange = (nextAppState: AppStateStatus) => {\n let nextAppStateActive = nextAppState === 'active';\n if (this.lastAppStateActive !== nextAppStateActive) {\n this.checkVisibility();\n }\n this.lastAppStateActive = nextAppStateActive;\n\n if (!this.hasValidTimeout(this.props.disabled, this.props.delay)) {\n this.stopWatching();\n } else {\n this.startWatching();\n }\n };\n\n startWatching = () => {\n if (this.interval) {\n return;\n }\n this.interval = setIntervalWithTimeout(\n this.checkVisibility,\n this.props.delay || DEFAULT_DELAY\n );\n };\n\n stopWatching = () => {\n this.interval?.clear();\n this.interval = null;\n };\n\n checkVisibility = () => {\n if (!this.view) {\n return;\n }\n\n if (AppState.currentState !== 'active') {\n this.updateVisibility(false);\n return;\n }\n\n this.view.measure((_x, _y, width, height, _pageX, _pageY) => {\n this.checkInViewPort(width, height);\n });\n };\n checkInViewPort = (width?: number, height?: number) => {\n let isVisible: boolean;\n // Not visible if any of these are missing.\n if (!width || !height) {\n isVisible = false;\n } else {\n isVisible = true;\n }\n this.updateVisibility(isVisible);\n };\n\n updateVisibility = (isVisible: boolean) => {\n if (this.lastValue !== isVisible) {\n this.lastValue = isVisible;\n this.props.onChange?.(isVisible);\n }\n };\n render() {\n return (\n <View\n collapsable={false}\n ref={(component) => {\n this.view = component;\n }}\n {...this.props}\n >\n {this.props.children}\n </View>\n );\n }\n}\n"]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { Component, PropsWithChildren } from 'react';
|
|
2
|
-
import { ViewStyle } from 'react-native';
|
|
2
|
+
import { AppStateStatus, ViewStyle } from 'react-native';
|
|
3
3
|
export declare type Props = {
|
|
4
4
|
disabled?: boolean;
|
|
5
5
|
style?: ViewStyle;
|
|
6
6
|
onChange?: (isVisible: boolean) => void;
|
|
7
7
|
delay?: number;
|
|
8
|
+
propKey?: any;
|
|
8
9
|
};
|
|
9
10
|
/**
|
|
10
11
|
* Detects when this is in the viewport and visible.
|
|
@@ -15,12 +16,18 @@ export default class ViewPortDetector extends Component<PropsWithChildren<Props>
|
|
|
15
16
|
private lastValue;
|
|
16
17
|
private interval;
|
|
17
18
|
private view;
|
|
19
|
+
private lastAppStateActive;
|
|
20
|
+
private appStateSubscription;
|
|
18
21
|
constructor(props: Props);
|
|
19
22
|
componentDidMount(): void;
|
|
20
23
|
componentWillUnmount(): void;
|
|
24
|
+
hasValidTimeout: (disabled?: boolean, delay?: number) => boolean;
|
|
21
25
|
UNSAFE_componentWillReceiveProps(nextProps: Props): void;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
handleAppStateChange: (nextAppState: AppStateStatus) => void;
|
|
27
|
+
startWatching: () => void;
|
|
28
|
+
stopWatching: () => void;
|
|
29
|
+
checkVisibility: () => void;
|
|
30
|
+
checkInViewPort: (width?: number, height?: number) => void;
|
|
31
|
+
updateVisibility: (isVisible: boolean) => void;
|
|
25
32
|
render(): React.JSX.Element;
|
|
26
33
|
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
VideoTrack,
|
|
10
10
|
} from 'livekit-client';
|
|
11
11
|
import { RTCView } from '@livekit/react-native-webrtc';
|
|
12
|
-
import { useEffect, useState } from 'react';
|
|
12
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
13
13
|
import { RemoteVideoTrack } from 'livekit-client';
|
|
14
14
|
import ViewPortDetector from './ViewPortDetector';
|
|
15
15
|
|
|
@@ -35,6 +35,20 @@ export const VideoView = ({
|
|
|
35
35
|
return info;
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
+
const layoutOnChange = useCallback(
|
|
39
|
+
(event: LayoutChangeEvent) => elementInfo.onLayout(event),
|
|
40
|
+
[elementInfo]
|
|
41
|
+
);
|
|
42
|
+
const visibilityOnChange = useCallback(
|
|
43
|
+
(isVisible: boolean) => elementInfo.onVisibility(isVisible),
|
|
44
|
+
[elementInfo]
|
|
45
|
+
);
|
|
46
|
+
const shouldObserveVisibility = useMemo(() => {
|
|
47
|
+
return (
|
|
48
|
+
videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream
|
|
49
|
+
);
|
|
50
|
+
}, [videoTrack]);
|
|
51
|
+
|
|
38
52
|
const [mediaStream, setMediaStream] = useState(videoTrack?.mediaStream);
|
|
39
53
|
useEffect(() => {
|
|
40
54
|
setMediaStream(videoTrack?.mediaStream);
|
|
@@ -64,22 +78,15 @@ export const VideoView = ({
|
|
|
64
78
|
}, [videoTrack, elementInfo]);
|
|
65
79
|
|
|
66
80
|
return (
|
|
67
|
-
<View
|
|
68
|
-
style={{ ...style, ...styles.container }}
|
|
69
|
-
onLayout={(event) => {
|
|
70
|
-
elementInfo.onLayout(event);
|
|
71
|
-
}}
|
|
72
|
-
>
|
|
81
|
+
<View style={{ ...style, ...styles.container }} onLayout={layoutOnChange}>
|
|
73
82
|
<ViewPortDetector
|
|
74
|
-
onChange={
|
|
83
|
+
onChange={visibilityOnChange}
|
|
75
84
|
style={styles.videoView}
|
|
85
|
+
disabled={!shouldObserveVisibility}
|
|
86
|
+
propKey={videoTrack}
|
|
76
87
|
>
|
|
77
88
|
<RTCView
|
|
78
|
-
|
|
79
|
-
style={{
|
|
80
|
-
flex: 1,
|
|
81
|
-
width: '100%',
|
|
82
|
-
}}
|
|
89
|
+
style={styles.videoView}
|
|
83
90
|
streamURL={mediaStream?.toURL() ?? ''}
|
|
84
91
|
objectFit={objectFit}
|
|
85
92
|
zOrder={zOrder}
|
|
@@ -1,15 +1,61 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
import React, { Component, PropsWithChildren } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AppState,
|
|
6
|
+
AppStateStatus,
|
|
7
|
+
NativeEventSubscription,
|
|
8
|
+
View,
|
|
9
|
+
ViewStyle,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_DELAY = 1000;
|
|
5
13
|
|
|
6
14
|
export type Props = {
|
|
7
15
|
disabled?: boolean;
|
|
8
16
|
style?: ViewStyle;
|
|
9
17
|
onChange?: (isVisible: boolean) => void;
|
|
10
18
|
delay?: number;
|
|
19
|
+
propKey?: any;
|
|
11
20
|
};
|
|
12
21
|
|
|
22
|
+
class TimeoutHandler {
|
|
23
|
+
private handlerRef: { id: any } = { id: -1 };
|
|
24
|
+
|
|
25
|
+
get handler(): any {
|
|
26
|
+
return this.handlerRef.id;
|
|
27
|
+
}
|
|
28
|
+
set handler(n: any) {
|
|
29
|
+
this.handlerRef.id = n;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
clear() {
|
|
33
|
+
clearTimeout(this.handlerRef.id as any);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function setIntervalWithTimeout(
|
|
38
|
+
callback: (clear: () => void) => any,
|
|
39
|
+
intervalMs: number,
|
|
40
|
+
handleWrapper = new TimeoutHandler()
|
|
41
|
+
): TimeoutHandler {
|
|
42
|
+
let cleared = false;
|
|
43
|
+
|
|
44
|
+
const timeout = () => {
|
|
45
|
+
handleWrapper.handler = setTimeout(() => {
|
|
46
|
+
callback(() => {
|
|
47
|
+
cleared = true;
|
|
48
|
+
handleWrapper.clear();
|
|
49
|
+
});
|
|
50
|
+
if (!cleared) {
|
|
51
|
+
timeout();
|
|
52
|
+
}
|
|
53
|
+
}, intervalMs);
|
|
54
|
+
};
|
|
55
|
+
timeout();
|
|
56
|
+
return handleWrapper;
|
|
57
|
+
}
|
|
58
|
+
|
|
13
59
|
/**
|
|
14
60
|
* Detects when this is in the viewport and visible.
|
|
15
61
|
*
|
|
@@ -19,8 +65,10 @@ export default class ViewPortDetector extends Component<
|
|
|
19
65
|
PropsWithChildren<Props>
|
|
20
66
|
> {
|
|
21
67
|
private lastValue: boolean | null = null;
|
|
22
|
-
private interval:
|
|
68
|
+
private interval: TimeoutHandler | null = null;
|
|
23
69
|
private view: View | null = null;
|
|
70
|
+
private lastAppStateActive = false;
|
|
71
|
+
private appStateSubscription: NativeEventSubscription | null = null;
|
|
24
72
|
|
|
25
73
|
constructor(props: Props) {
|
|
26
74
|
super(props);
|
|
@@ -28,43 +76,84 @@ export default class ViewPortDetector extends Component<
|
|
|
28
76
|
}
|
|
29
77
|
|
|
30
78
|
componentDidMount() {
|
|
31
|
-
|
|
79
|
+
this.lastAppStateActive = AppState.currentState === 'active';
|
|
80
|
+
this.appStateSubscription = AppState.addEventListener(
|
|
81
|
+
'change',
|
|
82
|
+
this.handleAppStateChange
|
|
83
|
+
);
|
|
84
|
+
if (this.hasValidTimeout(this.props.disabled, this.props.delay)) {
|
|
32
85
|
this.startWatching();
|
|
33
86
|
}
|
|
34
87
|
}
|
|
35
88
|
|
|
36
89
|
componentWillUnmount() {
|
|
90
|
+
this.appStateSubscription?.remove();
|
|
91
|
+
this.appStateSubscription = null;
|
|
37
92
|
this.stopWatching();
|
|
38
93
|
}
|
|
39
94
|
|
|
95
|
+
hasValidTimeout = (disabled?: boolean, delay?: number): boolean => {
|
|
96
|
+
let disabledValue = disabled ?? false;
|
|
97
|
+
let delayValue = delay ?? DEFAULT_DELAY;
|
|
98
|
+
return (
|
|
99
|
+
AppState.currentState === 'active' && !disabledValue && delayValue > 0
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
40
103
|
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
|
41
|
-
if (nextProps.disabled) {
|
|
104
|
+
if (!this.hasValidTimeout(nextProps.disabled, nextProps.delay)) {
|
|
42
105
|
this.stopWatching();
|
|
43
106
|
} else {
|
|
44
|
-
this.
|
|
107
|
+
if (this.props.propKey !== nextProps.propKey) {
|
|
108
|
+
this.lastValue = null;
|
|
109
|
+
}
|
|
45
110
|
this.startWatching();
|
|
46
111
|
}
|
|
47
112
|
}
|
|
113
|
+
handleAppStateChange = (nextAppState: AppStateStatus) => {
|
|
114
|
+
let nextAppStateActive = nextAppState === 'active';
|
|
115
|
+
if (this.lastAppStateActive !== nextAppStateActive) {
|
|
116
|
+
this.checkVisibility();
|
|
117
|
+
}
|
|
118
|
+
this.lastAppStateActive = nextAppStateActive;
|
|
48
119
|
|
|
49
|
-
|
|
120
|
+
if (!this.hasValidTimeout(this.props.disabled, this.props.delay)) {
|
|
121
|
+
this.stopWatching();
|
|
122
|
+
} else {
|
|
123
|
+
this.startWatching();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
startWatching = () => {
|
|
50
128
|
if (this.interval) {
|
|
51
129
|
return;
|
|
52
130
|
}
|
|
53
|
-
this.interval =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.checkInViewPort(width, height);
|
|
59
|
-
});
|
|
60
|
-
}, this.props.delay || 100);
|
|
61
|
-
}
|
|
131
|
+
this.interval = setIntervalWithTimeout(
|
|
132
|
+
this.checkVisibility,
|
|
133
|
+
this.props.delay || DEFAULT_DELAY
|
|
134
|
+
);
|
|
135
|
+
};
|
|
62
136
|
|
|
63
|
-
|
|
64
|
-
this.interval
|
|
65
|
-
|
|
137
|
+
stopWatching = () => {
|
|
138
|
+
this.interval?.clear();
|
|
139
|
+
this.interval = null;
|
|
140
|
+
};
|
|
66
141
|
|
|
67
|
-
|
|
142
|
+
checkVisibility = () => {
|
|
143
|
+
if (!this.view) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (AppState.currentState !== 'active') {
|
|
148
|
+
this.updateVisibility(false);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.view.measure((_x, _y, width, height, _pageX, _pageY) => {
|
|
153
|
+
this.checkInViewPort(width, height);
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
checkInViewPort = (width?: number, height?: number) => {
|
|
68
157
|
let isVisible: boolean;
|
|
69
158
|
// Not visible if any of these are missing.
|
|
70
159
|
if (!width || !height) {
|
|
@@ -72,13 +161,15 @@ export default class ViewPortDetector extends Component<
|
|
|
72
161
|
} else {
|
|
73
162
|
isVisible = true;
|
|
74
163
|
}
|
|
164
|
+
this.updateVisibility(isVisible);
|
|
165
|
+
};
|
|
75
166
|
|
|
167
|
+
updateVisibility = (isVisible: boolean) => {
|
|
76
168
|
if (this.lastValue !== isVisible) {
|
|
77
169
|
this.lastValue = isVisible;
|
|
78
170
|
this.props.onChange?.(isVisible);
|
|
79
171
|
}
|
|
80
|
-
}
|
|
81
|
-
|
|
172
|
+
};
|
|
82
173
|
render() {
|
|
83
174
|
return (
|
|
84
175
|
<View
|