@stream-io/video-react-native-sdk 1.8.0 → 1.9.0
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 +7 -0
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNative.kt +23 -11
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +71 -23
- package/dist/commonjs/components/Call/CallContent/CallContent.js +1 -1
- package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsGrid.js +2 -3
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsGrid.js.map +1 -1
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js +2 -3
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
- package/dist/commonjs/hooks/useAutoEnterPiPEffect.js +5 -3
- package/dist/commonjs/hooks/useAutoEnterPiPEffect.js.map +1 -1
- package/dist/commonjs/hooks/useIsInPiPMode.js +14 -24
- package/dist/commonjs/hooks/useIsInPiPMode.js.map +1 -1
- package/dist/commonjs/providers/StreamCall/AppStateListener.js +101 -0
- package/dist/commonjs/providers/StreamCall/AppStateListener.js.map +1 -0
- package/dist/commonjs/providers/StreamCall/DeviceStats.js +45 -0
- package/dist/commonjs/providers/StreamCall/DeviceStats.js.map +1 -0
- package/dist/commonjs/providers/StreamCall/index.js +69 -0
- package/dist/commonjs/providers/StreamCall/index.js.map +1 -0
- package/dist/commonjs/utils/internal/rxSubjects.js +10 -0
- package/dist/commonjs/utils/internal/rxSubjects.js.map +1 -0
- package/dist/commonjs/version.js +1 -1
- package/dist/module/components/Call/CallContent/CallContent.js +1 -1
- package/dist/module/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/module/components/Call/CallLayout/CallParticipantsGrid.js +2 -3
- package/dist/module/components/Call/CallLayout/CallParticipantsGrid.js.map +1 -1
- package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js +2 -3
- package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
- package/dist/module/hooks/useAutoEnterPiPEffect.js +5 -3
- package/dist/module/hooks/useAutoEnterPiPEffect.js.map +1 -1
- package/dist/module/hooks/useIsInPiPMode.js +14 -24
- package/dist/module/hooks/useIsInPiPMode.js.map +1 -1
- package/dist/module/providers/StreamCall/AppStateListener.js +94 -0
- package/dist/module/providers/StreamCall/AppStateListener.js.map +1 -0
- package/dist/module/providers/StreamCall/DeviceStats.js +38 -0
- package/dist/module/providers/StreamCall/DeviceStats.js.map +1 -0
- package/dist/module/providers/StreamCall/index.js +61 -0
- package/dist/module/providers/StreamCall/index.js.map +1 -0
- package/dist/module/utils/internal/rxSubjects.js +4 -0
- package/dist/module/utils/internal/rxSubjects.js.map +1 -0
- package/dist/module/version.js +1 -1
- package/dist/typescript/components/Call/CallContent/CallContent.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallLayout/CallParticipantsGrid.d.ts +2 -2
- package/dist/typescript/components/Call/CallLayout/CallParticipantsGrid.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallLayout/CallParticipantsSpotlight.d.ts +2 -2
- package/dist/typescript/components/Call/CallLayout/CallParticipantsSpotlight.d.ts.map +1 -1
- package/dist/typescript/hooks/useAutoEnterPiPEffect.d.ts.map +1 -1
- package/dist/typescript/hooks/useIsInPiPMode.d.ts +1 -1
- package/dist/typescript/hooks/useIsInPiPMode.d.ts.map +1 -1
- package/dist/typescript/providers/StreamCall/AppStateListener.d.ts +2 -0
- package/dist/typescript/providers/StreamCall/AppStateListener.d.ts.map +1 -0
- package/dist/typescript/providers/StreamCall/DeviceStats.d.ts +5 -0
- package/dist/typescript/providers/StreamCall/DeviceStats.d.ts.map +1 -0
- package/dist/typescript/providers/{StreamCall.d.ts → StreamCall/index.d.ts} +1 -1
- package/dist/typescript/providers/StreamCall/index.d.ts.map +1 -0
- package/dist/typescript/utils/internal/rxSubjects.d.ts +4 -0
- package/dist/typescript/utils/internal/rxSubjects.d.ts.map +1 -0
- package/dist/typescript/version.d.ts +1 -1
- package/expo-config-plugin/dist/common/types.d.ts +1 -4
- package/expo-config-plugin/dist/withMainActivity.js +42 -36
- package/package.json +1 -1
- package/src/components/Call/CallContent/CallContent.tsx +2 -1
- package/src/components/Call/CallLayout/CallParticipantsGrid.tsx +2 -6
- package/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx +2 -6
- package/src/hooks/useAutoEnterPiPEffect.tsx +5 -2
- package/src/hooks/useIsInPiPMode.tsx +16 -52
- package/src/providers/StreamCall/AppStateListener.tsx +125 -0
- package/src/providers/StreamCall/DeviceStats.tsx +59 -0
- package/src/providers/StreamCall/index.tsx +78 -0
- package/src/utils/internal/rxSubjects.ts +5 -0
- package/src/version.ts +1 -1
- package/dist/commonjs/providers/StreamCall.js +0 -155
- package/dist/commonjs/providers/StreamCall.js.map +0 -1
- package/dist/module/providers/StreamCall.js +0 -146
- package/dist/module/providers/StreamCall.js.map +0 -1
- package/dist/typescript/providers/StreamCall.d.ts.map +0 -1
- package/src/providers/StreamCall.tsx +0 -205
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["BehaviorSubject","isInPiPModeAndroid$","disablePiPMode$"],"sourceRoot":"../../../../src","sources":["utils/internal/rxSubjects.ts"],"mappings":"AAAA,SAASA,eAAe,QAAQ,MAAM;AAEtC,OAAO,MAAMC,mBAAmB,GAAG,IAAID,eAAe,CAAU,KAAK,CAAC;AAEtE,OAAO,MAAME,eAAe,GAAG,IAAIF,eAAe,CAAU,KAAK,CAAC","ignoreList":[]}
|
package/dist/module/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '1.
|
|
1
|
+
export const version = '1.9.0';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallContent.d.ts","sourceRoot":"","sources":["../../../../../src/components/Call/CallContent/CallContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAS5D,OAAO,EACL,gBAAgB,EAEhB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAIzD,OAAO,EAEL,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kCAAkC,EAClC,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAEL,uBAAuB,EACxB,MAAM,kCAAkC,CAAC;AAG1C,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG;IAChD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,KAAK,yBAAyB,GAAG,6BAA6B,GAC5D,IAAI,CAAC,kCAAkC,EAAE,iBAAiB,CAAC,GAAG;IAC5D;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;IAC5D;;OAEG;IACH,uBAAuB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;IACnF;;OAEG;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,yBAAyB,CAAC,GAAG,IAAI,CAAC;IAC7E;;OAEG;IACH,kBAAkB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,uBAAuB,CAAC,GAAG,IAAI,CAAC;CAC1E,CAAC;AAEJ,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,qBAAqB,EACrB,qBAAqB,CACtB,GACC,yBAAyB,GAAG;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC9B;;OAEG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAK1C,SAAS,CAAC,EAAE,OAAO,CAAC;IAIpB,kCAAkC,CAAC,EAAE,OAAO,CAAC;IAC7C;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEJ,eAAO,MAAM,WAAW,uVAiBrB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"CallContent.d.ts","sourceRoot":"","sources":["../../../../../src/components/Call/CallContent/CallContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAS5D,OAAO,EACL,gBAAgB,EAEhB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAIzD,OAAO,EAEL,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,kCAAkC,EAClC,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAEL,uBAAuB,EACxB,MAAM,kCAAkC,CAAC;AAG1C,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG;IAChD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,KAAK,yBAAyB,GAAG,6BAA6B,GAC5D,IAAI,CAAC,kCAAkC,EAAE,iBAAiB,CAAC,GAAG;IAC5D;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;IAC5D;;OAEG;IACH,uBAAuB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;IACnF;;OAEG;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,yBAAyB,CAAC,GAAG,IAAI,CAAC;IAC7E;;OAEG;IACH,kBAAkB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,uBAAuB,CAAC,GAAG,IAAI,CAAC;CAC1E,CAAC;AAEJ,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,qBAAqB,EACrB,qBAAqB,CACtB,GACC,yBAAyB,GAAG;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC9B;;OAEG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAK1C,SAAS,CAAC,EAAE,OAAO,CAAC;IAIpB,kCAAkC,CAAC,EAAE,OAAO,CAAC;IAC7C;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEJ,eAAO,MAAM,WAAW,uVAiBrB,gBAAgB,sBAmIlB,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { ParticipantViewComponentProps } from '../../Participant';
|
|
|
5
5
|
/**
|
|
6
6
|
* Props for the CallParticipantsGrid component.
|
|
7
7
|
*/
|
|
8
|
-
export type CallParticipantsGridProps = ParticipantViewComponentProps & Pick<CallContentProps, 'supportedReactions' | 'CallParticipantsList'
|
|
8
|
+
export type CallParticipantsGridProps = ParticipantViewComponentProps & Pick<CallContentProps, 'supportedReactions' | 'CallParticipantsList'> & Pick<CallParticipantsListComponentProps, 'ParticipantView'> & {
|
|
9
9
|
/**
|
|
10
10
|
* Boolean to decide if local participant will be visible in the grid when there is 1:1 call.
|
|
11
11
|
*/
|
|
@@ -19,5 +19,5 @@ export type CallParticipantsGridProps = ParticipantViewComponentProps & Pick<Cal
|
|
|
19
19
|
/**
|
|
20
20
|
* Component used to display the list of participants in a grid mode.
|
|
21
21
|
*/
|
|
22
|
-
export declare const CallParticipantsGrid: ({ CallParticipantsList, ParticipantLabel, ParticipantNetworkQualityIndicator, ParticipantReaction, ParticipantVideoFallback, ParticipantView, VideoRenderer, showLocalParticipant, supportedReactions, landscape,
|
|
22
|
+
export declare const CallParticipantsGrid: ({ CallParticipantsList, ParticipantLabel, ParticipantNetworkQualityIndicator, ParticipantReaction, ParticipantVideoFallback, ParticipantView, VideoRenderer, showLocalParticipant, supportedReactions, landscape, }: CallParticipantsGridProps) => React.JSX.Element;
|
|
23
23
|
//# sourceMappingURL=CallParticipantsGrid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallParticipantsGrid.d.ts","sourceRoot":"","sources":["../../../../../src/components/Call/CallLayout/CallParticipantsGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAEL,kCAAkC,EACnC,MAAM,8CAA8C,CAAC;AAGtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAIlE;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,6BAA6B,GACnE,IAAI,
|
|
1
|
+
{"version":3,"file":"CallParticipantsGrid.d.ts","sourceRoot":"","sources":["../../../../../src/components/Call/CallLayout/CallParticipantsGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAEL,kCAAkC,EACnC,MAAM,8CAA8C,CAAC;AAGtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAIlE;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,6BAA6B,GACnE,IAAI,CAAC,gBAAgB,EAAE,oBAAoB,GAAG,sBAAsB,CAAC,GACrE,IAAI,CAAC,kCAAkC,EAAE,iBAAiB,CAAC,GAAG;IAC5D;;OAEG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,oBAAoB,wNAW9B,yBAAyB,sBAwE3B,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { CallContentProps } from '../CallContent';
|
|
|
5
5
|
/**
|
|
6
6
|
* Props for the CallParticipantsSpotlight component.
|
|
7
7
|
*/
|
|
8
|
-
export type CallParticipantsSpotlightProps = ParticipantViewComponentProps & Pick<CallContentProps, 'supportedReactions' | 'CallParticipantsList' | 'ScreenShareOverlay'
|
|
8
|
+
export type CallParticipantsSpotlightProps = ParticipantViewComponentProps & Pick<CallContentProps, 'supportedReactions' | 'CallParticipantsList' | 'ScreenShareOverlay'> & Pick<CallParticipantsListComponentProps, 'ParticipantView'> & {
|
|
9
9
|
/**
|
|
10
10
|
* Check if device is in landscape mode.
|
|
11
11
|
* This will apply the landscape mode styles to the component.
|
|
@@ -16,5 +16,5 @@ export type CallParticipantsSpotlightProps = ParticipantViewComponentProps & Pic
|
|
|
16
16
|
* Component used to display the list of participants in a spotlight mode.
|
|
17
17
|
* This can be used when you want to render the screen sharing stream.
|
|
18
18
|
*/
|
|
19
|
-
export declare const CallParticipantsSpotlight: ({ CallParticipantsList, ParticipantLabel, ParticipantNetworkQualityIndicator, ParticipantReaction, ParticipantVideoFallback, ParticipantView, ScreenShareOverlay, VideoRenderer, supportedReactions, landscape,
|
|
19
|
+
export declare const CallParticipantsSpotlight: ({ CallParticipantsList, ParticipantLabel, ParticipantNetworkQualityIndicator, ParticipantReaction, ParticipantVideoFallback, ParticipantView, ScreenShareOverlay, VideoRenderer, supportedReactions, landscape, }: CallParticipantsSpotlightProps) => React.JSX.Element;
|
|
20
20
|
//# sourceMappingURL=CallParticipantsSpotlight.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallParticipantsSpotlight.d.ts","sourceRoot":"","sources":["../../../../../src/components/Call/CallLayout/CallParticipantsSpotlight.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AASvC,OAAO,EAEL,kCAAkC,EACnC,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAEL,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,6BAA6B,GACxE,IAAI,CACF,gBAAgB,
|
|
1
|
+
{"version":3,"file":"CallParticipantsSpotlight.d.ts","sourceRoot":"","sources":["../../../../../src/components/Call/CallLayout/CallParticipantsSpotlight.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AASvC,OAAO,EAEL,kCAAkC,EACnC,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAEL,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlD;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,6BAA6B,GACxE,IAAI,CACF,gBAAgB,EAChB,oBAAoB,GAAG,sBAAsB,GAAG,oBAAoB,CACrE,GACD,IAAI,CAAC,kCAAkC,EAAE,iBAAiB,CAAC,GAAG;IAC5D;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,yBAAyB,sNAWnC,8BAA8B,sBAsGhC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAutoEnterPiPEffect.d.ts","sourceRoot":"","sources":["../../../src/hooks/useAutoEnterPiPEffect.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAutoEnterPiPEffect.d.ts","sourceRoot":"","sources":["../../../src/hooks/useAutoEnterPiPEffect.tsx"],"names":[],"mappings":"AAMA,wBAAgB,qBAAqB,CACnC,uBAAuB,EAAE,OAAO,GAAG,SAAS,QA0C7C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function useIsInPiPMode(
|
|
1
|
+
export declare function useIsInPiPMode(): boolean;
|
|
2
2
|
//# sourceMappingURL=useIsInPiPMode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIsInPiPMode.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIsInPiPMode.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useIsInPiPMode.d.ts","sourceRoot":"","sources":["../../../src/hooks/useIsInPiPMode.tsx"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,YAmB7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppStateListener.d.ts","sourceRoot":"","sources":["../../../../src/providers/StreamCall/AppStateListener.tsx"],"names":[],"mappings":"AAsBA,eAAO,MAAM,gBAAgB,YAsG5B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeviceStats.d.ts","sourceRoot":"","sources":["../../../../src/providers/StreamCall/DeviceStats.tsx"],"names":[],"mappings":"AAaA;;GAEG;AACH,eAAO,MAAM,WAAW,YA0CvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/providers/StreamCall/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,iBAAiB,EAAa,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAc/C,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AACF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,wBAGpB,iBAAiB,CAAC,eAAe,CAAC,sBAWpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rxSubjects.d.ts","sourceRoot":"","sources":["../../../../src/utils/internal/rxSubjects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAEvC,eAAO,MAAM,mBAAmB,0BAAsC,CAAC;AAEvE,eAAO,MAAM,eAAe,0BAAsC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "1.
|
|
1
|
+
export declare const version = "1.9.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -3,13 +3,10 @@ export type RingingPushNotifications = {
|
|
|
3
3
|
includesCallsInRecentsIos?: boolean;
|
|
4
4
|
showWhenLockedAndroid?: boolean;
|
|
5
5
|
};
|
|
6
|
-
export type AndroidPictureInPicture = {
|
|
7
|
-
enableAutomaticEnter: boolean;
|
|
8
|
-
};
|
|
9
6
|
export type ConfigProps = {
|
|
10
7
|
ringingPushNotifications?: RingingPushNotifications;
|
|
11
8
|
enableNonRingingPushNotifications?: boolean;
|
|
12
|
-
androidPictureInPicture?:
|
|
9
|
+
androidPictureInPicture?: boolean;
|
|
13
10
|
androidKeepCallAlive?: boolean;
|
|
14
11
|
enableScreenshare?: boolean;
|
|
15
12
|
appleTeamId?: string;
|
|
@@ -12,13 +12,12 @@ const withStreamVideoReactNativeSDKMainActivity = (configuration, props) => {
|
|
|
12
12
|
config.modResults.contents = (0, codeMod_1.addImports)(config.modResults.contents, [
|
|
13
13
|
'com.streamvideo.reactnative.StreamVideoReactNative',
|
|
14
14
|
'android.os.Build',
|
|
15
|
-
'android.
|
|
15
|
+
'android.content.res.Configuration',
|
|
16
16
|
'androidx.lifecycle.Lifecycle',
|
|
17
|
-
'android.app.PictureInPictureParams',
|
|
18
17
|
'com.oney.WebRTCModule.WebRTCModuleOptions',
|
|
19
18
|
], isMainActivityJava);
|
|
20
19
|
config.modResults.contents = addOnPictureInPictureModeChanged(config.modResults.contents, isMainActivityJava);
|
|
21
|
-
if (props?.androidPictureInPicture
|
|
20
|
+
if (props?.androidPictureInPicture) {
|
|
22
21
|
config.modResults.contents = addOnUserLeaveHint(config.modResults.contents, isMainActivityJava);
|
|
23
22
|
}
|
|
24
23
|
if (props?.enableScreenshare) {
|
|
@@ -28,32 +27,33 @@ const withStreamVideoReactNativeSDKMainActivity = (configuration, props) => {
|
|
|
28
27
|
});
|
|
29
28
|
};
|
|
30
29
|
function addOnPictureInPictureModeChanged(contents, isJava) {
|
|
31
|
-
if (!contents.includes('StreamVideoReactNative.onPictureInPictureModeChanged')) {
|
|
30
|
+
if (!contents.includes('StreamVideoReactNative.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)')) {
|
|
32
31
|
let statementToInsert = '';
|
|
33
32
|
if (isJava) {
|
|
34
33
|
statementToInsert = `
|
|
35
34
|
@Override
|
|
36
|
-
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
|
|
36
|
+
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
|
|
37
|
+
|
|
38
|
+
if (lifecycleOwner.getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
|
|
39
|
+
// When user clicks on Close button of PIP
|
|
40
|
+
finishAndRemoveTask();
|
|
41
|
+
} else {
|
|
42
|
+
StreamVideoReactNative.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
|
|
43
|
+
}
|
|
44
44
|
}`;
|
|
45
45
|
}
|
|
46
46
|
else {
|
|
47
47
|
// Kotlin
|
|
48
48
|
statementToInsert = `
|
|
49
|
-
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
|
|
50
|
+
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
|
|
51
|
+
if (lifecycle.currentState === Lifecycle.State.CREATED) {
|
|
52
|
+
// when user clicks on Close button of PIP
|
|
53
|
+
finishAndRemoveTask()
|
|
54
|
+
} else {
|
|
55
|
+
StreamVideoReactNative.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
|
56
|
+
}
|
|
57
57
|
}`;
|
|
58
58
|
}
|
|
59
59
|
contents = (0, addNewLinesToMainActivity_1.default)(contents, statementToInsert.trim().split('\n'));
|
|
@@ -61,29 +61,35 @@ function addOnPictureInPictureModeChanged(contents, isJava) {
|
|
|
61
61
|
return contents;
|
|
62
62
|
}
|
|
63
63
|
function addOnUserLeaveHint(contents, isJava) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (
|
|
64
|
+
let statementToInsert = '';
|
|
65
|
+
if (isJava) {
|
|
66
|
+
if (!contents.includes('StreamVideoReactNative.Companion.getCanAutoEnterPictureInPictureMode')) {
|
|
67
67
|
statementToInsert = `
|
|
68
68
|
@Override
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
protected void onUserLeaveHint() {
|
|
70
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
|
71
|
+
Build.VERSION.SDK_INT < Build.VERSION_CODES.S &&
|
|
72
|
+
StreamVideoReactNative.Companion.getCanAutoEnterPictureInPictureMode()) {
|
|
73
|
+
Configuration config = getResources().getConfiguration();
|
|
74
|
+
onPictureInPictureModeChanged(true, config);
|
|
75
|
+
}
|
|
75
76
|
}`;
|
|
76
77
|
}
|
|
77
|
-
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (!contents.includes('StreamVideoReactNative.canAutoEnterPictureInPictureMode')) {
|
|
78
81
|
statementToInsert = `
|
|
79
|
-
override fun onUserLeaveHint
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
override fun onUserLeaveHint() {
|
|
83
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
|
84
|
+
Build.VERSION.SDK_INT < Build.VERSION_CODES.S &&
|
|
85
|
+
StreamVideoReactNative.canAutoEnterPictureInPictureMode) {
|
|
86
|
+
val config = resources.configuration
|
|
87
|
+
onPictureInPictureModeChanged(true, config)
|
|
88
|
+
}
|
|
85
89
|
}`;
|
|
86
90
|
}
|
|
91
|
+
}
|
|
92
|
+
if (statementToInsert) {
|
|
87
93
|
contents = (0, addNewLinesToMainActivity_1.default)(contents, statementToInsert.trim().split('\n'));
|
|
88
94
|
}
|
|
89
95
|
return contents;
|
package/package.json
CHANGED
|
@@ -127,7 +127,7 @@ export const CallContent = ({
|
|
|
127
127
|
const _remoteParticipants = useRemoteParticipants();
|
|
128
128
|
const remoteParticipants = useDebouncedValue(_remoteParticipants, 300); // we debounce the remote participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
|
|
129
129
|
const localParticipant = useLocalParticipant();
|
|
130
|
-
const isInPiPMode = useIsInPiPMode(
|
|
130
|
+
const isInPiPMode = useIsInPiPMode();
|
|
131
131
|
const hasScreenShare = useHasOngoingScreenShare();
|
|
132
132
|
const showSpotlightLayout = hasScreenShare || layout === 'spotlight';
|
|
133
133
|
|
|
@@ -136,6 +136,7 @@ export const CallContent = ({
|
|
|
136
136
|
!isInPiPMode &&
|
|
137
137
|
remoteParticipants.length > 0 &&
|
|
138
138
|
remoteParticipants.length < 3;
|
|
139
|
+
|
|
139
140
|
const isRemoteParticipantInFloatingView =
|
|
140
141
|
showFloatingView &&
|
|
141
142
|
showRemoteParticipantInFloatingView &&
|
|
@@ -17,10 +17,7 @@ import { StreamVideoParticipant } from '@stream-io/video-client';
|
|
|
17
17
|
* Props for the CallParticipantsGrid component.
|
|
18
18
|
*/
|
|
19
19
|
export type CallParticipantsGridProps = ParticipantViewComponentProps &
|
|
20
|
-
Pick<
|
|
21
|
-
CallContentProps,
|
|
22
|
-
'supportedReactions' | 'CallParticipantsList' | 'disablePictureInPicture'
|
|
23
|
-
> &
|
|
20
|
+
Pick<CallContentProps, 'supportedReactions' | 'CallParticipantsList'> &
|
|
24
21
|
Pick<CallParticipantsListComponentProps, 'ParticipantView'> & {
|
|
25
22
|
/**
|
|
26
23
|
* Boolean to decide if local participant will be visible in the grid when there is 1:1 call.
|
|
@@ -47,7 +44,6 @@ export const CallParticipantsGrid = ({
|
|
|
47
44
|
showLocalParticipant = false,
|
|
48
45
|
supportedReactions,
|
|
49
46
|
landscape,
|
|
50
|
-
disablePictureInPicture,
|
|
51
47
|
}: CallParticipantsGridProps) => {
|
|
52
48
|
const {
|
|
53
49
|
theme: { colors, callParticipantsGrid },
|
|
@@ -69,7 +65,7 @@ export const CallParticipantsGrid = ({
|
|
|
69
65
|
flexDirection: landscape ? 'row' : 'column',
|
|
70
66
|
};
|
|
71
67
|
|
|
72
|
-
const isInPiPMode = useIsInPiPMode(
|
|
68
|
+
const isInPiPMode = useIsInPiPMode();
|
|
73
69
|
|
|
74
70
|
const showFloatingView =
|
|
75
71
|
!isInPiPMode &&
|
|
@@ -25,10 +25,7 @@ import { useIsInPiPMode } from '../../../hooks/useIsInPiPMode';
|
|
|
25
25
|
export type CallParticipantsSpotlightProps = ParticipantViewComponentProps &
|
|
26
26
|
Pick<
|
|
27
27
|
CallContentProps,
|
|
28
|
-
| '
|
|
29
|
-
| 'CallParticipantsList'
|
|
30
|
-
| 'ScreenShareOverlay'
|
|
31
|
-
| 'disablePictureInPicture'
|
|
28
|
+
'supportedReactions' | 'CallParticipantsList' | 'ScreenShareOverlay'
|
|
32
29
|
> &
|
|
33
30
|
Pick<CallParticipantsListComponentProps, 'ParticipantView'> & {
|
|
34
31
|
/**
|
|
@@ -53,7 +50,6 @@ export const CallParticipantsSpotlight = ({
|
|
|
53
50
|
VideoRenderer,
|
|
54
51
|
supportedReactions,
|
|
55
52
|
landscape,
|
|
56
|
-
disablePictureInPicture,
|
|
57
53
|
}: CallParticipantsSpotlightProps) => {
|
|
58
54
|
const {
|
|
59
55
|
theme: { callParticipantsSpotlight, variants },
|
|
@@ -69,7 +65,7 @@ export const CallParticipantsSpotlight = ({
|
|
|
69
65
|
participantInSpotlight && hasScreenShare(participantInSpotlight);
|
|
70
66
|
const isUserAloneInCall = _allParticipants?.length === 1;
|
|
71
67
|
|
|
72
|
-
const isInPiP = useIsInPiPMode(
|
|
68
|
+
const isInPiP = useIsInPiPMode();
|
|
73
69
|
|
|
74
70
|
const participantViewProps: ParticipantViewComponentProps = {
|
|
75
71
|
ParticipantLabel,
|
|
@@ -2,6 +2,7 @@ import { CallingState } from '@stream-io/video-client';
|
|
|
2
2
|
import { useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
3
3
|
import { useEffect } from 'react';
|
|
4
4
|
import { NativeModules, Platform } from 'react-native';
|
|
5
|
+
import { disablePiPMode$ } from '../utils/internal/rxSubjects';
|
|
5
6
|
|
|
6
7
|
export function useAutoEnterPiPEffect(
|
|
7
8
|
disablePictureInPicture: boolean | undefined
|
|
@@ -10,7 +11,7 @@ export function useAutoEnterPiPEffect(
|
|
|
10
11
|
|
|
11
12
|
const callingState = useCallCallingState();
|
|
12
13
|
|
|
13
|
-
// if we need to enable, only enable in joined state
|
|
14
|
+
// if we need to enable autoEnter, only enable in joined state
|
|
14
15
|
useEffect(() => {
|
|
15
16
|
if (Platform.OS !== 'android') {
|
|
16
17
|
return;
|
|
@@ -23,12 +24,14 @@ export function useAutoEnterPiPEffect(
|
|
|
23
24
|
}
|
|
24
25
|
}, [callingState, disablePictureInPicture]);
|
|
25
26
|
|
|
26
|
-
// on unmount always disable PiP mode
|
|
27
27
|
useEffect(() => {
|
|
28
|
+
disablePiPMode$.next(disablePictureInPicture === true);
|
|
29
|
+
|
|
28
30
|
if (Platform.OS !== 'android') {
|
|
29
31
|
return;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
// on unmount always disable PiP mode auto enter
|
|
32
35
|
return () => {
|
|
33
36
|
NativeModules.StreamVideoReactNative.canAutoEnterPipMode(false);
|
|
34
37
|
};
|
|
@@ -1,60 +1,24 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
NativeEventEmitter,
|
|
5
|
-
NativeModules,
|
|
6
|
-
Platform,
|
|
7
|
-
} from 'react-native';
|
|
2
|
+
import { RxUtils } from '@stream-io/video-client';
|
|
3
|
+
import { isInPiPModeAndroid$ } from '../utils/internal/rxSubjects';
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export function useIsInPiPMode(disablePictureInPicture: boolean | undefined) {
|
|
14
|
-
const [isInPiPMode, setIsInPiPMode] = useState(
|
|
15
|
-
disablePictureInPicture &&
|
|
16
|
-
isAndroid8OrAbove &&
|
|
17
|
-
AppState.currentState === 'background'
|
|
18
|
-
);
|
|
5
|
+
export function useIsInPiPMode() {
|
|
6
|
+
const [value, setValue] = useState<boolean>(() => {
|
|
7
|
+
return RxUtils.getCurrentValue(isInPiPModeAndroid$);
|
|
8
|
+
});
|
|
19
9
|
|
|
20
10
|
useEffect(() => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const subscriptionPiPChange = eventEmitter.addListener(
|
|
30
|
-
PIP_CHANGE_EVENT,
|
|
31
|
-
setIsInPiPMode
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
const setFromNativeMethod = async () => {
|
|
35
|
-
const isInPiPNativeMethod: boolean | null | undefined =
|
|
36
|
-
await NativeModules?.StreamVideoReactNative?.isInPiPMode();
|
|
37
|
-
setIsInPiPMode(!!isInPiPNativeMethod);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const subscriptionAppState = AppState.addEventListener(
|
|
41
|
-
'change',
|
|
42
|
-
(nextAppState) => {
|
|
43
|
-
if (nextAppState === 'background') {
|
|
44
|
-
setIsInPiPMode(!disablePictureInPicture); // set with an assumption that its enabled so that UI disabling happens faster
|
|
45
|
-
// if PiP was not enabled anyway, then in the next code we ll set it to false and UI wont be shown anyway
|
|
46
|
-
}
|
|
47
|
-
setFromNativeMethod();
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
setFromNativeMethod();
|
|
52
|
-
|
|
11
|
+
const subscription = isInPiPModeAndroid$.subscribe({
|
|
12
|
+
next: setValue,
|
|
13
|
+
error: (err) => {
|
|
14
|
+
console.log('An error occurred while reading isInPiPModeAndroid$', err);
|
|
15
|
+
setValue(false);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
53
18
|
return () => {
|
|
54
|
-
|
|
55
|
-
subscriptionAppState.remove();
|
|
19
|
+
subscription.unsubscribe();
|
|
56
20
|
};
|
|
57
|
-
}, [
|
|
21
|
+
}, []);
|
|
58
22
|
|
|
59
|
-
return
|
|
23
|
+
return value;
|
|
60
24
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useCall } from '@stream-io/video-react-bindings';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
AppState,
|
|
5
|
+
NativeEventEmitter,
|
|
6
|
+
NativeModules,
|
|
7
|
+
Platform,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import { shouldDisableIOSLocalVideoOnBackgroundRef } from '../../utils/internal/shouldDisableIOSLocalVideoOnBackground';
|
|
10
|
+
import {
|
|
11
|
+
disablePiPMode$,
|
|
12
|
+
isInPiPModeAndroid$,
|
|
13
|
+
} from '../../utils/internal/rxSubjects';
|
|
14
|
+
import { RxUtils } from '@stream-io/video-client';
|
|
15
|
+
|
|
16
|
+
const PIP_CHANGE_EVENT = 'StreamVideoReactNative_PIP_CHANGE_EVENT';
|
|
17
|
+
|
|
18
|
+
const isAndroid8OrAbove = Platform.OS === 'android' && Platform.Version >= 26;
|
|
19
|
+
|
|
20
|
+
// Does 2 functionalities:
|
|
21
|
+
// 1. Resume/Disable video stream tracks when app goes to background/foreground - To save on CPU resources
|
|
22
|
+
// 2. Handle PiP mode in Android
|
|
23
|
+
export const AppStateListener = () => {
|
|
24
|
+
const call = useCall();
|
|
25
|
+
const appState = useRef(AppState.currentState);
|
|
26
|
+
|
|
27
|
+
// on mount: set initial PiP mode and listen to PiP events
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!isAndroid8OrAbove) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const disablePiP = RxUtils.getCurrentValue(disablePiPMode$);
|
|
34
|
+
isInPiPModeAndroid$.next(
|
|
35
|
+
!disablePiP && AppState.currentState === 'background'
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const eventEmitter = new NativeEventEmitter(
|
|
39
|
+
NativeModules.StreamVideoReactNative
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const subscriptionPiPChange = eventEmitter.addListener(
|
|
43
|
+
PIP_CHANGE_EVENT,
|
|
44
|
+
(isInPiPMode: boolean) => {
|
|
45
|
+
isInPiPModeAndroid$.next(isInPiPMode);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
subscriptionPiPChange.remove();
|
|
51
|
+
};
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
// due to strange behavior in iOS when app goes to "inactive" state
|
|
56
|
+
// we dont check for inactive states
|
|
57
|
+
// ref: https://www.reddit.com/r/reactnative/comments/15kib42/appstate_behavior_in_ios_when_swiping_down_to/
|
|
58
|
+
const subscription = AppState.addEventListener('change', (nextAppState) => {
|
|
59
|
+
if (appState.current.match(/background/) && nextAppState === 'active') {
|
|
60
|
+
if (
|
|
61
|
+
call?.camera?.state.status === 'enabled' &&
|
|
62
|
+
Platform.OS === 'android'
|
|
63
|
+
) {
|
|
64
|
+
// when device is locked and resumed, the status isnt made disabled but stays enabled
|
|
65
|
+
// as a workaround we stop the track and enable again if its already in enabled state
|
|
66
|
+
call?.camera?.disable(true).then(() => {
|
|
67
|
+
call?.camera?.enable();
|
|
68
|
+
});
|
|
69
|
+
} else {
|
|
70
|
+
call?.camera?.resume();
|
|
71
|
+
}
|
|
72
|
+
appState.current = nextAppState;
|
|
73
|
+
} else if (
|
|
74
|
+
appState.current === 'active' &&
|
|
75
|
+
nextAppState.match(/background/)
|
|
76
|
+
) {
|
|
77
|
+
if (Platform.OS === 'android') {
|
|
78
|
+
// in Android, we need to check if we are in PiP mode
|
|
79
|
+
// in PiP mode, we don't want to disable the camera
|
|
80
|
+
const disableCameraIfNeeded = () => {
|
|
81
|
+
if (call?.camera?.state.status === 'enabled') {
|
|
82
|
+
call?.camera?.disable();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
if (isAndroid8OrAbove) {
|
|
86
|
+
// set with an assumption that its enabled so that UI disabling happens faster
|
|
87
|
+
const disablePiP = RxUtils.getCurrentValue(disablePiPMode$);
|
|
88
|
+
isInPiPModeAndroid$.next(!disablePiP);
|
|
89
|
+
// if PiP was not enabled anyway, then in the next code we ll set it to false and UI wont be shown anyway
|
|
90
|
+
NativeModules?.StreamVideoReactNative?.isInPiPMode().then(
|
|
91
|
+
(isInPiP: boolean | null | undefined) => {
|
|
92
|
+
isInPiPModeAndroid$.next(!!isInPiP);
|
|
93
|
+
if (!isInPiP) {
|
|
94
|
+
if (AppState.currentState === 'active') {
|
|
95
|
+
// this is to handle the case that the app became active as soon as it went to background
|
|
96
|
+
// in this case, we dont want to disable the camera
|
|
97
|
+
// this happens on foreground push notifications
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
disableCameraIfNeeded();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
} else {
|
|
105
|
+
disableCameraIfNeeded();
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
// shouldDisableIOSLocalVideoOnBackgroundRef is false, if local video is enabled on PiP
|
|
109
|
+
if (shouldDisableIOSLocalVideoOnBackgroundRef.current) {
|
|
110
|
+
if (call?.camera?.state.status === 'enabled') {
|
|
111
|
+
call?.camera?.disable();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
appState.current = nextAppState;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return () => {
|
|
120
|
+
subscription.remove();
|
|
121
|
+
};
|
|
122
|
+
}, [call]);
|
|
123
|
+
|
|
124
|
+
return null;
|
|
125
|
+
};
|