@stream-io/video-react-native-sdk 1.13.2 → 1.14.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/android/gradle.properties +1 -1
  3. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.js +23 -29
  4. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.js.map +1 -1
  5. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +187 -29
  6. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
  7. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js +1 -1
  8. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
  9. package/dist/commonjs/components/Livestream/LivestreamPlayer/LivestreamEnded.js +111 -0
  10. package/dist/commonjs/components/Livestream/LivestreamPlayer/LivestreamEnded.js.map +1 -0
  11. package/dist/commonjs/components/Livestream/LivestreamPlayer/LivestreamPlayer.js +5 -6
  12. package/dist/commonjs/components/Livestream/LivestreamPlayer/LivestreamPlayer.js.map +1 -1
  13. package/dist/commonjs/components/Livestream/LivestreamTopView/DurationBadge.js +32 -28
  14. package/dist/commonjs/components/Livestream/LivestreamTopView/DurationBadge.js.map +1 -1
  15. package/dist/commonjs/components/Livestream/LivestreamTopView/FollowerCount.js +36 -36
  16. package/dist/commonjs/components/Livestream/LivestreamTopView/FollowerCount.js.map +1 -1
  17. package/dist/commonjs/components/Livestream/LivestreamTopView/LiveIndicator.js +21 -15
  18. package/dist/commonjs/components/Livestream/LivestreamTopView/LiveIndicator.js.map +1 -1
  19. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js +70 -4
  20. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
  21. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLobby.js +143 -0
  22. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLobby.js.map +1 -0
  23. package/dist/commonjs/icons/LivestreamControls.js +73 -0
  24. package/dist/commonjs/icons/LivestreamControls.js.map +1 -0
  25. package/dist/commonjs/icons/Maximize.js +52 -0
  26. package/dist/commonjs/icons/Maximize.js.map +1 -0
  27. package/dist/commonjs/icons/index.js +11 -0
  28. package/dist/commonjs/icons/index.js.map +1 -1
  29. package/dist/commonjs/index.js +12 -0
  30. package/dist/commonjs/index.js.map +1 -1
  31. package/dist/commonjs/providers/NoiseCancellation/NoiseCancellationProvider.js +75 -0
  32. package/dist/commonjs/providers/NoiseCancellation/NoiseCancellationProvider.js.map +1 -0
  33. package/dist/commonjs/providers/NoiseCancellation/index.js +17 -0
  34. package/dist/commonjs/providers/NoiseCancellation/index.js.map +1 -0
  35. package/dist/commonjs/providers/NoiseCancellation/lib.js +34 -0
  36. package/dist/commonjs/providers/NoiseCancellation/lib.js.map +1 -0
  37. package/dist/commonjs/version.js +1 -1
  38. package/dist/module/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.js +27 -33
  39. package/dist/module/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.js.map +1 -1
  40. package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +187 -30
  41. package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
  42. package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js +1 -1
  43. package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
  44. package/dist/module/components/Livestream/LivestreamPlayer/LivestreamEnded.js +104 -0
  45. package/dist/module/components/Livestream/LivestreamPlayer/LivestreamEnded.js.map +1 -0
  46. package/dist/module/components/Livestream/LivestreamPlayer/LivestreamPlayer.js +5 -6
  47. package/dist/module/components/Livestream/LivestreamPlayer/LivestreamPlayer.js.map +1 -1
  48. package/dist/module/components/Livestream/LivestreamTopView/DurationBadge.js +33 -29
  49. package/dist/module/components/Livestream/LivestreamTopView/DurationBadge.js.map +1 -1
  50. package/dist/module/components/Livestream/LivestreamTopView/FollowerCount.js +35 -35
  51. package/dist/module/components/Livestream/LivestreamTopView/FollowerCount.js.map +1 -1
  52. package/dist/module/components/Livestream/LivestreamTopView/LiveIndicator.js +20 -14
  53. package/dist/module/components/Livestream/LivestreamTopView/LiveIndicator.js.map +1 -1
  54. package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js +73 -7
  55. package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
  56. package/dist/module/components/Livestream/ViewerLivestream/ViewerLobby.js +136 -0
  57. package/dist/module/components/Livestream/ViewerLivestream/ViewerLobby.js.map +1 -0
  58. package/dist/module/icons/LivestreamControls.js +62 -0
  59. package/dist/module/icons/LivestreamControls.js.map +1 -0
  60. package/dist/module/icons/Maximize.js +43 -0
  61. package/dist/module/icons/Maximize.js.map +1 -0
  62. package/dist/module/icons/index.js +1 -0
  63. package/dist/module/icons/index.js.map +1 -1
  64. package/dist/module/index.js +1 -0
  65. package/dist/module/index.js.map +1 -1
  66. package/dist/module/providers/NoiseCancellation/NoiseCancellationProvider.js +67 -0
  67. package/dist/module/providers/NoiseCancellation/NoiseCancellationProvider.js.map +1 -0
  68. package/dist/module/providers/NoiseCancellation/index.js +2 -0
  69. package/dist/module/providers/NoiseCancellation/index.js.map +1 -0
  70. package/dist/module/providers/NoiseCancellation/lib.js +26 -0
  71. package/dist/module/providers/NoiseCancellation/lib.js.map +1 -0
  72. package/dist/module/version.js +1 -1
  73. package/dist/typescript/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.d.ts.map +1 -1
  74. package/dist/typescript/components/Livestream/LivestreamControls/ViewerLivestreamControls.d.ts +7 -0
  75. package/dist/typescript/components/Livestream/LivestreamControls/ViewerLivestreamControls.d.ts.map +1 -1
  76. package/dist/typescript/components/Livestream/LivestreamPlayer/LivestreamEnded.d.ts +3 -0
  77. package/dist/typescript/components/Livestream/LivestreamPlayer/LivestreamEnded.d.ts.map +1 -0
  78. package/dist/typescript/components/Livestream/LivestreamPlayer/LivestreamPlayer.d.ts +13 -1
  79. package/dist/typescript/components/Livestream/LivestreamPlayer/LivestreamPlayer.d.ts.map +1 -1
  80. package/dist/typescript/components/Livestream/LivestreamTopView/DurationBadge.d.ts.map +1 -1
  81. package/dist/typescript/components/Livestream/LivestreamTopView/FollowerCount.d.ts.map +1 -1
  82. package/dist/typescript/components/Livestream/LivestreamTopView/LiveIndicator.d.ts.map +1 -1
  83. package/dist/typescript/components/Livestream/ViewerLivestream/ViewerLivestream.d.ts +9 -1
  84. package/dist/typescript/components/Livestream/ViewerLivestream/ViewerLivestream.d.ts.map +1 -1
  85. package/dist/typescript/components/Livestream/ViewerLivestream/ViewerLobby.d.ts +8 -0
  86. package/dist/typescript/components/Livestream/ViewerLivestream/ViewerLobby.d.ts.map +1 -0
  87. package/dist/typescript/icons/LivestreamControls.d.ts +12 -0
  88. package/dist/typescript/icons/LivestreamControls.d.ts.map +1 -0
  89. package/dist/typescript/icons/Maximize.d.ts +10 -0
  90. package/dist/typescript/icons/Maximize.d.ts.map +1 -0
  91. package/dist/typescript/icons/index.d.ts +1 -0
  92. package/dist/typescript/icons/index.d.ts.map +1 -1
  93. package/dist/typescript/index.d.ts +1 -0
  94. package/dist/typescript/index.d.ts.map +1 -1
  95. package/dist/typescript/providers/NoiseCancellation/NoiseCancellationProvider.d.ts +34 -0
  96. package/dist/typescript/providers/NoiseCancellation/NoiseCancellationProvider.d.ts.map +1 -0
  97. package/dist/typescript/providers/NoiseCancellation/index.d.ts +2 -0
  98. package/dist/typescript/providers/NoiseCancellation/index.d.ts.map +1 -0
  99. package/dist/typescript/providers/NoiseCancellation/lib.d.ts +8 -0
  100. package/dist/typescript/providers/NoiseCancellation/lib.d.ts.map +1 -0
  101. package/dist/typescript/version.d.ts +1 -1
  102. package/expo-config-plugin/dist/index.js +2 -0
  103. package/expo-config-plugin/dist/withAndroidPermissions.js +1 -0
  104. package/expo-config-plugin/dist/withAppDelegate.js +26 -7
  105. package/expo-config-plugin/dist/withMainApplication.js +24 -0
  106. package/package.json +10 -5
  107. package/src/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.tsx +30 -48
  108. package/src/components/Livestream/LivestreamControls/ViewerLivestreamControls.tsx +260 -43
  109. package/src/components/Livestream/LivestreamLayout/LivestreamLayout.tsx +1 -1
  110. package/src/components/Livestream/LivestreamPlayer/LivestreamEnded.tsx +130 -0
  111. package/src/components/Livestream/LivestreamPlayer/LivestreamPlayer.tsx +15 -5
  112. package/src/components/Livestream/LivestreamTopView/DurationBadge.tsx +35 -38
  113. package/src/components/Livestream/LivestreamTopView/FollowerCount.tsx +40 -47
  114. package/src/components/Livestream/LivestreamTopView/LiveIndicator.tsx +22 -14
  115. package/src/components/Livestream/ViewerLivestream/ViewerLivestream.tsx +107 -10
  116. package/src/components/Livestream/ViewerLivestream/ViewerLobby.tsx +171 -0
  117. package/src/icons/LivestreamControls.tsx +51 -0
  118. package/src/icons/Maximize.tsx +48 -0
  119. package/src/icons/index.tsx +1 -0
  120. package/src/index.ts +1 -0
  121. package/src/providers/NoiseCancellation/NoiseCancellationProvider.tsx +147 -0
  122. package/src/providers/NoiseCancellation/index.ts +1 -0
  123. package/src/providers/NoiseCancellation/lib.ts +37 -0
  124. package/src/version.ts +1 -1
@@ -10,13 +10,17 @@ const addToSwiftBridgingHeaderFile_1 = require("./common/addToSwiftBridgingHeade
10
10
  const withAppDelegate = (configuration, props) => {
11
11
  return (0, config_plugins_1.withAppDelegate)(configuration, (config) => {
12
12
  if (!props?.ringingPushNotifications &&
13
- !props?.iOSEnableMultitaskingCameraAccess) {
13
+ !props?.iOSEnableMultitaskingCameraAccess &&
14
+ !props?.addNoiseCancellation) {
14
15
  // quit early if no change is necessary
15
16
  return config;
16
17
  }
17
18
  if (['objc', 'objcpp'].includes(config.modResults.language)) {
18
19
  try {
19
- config.modResults.contents = addDidFinishLaunchingWithOptionsObjc(config.modResults.contents, props.iOSEnableMultitaskingCameraAccess);
20
+ if (props?.addNoiseCancellation) {
21
+ config.modResults.contents = (0, codeMod_1.addObjcImports)(config.modResults.contents, ['"NoiseCancellationManagerObjc.h"']);
22
+ }
23
+ config.modResults.contents = addDidFinishLaunchingWithOptionsObjc(config.modResults.contents, props.iOSEnableMultitaskingCameraAccess, props.addNoiseCancellation);
20
24
  if (props?.ringingPushNotifications) {
21
25
  config.modResults.contents = (0, codeMod_1.addObjcImports)(config.modResults.contents, [
22
26
  '"RNCallKeep.h"',
@@ -68,7 +72,10 @@ const withAppDelegate = (configuration, props) => {
68
72
  ]);
69
73
  return headerFileContents;
70
74
  });
71
- config.modResults.contents = addDidFinishLaunchingWithOptionsSwift(config.modResults.contents, props.iOSEnableMultitaskingCameraAccess);
75
+ if (props?.addNoiseCancellation) {
76
+ config.modResults.contents = (0, codeMod_1.addObjcImports)(config.modResults.contents, ['stream_io_noise_cancellation_react_native']);
77
+ }
78
+ config.modResults.contents = addDidFinishLaunchingWithOptionsSwift(config.modResults.contents, props.iOSEnableMultitaskingCameraAccess, props.addNoiseCancellation);
72
79
  if (props?.ringingPushNotifications) {
73
80
  config.modResults.contents = (0, codeMod_1.addSwiftImports)(config.modResults.contents, ['RNCallKeep', 'PushKit', 'RNVoipPushNotification']);
74
81
  config.modResults.contents =
@@ -85,20 +92,26 @@ const withAppDelegate = (configuration, props) => {
85
92
  }
86
93
  });
87
94
  };
88
- function addDidFinishLaunchingWithOptionsSwift(contents, iOSEnableMultitaskingCameraAccess) {
95
+ function addDidFinishLaunchingWithOptionsSwift(contents, iOSEnableMultitaskingCameraAccess, enableNoiseCancellation) {
96
+ const functionSelector = 'application(_:didFinishLaunchingWithOptions:)';
89
97
  if (iOSEnableMultitaskingCameraAccess) {
90
- const functionSelector = 'application(_:didFinishLaunchingWithOptions:)';
91
98
  const setupMethod = `let options = WebRTCModuleOptions.sharedInstance()
92
99
  options.enableMultitaskingCameraAccess = true`;
93
100
  if (!contents.includes('options.enableMultitaskingCameraAccess = true')) {
94
101
  contents = (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, setupMethod, { position: 'head' });
95
102
  }
96
103
  }
104
+ if (enableNoiseCancellation) {
105
+ const setupMethod = `NoiseCancellationManager.getInstance().registerProcessor()`;
106
+ if (!contents.includes(setupMethod)) {
107
+ contents = (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, setupMethod, { position: 'head' });
108
+ }
109
+ }
97
110
  return contents;
98
111
  }
99
- function addDidFinishLaunchingWithOptionsObjc(contents, iOSEnableMultitaskingCameraAccess) {
112
+ function addDidFinishLaunchingWithOptionsObjc(contents, iOSEnableMultitaskingCameraAccess, enableNoiseCancellation) {
113
+ const functionSelector = 'application:didFinishLaunchingWithOptions:';
100
114
  if (iOSEnableMultitaskingCameraAccess) {
101
- const functionSelector = 'application:didFinishLaunchingWithOptions:';
102
115
  contents = (0, codeMod_1.addObjcImports)(contents, ['<WebRTCModuleOptions.h>']);
103
116
  const setupMethod = `WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance];
104
117
  options.enableMultitaskingCameraAccess = YES;`;
@@ -106,6 +119,12 @@ function addDidFinishLaunchingWithOptionsObjc(contents, iOSEnableMultitaskingCam
106
119
  contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, setupMethod, { position: 'head' });
107
120
  }
108
121
  }
122
+ if (enableNoiseCancellation) {
123
+ const setupMethod = `[[NoiseCancellationManagerObjc sharedInstance] registerProcessor];`;
124
+ if (!contents.includes(setupMethod)) {
125
+ contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, setupMethod, { position: 'head' });
126
+ }
127
+ }
109
128
  return contents;
110
129
  }
111
130
  function addDidFinishLaunchingWithOptionsRingingSwift(contents, ringingPushNotifications) {
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config_plugins_1 = require("@expo/config-plugins");
4
+ const codeMod_1 = require("@expo/config-plugins/build/android/codeMod");
5
+ const withStreamVideoReactNativeSDKMainApplication = (configuration, props) => {
6
+ return (0, config_plugins_1.withMainApplication)(configuration, (config) => {
7
+ const isMainActivityJava = config.modResults.language === 'java';
8
+ if (props?.addNoiseCancellation) {
9
+ config.modResults.contents = (0, codeMod_1.addImports)(config.modResults.contents, ['io.getstream.rn.noisecancellation.NoiseCancellationReactNative'], isMainActivityJava);
10
+ config.modResults.contents = addNoiseCancellationInsideOnCreate(config.modResults.contents, isMainActivityJava);
11
+ }
12
+ return config;
13
+ });
14
+ };
15
+ function addNoiseCancellationInsideOnCreate(contents, isJava) {
16
+ const addBlock = isJava
17
+ ? `NoiseCancellationReactNative.registerProcessor(getApplicationContext());`
18
+ : `NoiseCancellationReactNative.registerProcessor(applicationContext)`;
19
+ if (!contents.includes(addBlock)) {
20
+ contents = (0, codeMod_1.appendContentsInsideDeclarationBlock)(contents, 'onCreate', addBlock + '\n');
21
+ }
22
+ return contents;
23
+ }
24
+ exports.default = withStreamVideoReactNativeSDKMainApplication;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-native-sdk",
3
- "version": "1.13.2",
3
+ "version": "1.14.0",
4
4
  "description": "Stream Video SDK for React Native",
5
5
  "author": "https://getstream.io",
6
6
  "homepage": "https://getstream.io/video/docs/react-native/",
@@ -45,8 +45,8 @@
45
45
  "!**/.*"
46
46
  ],
47
47
  "dependencies": {
48
- "@stream-io/video-client": "1.22.1",
49
- "@stream-io/video-react-bindings": "1.6.1",
48
+ "@stream-io/video-client": "1.23.0",
49
+ "@stream-io/video-react-bindings": "1.6.3",
50
50
  "intl-pluralrules": "2.0.1",
51
51
  "lodash.merge": "^4.6.2",
52
52
  "react-native-url-polyfill": "1.3.0",
@@ -59,6 +59,7 @@
59
59
  "@react-native-community/push-notification-ios": ">=1.11.0",
60
60
  "@react-native-firebase/app": ">=17.5.0",
61
61
  "@react-native-firebase/messaging": ">=17.5.0",
62
+ "@stream-io/noise-cancellation-react-native": ">=0.1.0",
62
63
  "@stream-io/react-native-webrtc": ">=125.2.1",
63
64
  "@stream-io/video-filters-react-native": ">=0.1.0",
64
65
  "expo": ">=47.0.0",
@@ -86,6 +87,9 @@
86
87
  "@react-native-firebase/messaging": {
87
88
  "optional": true
88
89
  },
90
+ "@stream-io/noise-cancellation-react-native": {
91
+ "optional": true
92
+ },
89
93
  "@stream-io/video-filters-react-native": {
90
94
  "optional": true
91
95
  },
@@ -121,8 +125,9 @@
121
125
  "@react-native-firebase/app": "^22.1.0",
122
126
  "@react-native-firebase/messaging": "^22.1.0",
123
127
  "@react-native/babel-preset": "^0.79.2",
124
- "@stream-io/react-native-webrtc": "^125.2.1",
125
- "@stream-io/video-filters-react-native": "^0.3.0",
128
+ "@stream-io/noise-cancellation-react-native": "^0.1.0",
129
+ "@stream-io/react-native-webrtc": "^125.3.0",
130
+ "@stream-io/video-filters-react-native": "^0.4.0",
126
131
  "@testing-library/jest-native": "^5.4.3",
127
132
  "@testing-library/react-native": "13.2.0",
128
133
  "@tsconfig/node14": "14.1.3",
@@ -1,14 +1,8 @@
1
- import React, { useState } from 'react';
2
- import {
3
- ActivityIndicator,
4
- Pressable,
5
- StyleSheet,
6
- Text,
7
- View,
8
- } from 'react-native';
1
+ import React, { useMemo, useState } from 'react';
2
+ import { ActivityIndicator, Pressable, StyleSheet, View } from 'react-native';
9
3
  import { useTheme } from '../../../contexts';
10
- import { LeaveStreamIcon } from '../../../icons';
11
- import { useCall, useI18n } from '@stream-io/video-react-bindings';
4
+ import { PhoneDown } from '../../../icons';
5
+ import { useCall } from '@stream-io/video-react-bindings';
12
6
  import { getLogger } from '@stream-io/video-client';
13
7
 
14
8
  /**
@@ -30,12 +24,11 @@ export const ViewerLeaveStreamButton = ({
30
24
  }: ViewerLeaveStreamButtonProps) => {
31
25
  const [isAwaitingResponse, setIsAwaitingResponse] = useState(false);
32
26
  const call = useCall();
33
- const { t } = useI18n();
27
+ const styles = useStyles();
34
28
  const {
35
29
  theme: {
36
30
  colors,
37
31
  variants: { iconSizes },
38
- typefaces,
39
32
  viewerLeaveStreamButton,
40
33
  },
41
34
  } = useTheme();
@@ -57,46 +50,35 @@ export const ViewerLeaveStreamButton = ({
57
50
 
58
51
  return (
59
52
  <Pressable
60
- style={[
61
- styles.container,
62
- { backgroundColor: colors.buttonSecondary },
63
- viewerLeaveStreamButton.container,
64
- ]}
53
+ style={viewerLeaveStreamButton.container}
65
54
  onPress={onLeaveStreamButtonPress}
66
55
  >
67
- <View
68
- style={[
69
- styles.icon,
70
- { height: iconSizes.xs, width: iconSizes.xs },
71
- viewerLeaveStreamButton.icon,
72
- ]}
73
- >
74
- {isAwaitingResponse ? <ActivityIndicator /> : <LeaveStreamIcon />}
56
+ <View style={[styles.icon, viewerLeaveStreamButton.icon]}>
57
+ {isAwaitingResponse ? (
58
+ <ActivityIndicator />
59
+ ) : (
60
+ <PhoneDown color={colors.iconPrimary} size={iconSizes.sm} />
61
+ )}
75
62
  </View>
76
- <Text
77
- style={[
78
- styles.text,
79
- typefaces.subtitleBold,
80
- { color: colors.textPrimary },
81
- viewerLeaveStreamButton.text,
82
- ]}
83
- >
84
- {isAwaitingResponse ? t('Loading...') : t('Leave Stream')}
85
- </Text>
86
63
  </Pressable>
87
64
  );
88
65
  };
89
66
 
90
- const styles = StyleSheet.create({
91
- container: {
92
- flexDirection: 'row',
93
- alignItems: 'center',
94
- padding: 8,
95
- borderRadius: 4,
96
- },
97
- icon: {},
98
- text: {
99
- marginLeft: 8,
100
- includeFontPadding: false,
101
- },
102
- });
67
+ const useStyles = () => {
68
+ const { theme } = useTheme();
69
+ return useMemo(
70
+ () =>
71
+ StyleSheet.create({
72
+ icon: {
73
+ backgroundColor: theme.colors.buttonSecondary,
74
+ height: theme.variants.buttonSizes.xs,
75
+ width: theme.variants.buttonSizes.xs,
76
+ justifyContent: 'center',
77
+ alignItems: 'center',
78
+ borderRadius: theme.variants.borderRadiusSizes.sm,
79
+ zIndex: 2,
80
+ },
81
+ }),
82
+ [theme],
83
+ );
84
+ };
@@ -1,12 +1,30 @@
1
- import React from 'react';
2
- import { StyleSheet, View, type ViewProps } from 'react-native';
3
-
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import { Pressable, StyleSheet, View, type ViewProps } from 'react-native';
4
9
  import {
5
10
  ViewerLeaveStreamButton as DefaultViewerLeaveStreamButton,
6
11
  type ViewerLeaveStreamButtonProps,
7
12
  } from './ViewerLeaveStreamButton';
8
13
  import { useTheme } from '../../../contexts';
9
14
  import { Z_INDEX } from '../../../constants';
15
+ import {
16
+ DurationBadge,
17
+ FollowerCount,
18
+ LiveIndicator,
19
+ } from '../LivestreamTopView';
20
+ import { IconWrapper, Maximize } from '../../../icons';
21
+ import InCallManager from 'react-native-incall-manager';
22
+ import {
23
+ VolumeOff,
24
+ VolumeOn,
25
+ PauseIcon,
26
+ PlayIcon,
27
+ } from '../../../icons/LivestreamControls';
10
28
 
11
29
  /**
12
30
  * Props for the ViewerLivestreamControls component.
@@ -16,6 +34,15 @@ export type ViewerLivestreamControlsProps = ViewerLeaveStreamButtonProps & {
16
34
  * Component to customize the leave stream button on the viewer's end live stream.
17
35
  */
18
36
  ViewerLeaveStreamButton?: React.ComponentType<ViewerLeaveStreamButtonProps> | null;
37
+
38
+ /**
39
+ * Handler to be called when the leave stream button is pressed.
40
+ */
41
+ onLeaveStreamHandler?: () => void;
42
+
43
+ /**
44
+ * Handler to be called when the layout of the component changes.
45
+ */
19
46
  onLayout?: ViewProps['onLayout'];
20
47
  };
21
48
 
@@ -27,50 +54,240 @@ export const ViewerLivestreamControls = ({
27
54
  onLeaveStreamHandler,
28
55
  onLayout,
29
56
  }: ViewerLivestreamControlsProps) => {
57
+ const styles = useStyles();
30
58
  const {
31
- theme: { colors, viewerLivestreamControls },
59
+ theme: { colors, viewerLivestreamControls, variants },
32
60
  } = useTheme();
33
61
 
34
- return (
35
- <View
36
- style={[
37
- styles.container,
38
- { backgroundColor: colors.sheetOverlay },
39
- viewerLivestreamControls.container,
40
- ]}
41
- onLayout={onLayout}
42
- >
43
- <View style={[styles.leftElement, viewerLivestreamControls.leftElement]}>
44
- {ViewerLeaveStreamButton && (
45
- <ViewerLeaveStreamButton
46
- onLeaveStreamHandler={onLeaveStreamHandler}
47
- />
48
- )}
62
+ const [showControls, setShowControls] = useState(true);
63
+ const [isMuted, setIsMuted] = useState(false);
64
+ const [isPlaying, setIsPlaying] = useState(true);
65
+ const [showPlayPauseButton, setShowPlayPauseButton] = useState(true);
66
+ const playPauseTimeout = useRef<NodeJS.Timeout | null>(null);
67
+
68
+ const hidePlayPauseButtonAfterDelay = useCallback(() => {
69
+ if (playPauseTimeout.current) {
70
+ clearTimeout(playPauseTimeout.current);
71
+ }
72
+
73
+ playPauseTimeout.current = setTimeout(() => {
74
+ setShowPlayPauseButton(false);
75
+ playPauseTimeout.current = null;
76
+ }, 3000);
77
+ }, []);
78
+
79
+ useEffect(() => {
80
+ hidePlayPauseButtonAfterDelay();
81
+ return () => {
82
+ if (playPauseTimeout.current) {
83
+ clearTimeout(playPauseTimeout.current);
84
+ }
85
+ };
86
+ }, [hidePlayPauseButtonAfterDelay]);
87
+
88
+ const showPlayPauseButtonWithTimeout = () => {
89
+ setShowPlayPauseButton(true);
90
+ hidePlayPauseButtonAfterDelay();
91
+ };
92
+
93
+ const showControlsHandler = () => {
94
+ showPlayPauseButtonWithTimeout();
95
+ if (showControls) {
96
+ return;
97
+ }
98
+
99
+ setShowControls(true);
100
+ };
101
+
102
+ const toggleControls = () => {
103
+ setShowControls(!showControls);
104
+ };
105
+
106
+ const toggleAudio = () => {
107
+ setIsMuted(!isMuted);
108
+ InCallManager.setForceSpeakerphoneOn(isMuted);
109
+ };
110
+
111
+ const togglePlayPause = () => {
112
+ setIsPlaying(!isPlaying);
113
+ showPlayPauseButtonWithTimeout();
114
+ };
115
+
116
+ const VolumeButton = (
117
+ <Pressable onPress={toggleAudio} style={[styles.fullscreenButton]}>
118
+ <View style={[styles.icon]}>
119
+ <IconWrapper>
120
+ {isMuted ? (
121
+ <VolumeOff
122
+ color={colors.iconPrimary}
123
+ size={variants.iconSizes.sm}
124
+ />
125
+ ) : (
126
+ <VolumeOn color={colors.iconPrimary} size={variants.iconSizes.sm} />
127
+ )}
128
+ </IconWrapper>
129
+ </View>
130
+ </Pressable>
131
+ );
132
+
133
+ const MaximizeButton = (
134
+ <Pressable onPress={toggleControls} style={[styles.fullscreenButton]}>
135
+ <View style={[styles.icon]}>
136
+ <Maximize
137
+ color={colors.iconPrimary}
138
+ width={variants.iconSizes.sm}
139
+ height={variants.iconSizes.sm}
140
+ />
49
141
  </View>
50
- <View
51
- style={[styles.rightElement, viewerLivestreamControls.rightElement]}
52
- />
53
- </View>
142
+ </Pressable>
143
+ );
144
+
145
+ const PlayPauseButton = (
146
+ <Pressable onPress={togglePlayPause} style={styles.playPauseButton}>
147
+ <View style={styles.playPauseIcon}>
148
+ <IconWrapper>
149
+ {isPlaying ? (
150
+ <PauseIcon
151
+ color={colors.iconPrimary}
152
+ size={variants.iconSizes.lg * 3}
153
+ />
154
+ ) : (
155
+ <PlayIcon
156
+ color={colors.iconPrimary}
157
+ size={variants.iconSizes.lg * 3}
158
+ />
159
+ )}
160
+ </IconWrapper>
161
+ </View>
162
+ </Pressable>
163
+ );
164
+
165
+ return (
166
+ <Pressable style={StyleSheet.absoluteFill} onPress={showControlsHandler}>
167
+ {!isPlaying && <View style={styles.blackOverlay} />}
168
+
169
+ {showPlayPauseButton && (
170
+ <View style={styles.centerButtonContainer}>{PlayPauseButton}</View>
171
+ )}
172
+
173
+ {showControls && (
174
+ <View
175
+ style={[styles.container, viewerLivestreamControls.container]}
176
+ onLayout={onLayout}
177
+ >
178
+ <View
179
+ style={[styles.leftElement, viewerLivestreamControls.leftElement]}
180
+ >
181
+ <View style={[styles.leftElement]}>
182
+ <View style={[styles.liveInfo]}>
183
+ <LiveIndicator />
184
+ <FollowerCount />
185
+ </View>
186
+ </View>
187
+ </View>
188
+ <View>
189
+ <DurationBadge mode="viewer" />
190
+ </View>
191
+
192
+ <View
193
+ style={[styles.rightElement, viewerLivestreamControls.rightElement]}
194
+ >
195
+ <View style={styles.buttonContainer}>
196
+ {VolumeButton}
197
+ {MaximizeButton}
198
+ {ViewerLeaveStreamButton && (
199
+ <ViewerLeaveStreamButton
200
+ onLeaveStreamHandler={onLeaveStreamHandler}
201
+ />
202
+ )}
203
+ </View>
204
+ </View>
205
+ </View>
206
+ )}
207
+ </Pressable>
54
208
  );
55
209
  };
56
210
 
57
- const styles = StyleSheet.create({
58
- container: {
59
- position: 'absolute',
60
- bottom: 0,
61
- flexDirection: 'row',
62
- alignItems: 'center',
63
- paddingVertical: 16,
64
- paddingHorizontal: 8,
65
- zIndex: Z_INDEX.IN_FRONT,
66
- },
67
- content: {},
68
- leftElement: {
69
- flex: 1,
70
- alignItems: 'flex-start',
71
- },
72
- rightElement: {
73
- flex: 1,
74
- alignItems: 'flex-end',
75
- },
76
- });
211
+ const useStyles = () => {
212
+ const { theme } = useTheme();
213
+ return useMemo(
214
+ () =>
215
+ StyleSheet.create({
216
+ container: {
217
+ position: 'absolute',
218
+ bottom: 0,
219
+ flexDirection: 'row',
220
+ alignItems: 'center',
221
+ justifyContent: 'center',
222
+ paddingVertical: 16,
223
+ paddingHorizontal: 8,
224
+ zIndex: Z_INDEX.IN_FRONT,
225
+ backgroundColor: theme.colors.sheetOverlay,
226
+ },
227
+ leftElement: {
228
+ flex: 1,
229
+ alignItems: 'flex-start',
230
+ justifyContent: 'center',
231
+ },
232
+ rightElement: {
233
+ flex: 1,
234
+ alignItems: 'flex-end',
235
+ },
236
+ liveInfo: {
237
+ flexDirection: 'row',
238
+ },
239
+ icon: {
240
+ height: theme.variants.iconSizes.sm,
241
+ width: theme.variants.iconSizes.sm,
242
+ },
243
+ fullscreenButton: {
244
+ backgroundColor: theme.colors.buttonSecondary,
245
+ height: theme.variants.buttonSizes.xs,
246
+ width: theme.variants.buttonSizes.xs,
247
+ justifyContent: 'center',
248
+ alignItems: 'center',
249
+ borderRadius: theme.variants.borderRadiusSizes.sm,
250
+ zIndex: 2,
251
+ },
252
+ buttonContainer: {
253
+ flexDirection: 'row',
254
+ alignItems: 'center',
255
+ justifyContent: 'center',
256
+ gap: theme.variants.spacingSizes.sm,
257
+ },
258
+ centerButtonContainer: {
259
+ position: 'absolute',
260
+ top: 0,
261
+ bottom: 0,
262
+ left: 0,
263
+ right: 0,
264
+ justifyContent: 'center',
265
+ alignItems: 'center',
266
+ zIndex: Z_INDEX.IN_FRONT,
267
+ pointerEvents: 'box-none',
268
+ },
269
+ playPauseButton: {
270
+ height: 200,
271
+ width: 200,
272
+
273
+ justifyContent: 'center',
274
+ alignItems: 'center',
275
+ zIndex: Z_INDEX.IN_FRONT + 1,
276
+ },
277
+ playPauseIcon: {
278
+ height: 200,
279
+ width: 200,
280
+ },
281
+ blackOverlay: {
282
+ position: 'absolute',
283
+ top: 0,
284
+ left: 0,
285
+ right: 0,
286
+ bottom: 0,
287
+ backgroundColor: 'black',
288
+ zIndex: Z_INDEX.IN_FRONT - 1,
289
+ },
290
+ }),
291
+ [theme],
292
+ );
293
+ };
@@ -80,7 +80,7 @@ export const LivestreamLayout = ({
80
80
  style={[
81
81
  styles.container,
82
82
  landScapeStyles,
83
- { backgroundColor: colors.sheetTertiary },
83
+ { backgroundColor: colors.sheetPrimary },
84
84
  livestreamLayout.container,
85
85
  ]}
86
86
  >