@stream-io/video-react-native-sdk 1.12.0 → 1.13.1

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 (118) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/android/build.gradle +2 -0
  3. package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +72 -0
  4. package/android/src/main/java/com/streamvideo/reactnative/util/YuvFrame.kt +97 -0
  5. package/dist/commonjs/components/Call/CallContent/CallContent.js +1 -2
  6. package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
  7. package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js +1 -2
  8. package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js.map +1 -1
  9. package/dist/commonjs/components/Call/CallControls/AcceptCallButton.js +1 -2
  10. package/dist/commonjs/components/Call/CallControls/AcceptCallButton.js.map +1 -1
  11. package/dist/commonjs/components/Call/CallControls/LobbyControls.js +1 -2
  12. package/dist/commonjs/components/Call/CallControls/LobbyControls.js.map +1 -1
  13. package/dist/commonjs/components/Call/CallControls/ReactionsButton.js +1 -2
  14. package/dist/commonjs/components/Call/CallControls/ReactionsButton.js.map +1 -1
  15. package/dist/commonjs/components/Call/CallControls/RejectCallButton.js +1 -2
  16. package/dist/commonjs/components/Call/CallControls/RejectCallButton.js.map +1 -1
  17. package/dist/commonjs/components/Call/CallControls/ScreenShareToggleButton.js +1 -2
  18. package/dist/commonjs/components/Call/CallControls/ScreenShareToggleButton.js.map +1 -1
  19. package/dist/commonjs/components/Call/CallControls/internal/ReactionsPicker.js +1 -2
  20. package/dist/commonjs/components/Call/CallControls/internal/ReactionsPicker.js.map +1 -1
  21. package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js +1 -2
  22. package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
  23. package/dist/commonjs/components/Call/CallParticipantsList/CallParticipantsList.js +1 -2
  24. package/dist/commonjs/components/Call/CallParticipantsList/CallParticipantsList.js.map +1 -1
  25. package/dist/commonjs/components/Call/Lobby/JoinCallButton.js +1 -2
  26. package/dist/commonjs/components/Call/Lobby/JoinCallButton.js.map +1 -1
  27. package/dist/commonjs/components/Call/Lobby/Lobby.js +1 -2
  28. package/dist/commonjs/components/Call/Lobby/Lobby.js.map +1 -1
  29. package/dist/commonjs/components/Call/Lobby/LobbyFooter.js +1 -2
  30. package/dist/commonjs/components/Call/Lobby/LobbyFooter.js.map +1 -1
  31. package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js +1 -2
  32. package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js.map +1 -1
  33. package/dist/commonjs/components/Livestream/LivestreamControls/HostStartStreamButton.js +1 -2
  34. package/dist/commonjs/components/Livestream/LivestreamControls/HostStartStreamButton.js.map +1 -1
  35. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.js +1 -2
  36. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLeaveStreamButton.js.map +1 -1
  37. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js +1 -2
  38. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
  39. package/dist/commonjs/components/Livestream/LivestreamPlayer/LivestreamPlayer.js +1 -2
  40. package/dist/commonjs/components/Livestream/LivestreamPlayer/LivestreamPlayer.js.map +1 -1
  41. package/dist/commonjs/components/Livestream/LivestreamTopView/DurationBadge.js +1 -2
  42. package/dist/commonjs/components/Livestream/LivestreamTopView/DurationBadge.js.map +1 -1
  43. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js +1 -2
  44. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
  45. package/dist/commonjs/components/Participant/FloatingParticipantView/FloatingView/AnimatedFloatingView.js +1 -2
  46. package/dist/commonjs/components/Participant/FloatingParticipantView/FloatingView/AnimatedFloatingView.js.map +1 -1
  47. package/dist/commonjs/components/Participant/FloatingParticipantView/FloatingView/ReanimatedFloatingView.js +1 -2
  48. package/dist/commonjs/components/Participant/FloatingParticipantView/FloatingView/ReanimatedFloatingView.js.map +1 -1
  49. package/dist/commonjs/components/Participant/ParticipantView/ParticipantLabel.js +1 -2
  50. package/dist/commonjs/components/Participant/ParticipantView/ParticipantLabel.js.map +1 -1
  51. package/dist/commonjs/components/Participant/ParticipantView/ParticipantReaction.js +1 -2
  52. package/dist/commonjs/components/Participant/ParticipantView/ParticipantReaction.js.map +1 -1
  53. package/dist/commonjs/components/Participant/ParticipantView/ParticipantView.js +1 -2
  54. package/dist/commonjs/components/Participant/ParticipantView/ParticipantView.js.map +1 -1
  55. package/dist/commonjs/components/Participant/ParticipantView/SpeechIndicator.js +1 -2
  56. package/dist/commonjs/components/Participant/ParticipantView/SpeechIndicator.js.map +1 -1
  57. package/dist/commonjs/components/Participant/ParticipantView/VideoRenderer.js +18 -4
  58. package/dist/commonjs/components/Participant/ParticipantView/VideoRenderer.js.map +1 -1
  59. package/dist/commonjs/contexts/BackgroundFilters.js +1 -2
  60. package/dist/commonjs/contexts/BackgroundFilters.js.map +1 -1
  61. package/dist/commonjs/contexts/StreamVideoContext.js +1 -2
  62. package/dist/commonjs/contexts/StreamVideoContext.js.map +1 -1
  63. package/dist/commonjs/contexts/ThemeContext.js +1 -2
  64. package/dist/commonjs/contexts/ThemeContext.js.map +1 -1
  65. package/dist/commonjs/contexts/internal/ScreenshotIosContext.js +77 -0
  66. package/dist/commonjs/contexts/internal/ScreenshotIosContext.js.map +1 -0
  67. package/dist/commonjs/hooks/index.js +11 -0
  68. package/dist/commonjs/hooks/index.js.map +1 -1
  69. package/dist/commonjs/hooks/useScreenshot.js +54 -0
  70. package/dist/commonjs/hooks/useScreenshot.js.map +1 -0
  71. package/dist/commonjs/icons/Lock.js +1 -2
  72. package/dist/commonjs/icons/Lock.js.map +1 -1
  73. package/dist/commonjs/icons/ScreenShare.js +1 -2
  74. package/dist/commonjs/icons/ScreenShare.js.map +1 -1
  75. package/dist/commonjs/icons/ScreenShareIndicator.js +1 -2
  76. package/dist/commonjs/icons/ScreenShareIndicator.js.map +1 -1
  77. package/dist/commonjs/icons/Settings.js +1 -2
  78. package/dist/commonjs/icons/Settings.js.map +1 -1
  79. package/dist/commonjs/icons/StopScreenShare.js +1 -2
  80. package/dist/commonjs/icons/StopScreenShare.js.map +1 -1
  81. package/dist/commonjs/providers/StreamCall/index.js +1 -2
  82. package/dist/commonjs/providers/StreamCall/index.js.map +1 -1
  83. package/dist/commonjs/providers/StreamVideo.js +3 -3
  84. package/dist/commonjs/providers/StreamVideo.js.map +1 -1
  85. package/dist/commonjs/version.js +1 -1
  86. package/dist/module/components/Participant/ParticipantView/VideoRenderer.js +18 -3
  87. package/dist/module/components/Participant/ParticipantView/VideoRenderer.js.map +1 -1
  88. package/dist/module/contexts/internal/ScreenshotIosContext.js +68 -0
  89. package/dist/module/contexts/internal/ScreenshotIosContext.js.map +1 -0
  90. package/dist/module/hooks/index.js +1 -0
  91. package/dist/module/hooks/index.js.map +1 -1
  92. package/dist/module/hooks/useScreenshot.js +48 -0
  93. package/dist/module/hooks/useScreenshot.js.map +1 -0
  94. package/dist/module/providers/StreamVideo.js +3 -1
  95. package/dist/module/providers/StreamVideo.js.map +1 -1
  96. package/dist/module/version.js +1 -1
  97. package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts +2 -2
  98. package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts.map +1 -1
  99. package/dist/typescript/components/Participant/ParticipantView/VideoRenderer.d.ts.map +1 -1
  100. package/dist/typescript/contexts/internal/ScreenshotIosContext.d.ts +11 -0
  101. package/dist/typescript/contexts/internal/ScreenshotIosContext.d.ts.map +1 -0
  102. package/dist/typescript/hooks/index.d.ts +1 -0
  103. package/dist/typescript/hooks/index.d.ts.map +1 -1
  104. package/dist/typescript/hooks/useScreenshot.d.ts +19 -0
  105. package/dist/typescript/hooks/useScreenshot.d.ts.map +1 -0
  106. package/dist/typescript/providers/StreamVideo.d.ts.map +1 -1
  107. package/dist/typescript/version.d.ts +1 -1
  108. package/expo-config-plugin/dist/common/{addNewLinesToAppDelegate.js → addNewLinesToAppDelegateObjc.js} +3 -3
  109. package/expo-config-plugin/dist/common/addToSwiftBridgingHeaderFile.js +48 -0
  110. package/expo-config-plugin/dist/withAppDelegate.js +216 -34
  111. package/ios/StreamVideoReactNative.m +96 -0
  112. package/package.json +23 -25
  113. package/src/components/Participant/ParticipantView/VideoRenderer.tsx +35 -3
  114. package/src/contexts/internal/ScreenshotIosContext.tsx +145 -0
  115. package/src/hooks/index.ts +1 -0
  116. package/src/hooks/useScreenshot.ts +78 -0
  117. package/src/providers/StreamVideo.tsx +5 -2
  118. package/src/version.ts +1 -1
@@ -5,12 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const config_plugins_1 = require("@expo/config-plugins");
7
7
  const codeMod_1 = require("@expo/config-plugins/build/ios/codeMod");
8
- const addNewLinesToAppDelegate_1 = __importDefault(require("./common/addNewLinesToAppDelegate"));
9
- const DID_FINISH_LAUNCHING_WITH_OPTIONS = 'application:didFinishLaunchingWithOptions:';
10
- const DID_UPDATE_PUSH_CREDENTIALS = 'pushRegistry:didUpdatePushCredentials:forType:';
11
- const DID_RECEIVE_INCOMING_PUSH = 'pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:';
12
- const DID_ACTIVATE_AUDIO_SESSION = 'provider:didActivateAudioSession:audioSession';
13
- const DID_DEACTIVATE_AUDIO_SESSION = 'provider:didDeactivateAudioSession:audioSession';
8
+ const addNewLinesToAppDelegateObjc_1 = __importDefault(require("./common/addNewLinesToAppDelegateObjc"));
9
+ const addToSwiftBridgingHeaderFile_1 = require("./common/addToSwiftBridgingHeaderFile");
14
10
  const withAppDelegate = (configuration, props) => {
15
11
  return (0, config_plugins_1.withAppDelegate)(configuration, (config) => {
16
12
  if (!props?.ringingPushNotifications &&
@@ -20,7 +16,7 @@ const withAppDelegate = (configuration, props) => {
20
16
  }
21
17
  if (['objc', 'objcpp'].includes(config.modResults.language)) {
22
18
  try {
23
- // all the imports that are needed
19
+ config.modResults.contents = addDidFinishLaunchingWithOptionsObjc(config.modResults.contents, props.iOSEnableMultitaskingCameraAccess);
24
20
  if (props?.ringingPushNotifications) {
25
21
  config.modResults.contents = (0, codeMod_1.addObjcImports)(config.modResults.contents, [
26
22
  '"RNCallKeep.h"',
@@ -29,35 +25,114 @@ const withAppDelegate = (configuration, props) => {
29
25
  '"StreamVideoReactNative.h"',
30
26
  '<WebRTC/RTCAudioSession.h>',
31
27
  ]);
32
- config.modResults.contents = addDidFinishLaunchingWithOptionsRinging(config.modResults.contents, props.ringingPushNotifications);
33
- config.modResults.contents = addDidUpdatePushCredentials(config.modResults.contents);
34
- config.modResults.contents = addDidReceiveIncomingPushCallback(config.modResults.contents);
35
- config.modResults.contents = addAudioSessionMethods(config.modResults.contents);
28
+ config.modResults.contents =
29
+ addDidFinishLaunchingWithOptionsRingingObjc(config.modResults.contents, props.ringingPushNotifications);
30
+ config.modResults.contents = addDidUpdatePushCredentialsObjc(config.modResults.contents);
31
+ config.modResults.contents = addDidReceiveIncomingPushCallbackObjc(config.modResults.contents);
32
+ config.modResults.contents = addAudioSessionMethodsObjc(config.modResults.contents);
36
33
  }
37
- config.modResults.contents = addDidFinishLaunchingWithOptions(config.modResults.contents, props.iOSEnableMultitaskingCameraAccess);
38
34
  return config;
39
35
  }
40
36
  catch (error) {
41
- throw new Error(`Cannot setup StreamVideoReactNativeSDK because the AppDelegate is malformed ${error}`);
37
+ throw new Error(`Cannot setup StreamVideoReactNativeSDK because the AppDelegate(objc) is malformed ${error}`);
42
38
  }
43
39
  }
44
40
  else {
45
- throw new Error('Cannot setup StreamVideoReactNativeSDK because the language is not supported');
41
+ try {
42
+ if (props?.ringingPushNotifications) {
43
+ // make it public class AppDelegate: ExpoAppDelegate, PKPushRegistryDelegate {
44
+ const regex = /(class\s+AppDelegate[^{]*)(\s*\{)/;
45
+ config.modResults.contents = config.modResults.contents.replace(regex, (match, declarationPart, openBrace) => {
46
+ // Check if PKPushRegistryDelegate is already in the declaration part
47
+ if (declarationPart.includes('PKPushRegistryDelegate')) {
48
+ return match; // Already present, no change needed
49
+ }
50
+ const trimmedDecl = declarationPart.trimRight();
51
+ // If the declaration already has a colon (superclass or other protocols)
52
+ if (trimmedDecl.includes(':')) {
53
+ return `${trimmedDecl}, PKPushRegistryDelegate${openBrace}`;
54
+ }
55
+ else {
56
+ // No colon, so AppDelegate is the first thing to be listed after :
57
+ // This means the class declaration was like "class AppDelegate {"
58
+ return `${trimmedDecl}: PKPushRegistryDelegate${openBrace}`;
59
+ }
60
+ });
61
+ }
62
+ config.modResults.contents = (0, codeMod_1.addSwiftImports)(config.modResults.contents, ['WebRTC']);
63
+ (0, addToSwiftBridgingHeaderFile_1.addToSwiftBridgingHeaderFile)(config.modRequest.projectRoot, (headerFileContents) => {
64
+ headerFileContents = (0, codeMod_1.addObjcImports)(headerFileContents, [
65
+ '"ProcessorProvider.h"',
66
+ '"StreamVideoReactNative.h"',
67
+ '<WebRTCModuleOptions.h>',
68
+ ]);
69
+ return headerFileContents;
70
+ });
71
+ config.modResults.contents = addDidFinishLaunchingWithOptionsSwift(config.modResults.contents, props.iOSEnableMultitaskingCameraAccess);
72
+ if (props?.ringingPushNotifications) {
73
+ config.modResults.contents = (0, codeMod_1.addSwiftImports)(config.modResults.contents, ['RNCallKeep', 'PushKit', 'RNVoipPushNotification']);
74
+ config.modResults.contents =
75
+ addDidFinishLaunchingWithOptionsRingingSwift(config.modResults.contents, props.ringingPushNotifications);
76
+ config.modResults.contents = addDidUpdatePushCredentialsSwift(config.modResults.contents);
77
+ config.modResults.contents = addDidReceiveIncomingPushCallbackSwift(config.modResults.contents);
78
+ config.modResults.contents = addAudioSessionMethodsSwift(config.modResults.contents);
79
+ }
80
+ return config;
81
+ }
82
+ catch (error) {
83
+ throw new Error(`Cannot setup StreamVideoReactNativeSDK because the AppDelegate(swift) is malformed ${error}`);
84
+ }
46
85
  }
47
86
  });
48
87
  };
49
- function addDidFinishLaunchingWithOptions(contents, iOSEnableMultitaskingCameraAccess) {
88
+ function addDidFinishLaunchingWithOptionsSwift(contents, iOSEnableMultitaskingCameraAccess) {
50
89
  if (iOSEnableMultitaskingCameraAccess) {
90
+ const functionSelector = 'application(_:didFinishLaunchingWithOptions:)';
91
+ const setupMethod = `let options = WebRTCModuleOptions.sharedInstance()
92
+ options.enableMultitaskingCameraAccess = true`;
93
+ if (!contents.includes('options.enableMultitaskingCameraAccess = true')) {
94
+ contents = (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, setupMethod, { position: 'head' });
95
+ }
96
+ }
97
+ return contents;
98
+ }
99
+ function addDidFinishLaunchingWithOptionsObjc(contents, iOSEnableMultitaskingCameraAccess) {
100
+ if (iOSEnableMultitaskingCameraAccess) {
101
+ const functionSelector = 'application:didFinishLaunchingWithOptions:';
51
102
  contents = (0, codeMod_1.addObjcImports)(contents, ['<WebRTCModuleOptions.h>']);
52
103
  const setupMethod = `WebRTCModuleOptions *options = [WebRTCModuleOptions sharedInstance];
53
104
  options.enableMultitaskingCameraAccess = YES;`;
54
105
  if (!contents.includes('options.enableMultitaskingCameraAccess = YES')) {
55
- contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, DID_FINISH_LAUNCHING_WITH_OPTIONS, setupMethod, { position: 'tailBeforeLastReturn' });
106
+ contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, setupMethod, { position: 'head' });
56
107
  }
57
108
  }
58
109
  return contents;
59
110
  }
60
- function addDidFinishLaunchingWithOptionsRinging(contents, ringingPushNotifications) {
111
+ function addDidFinishLaunchingWithOptionsRingingSwift(contents, ringingPushNotifications) {
112
+ const functionSelector = 'application(_:didFinishLaunchingWithOptions:)';
113
+ const supportsVideoString = ringingPushNotifications.disableVideoIos
114
+ ? 'false'
115
+ : 'true';
116
+ const includesCallsInRecents = ringingPushNotifications.includesCallsInRecentsIos ? 'false' : 'true';
117
+ const setupCallKeep = ` let localizedAppName = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String
118
+ let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String
119
+ RNCallKeep.setup([
120
+ "appName": localizedAppName != nil ? localizedAppName! : appName as Any,
121
+ "supportsVideo": ${supportsVideoString},
122
+ "includesCallsInRecents": ${includesCallsInRecents},
123
+ ])`;
124
+ if (!contents.includes('RNCallKeep.setup')) {
125
+ contents = (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, setupCallKeep, { position: 'head' });
126
+ }
127
+ // call the setup of voip push notification
128
+ const voipSetupMethod = 'RNVoipPushNotificationManager.voipRegistration()';
129
+ if (!contents.includes(voipSetupMethod)) {
130
+ contents = (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, ' ' /* indentation */ + voipSetupMethod, { position: 'head' });
131
+ }
132
+ return contents;
133
+ }
134
+ function addDidFinishLaunchingWithOptionsRingingObjc(contents, ringingPushNotifications) {
135
+ const functionSelector = 'application:didFinishLaunchingWithOptions:';
61
136
  // call the setup RNCallKeep
62
137
  const supportsVideoString = ringingPushNotifications.disableVideoIos
63
138
  ? '@NO'
@@ -71,64 +146,170 @@ function addDidFinishLaunchingWithOptionsRinging(contents, ringingPushNotificati
71
146
  @"includesCallsInRecents": ${includesCallsInRecents},
72
147
  }];`;
73
148
  if (!contents.includes('[RNCallKeep setup:@')) {
74
- contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, DID_FINISH_LAUNCHING_WITH_OPTIONS, setupCallKeep, { position: 'head' });
149
+ contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, setupCallKeep, { position: 'head' });
75
150
  }
76
151
  // call the setup of voip push notification
77
152
  const voipSetupMethod = '[RNVoipPushNotificationManager voipRegistration];';
78
153
  if (!contents.includes(voipSetupMethod)) {
79
- contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, DID_FINISH_LAUNCHING_WITH_OPTIONS, voipSetupMethod, { position: 'head' });
154
+ contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, voipSetupMethod, { position: 'head' });
80
155
  }
81
156
  return contents;
82
157
  }
83
- function addDidUpdatePushCredentials(contents) {
158
+ function addDidUpdatePushCredentialsSwift(contents) {
159
+ const updatedPushCredentialsMethod = 'RNVoipPushNotificationManager.didUpdate(credentials, forType: type.rawValue)';
160
+ if (!contents.includes(updatedPushCredentialsMethod)) {
161
+ const functionSelector = 'pushRegistry(_:didUpdate:for:)';
162
+ const codeblock = (0, codeMod_1.findSwiftFunctionCodeBlock)(contents, functionSelector);
163
+ if (!codeblock) {
164
+ return (0, codeMod_1.insertContentsInsideSwiftClassBlock)(contents, 'class AppDelegate', `
165
+ public func pushRegistry(
166
+ _ registry: PKPushRegistry,
167
+ didUpdate credentials: PKPushCredentials,
168
+ for type: PKPushType
169
+ ) {
170
+ ${updatedPushCredentialsMethod}
171
+ }
172
+ `, { position: 'tail' });
173
+ }
174
+ else {
175
+ return (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, updatedPushCredentialsMethod, { position: 'tail' });
176
+ }
177
+ }
178
+ return contents;
179
+ }
180
+ function addDidUpdatePushCredentialsObjc(contents) {
84
181
  const updatedPushCredentialsMethod = '[RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];';
85
182
  if (!contents.includes(updatedPushCredentialsMethod)) {
86
- const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, DID_UPDATE_PUSH_CREDENTIALS);
183
+ const functionSelector = 'pushRegistry:didUpdatePushCredentials:forType:';
184
+ const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, functionSelector);
87
185
  if (!codeblock) {
88
- return (0, addNewLinesToAppDelegate_1.default)(contents, [
186
+ return (0, addNewLinesToAppDelegateObjc_1.default)(contents, [
89
187
  '- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {',
90
188
  ' ' /* indentation */ + updatedPushCredentialsMethod,
91
189
  '}',
92
190
  ]);
93
191
  }
94
192
  else {
95
- return (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, DID_UPDATE_PUSH_CREDENTIALS, updatedPushCredentialsMethod, { position: 'tail' });
193
+ return (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, updatedPushCredentialsMethod, { position: 'tail' });
194
+ }
195
+ }
196
+ return contents;
197
+ }
198
+ function addAudioSessionMethodsSwift(contents) {
199
+ const audioSessionDidActivateMethod = 'RTCAudioSession.sharedInstance().audioSessionDidActivate(AVAudioSession.sharedInstance())';
200
+ if (!contents.includes(audioSessionDidActivateMethod)) {
201
+ const functionSelector = 'provider(_:didActivate:)';
202
+ if (!contents.includes('didActivateAudioSession')) {
203
+ contents = (0, codeMod_1.insertContentsInsideSwiftClassBlock)(contents, 'class AppDelegate', `
204
+ func provider(_ provider: CXProvider, didActivateAudioSession audioSession: AVAudioSession) {
205
+ ${audioSessionDidActivateMethod}
206
+ }
207
+ `, { position: 'tail' });
208
+ }
209
+ else {
210
+ contents = (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, audioSessionDidActivateMethod, { position: 'tail' });
211
+ }
212
+ }
213
+ const audioSessionDidDeactivateMethod = 'RTCAudioSession.sharedInstance().audioSessionDidDeactivate(AVAudioSession.sharedInstance())';
214
+ if (!contents.includes(audioSessionDidDeactivateMethod)) {
215
+ const functionSelector = 'provider(_:didDeactivate:)';
216
+ if (!contents.includes('didDeactivateAudioSession')) {
217
+ contents = (0, codeMod_1.insertContentsInsideSwiftClassBlock)(contents, 'class AppDelegate', `
218
+ func provider(_ provider: CXProvider, didDeactivateAudioSession audioSession: AVAudioSession) {
219
+ ${audioSessionDidDeactivateMethod}
220
+ }
221
+ `, { position: 'tail' });
222
+ }
223
+ else {
224
+ contents = (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, audioSessionDidDeactivateMethod, { position: 'tail' });
96
225
  }
97
226
  }
98
227
  return contents;
99
228
  }
100
- function addAudioSessionMethods(contents) {
229
+ function addAudioSessionMethodsObjc(contents) {
101
230
  const audioSessionDidActivateMethod = '[[RTCAudioSession sharedInstance] audioSessionDidActivate:[AVAudioSession sharedInstance]];';
102
231
  if (!contents.includes(audioSessionDidActivateMethod)) {
103
- const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, DID_ACTIVATE_AUDIO_SESSION);
232
+ const functionSelector = 'provider:didActivateAudioSession:audioSession:';
233
+ const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, functionSelector);
104
234
  if (!codeblock) {
105
- contents = (0, addNewLinesToAppDelegate_1.default)(contents, [
235
+ contents = (0, addNewLinesToAppDelegateObjc_1.default)(contents, [
106
236
  '- (void) provider:(CXProvider *) provider didActivateAudioSession:(AVAudioSession *) audioSession {',
107
237
  ' ' /* indentation */ + audioSessionDidActivateMethod,
108
238
  '}',
109
239
  ]);
110
240
  }
111
241
  else {
112
- contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, DID_ACTIVATE_AUDIO_SESSION, audioSessionDidActivateMethod, { position: 'tail' });
242
+ contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, audioSessionDidActivateMethod, { position: 'tail' });
113
243
  }
114
244
  }
115
245
  const audioSessionDidDeactivateMethod = '[[RTCAudioSession sharedInstance] audioSessionDidDeactivate:[AVAudioSession sharedInstance]];';
116
246
  if (!contents.includes(audioSessionDidDeactivateMethod)) {
117
- const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, DID_DEACTIVATE_AUDIO_SESSION);
247
+ const functionSelector = 'provider:didDeactivateAudioSession:audioSession:';
248
+ const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, functionSelector);
118
249
  if (!codeblock) {
119
- contents = (0, addNewLinesToAppDelegate_1.default)(contents, [
250
+ contents = (0, addNewLinesToAppDelegateObjc_1.default)(contents, [
120
251
  '- (void) provider:(CXProvider *) provider didDeactivateAudioSession:(AVAudioSession *) audioSession {',
121
252
  ' ' /* indentation */ + audioSessionDidDeactivateMethod,
122
253
  '}',
123
254
  ]);
124
255
  }
125
256
  else {
126
- contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, DID_DEACTIVATE_AUDIO_SESSION, audioSessionDidDeactivateMethod, { position: 'tail' });
257
+ contents = (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, audioSessionDidDeactivateMethod, { position: 'tail' });
258
+ }
259
+ }
260
+ return contents;
261
+ }
262
+ function addDidReceiveIncomingPushCallbackSwift(contents) {
263
+ const onIncomingPush = `
264
+ guard let stream = payload.dictionaryPayload["stream"] as? [String: Any],
265
+ let createdCallerName = stream["created_by_display_name"] as? String,
266
+ let cid = stream["call_cid"] as? String else {
267
+ completion()
268
+ return
269
+ }
270
+
271
+ let uuid = UUID().uuidString
272
+
273
+ StreamVideoReactNative.registerIncomingCall(cid, uuid: uuid)
274
+
275
+ RNVoipPushNotificationManager.addCompletionHandler(uuid, completionHandler: completion)
276
+
277
+ RNVoipPushNotificationManager.didReceiveIncomingPush(with: payload, forType: type.rawValue)
278
+
279
+ RNCallKeep.reportNewIncomingCall(uuid,
280
+ handle: createdCallerName,
281
+ handleType: "generic",
282
+ hasVideo: true,
283
+ localizedCallerName: createdCallerName,
284
+ supportsHolding: true,
285
+ supportsDTMF: true,
286
+ supportsGrouping: true,
287
+ supportsUngrouping: true,
288
+ fromPushKit: true,
289
+ payload: stream,
290
+ withCompletionHandler: nil)`;
291
+ if (!contents.includes('RNVoipPushNotificationManager.didReceiveIncomingPush')) {
292
+ const functionSelector = 'pushRegistry(_:didReceiveIncomingPushWith:for:completion:)';
293
+ const codeblock = (0, codeMod_1.findSwiftFunctionCodeBlock)(contents, functionSelector);
294
+ if (!codeblock) {
295
+ return (0, codeMod_1.insertContentsInsideSwiftClassBlock)(contents, 'class AppDelegate', `
296
+ public func pushRegistry(
297
+ _ registry: PKPushRegistry,
298
+ didReceiveIncomingPushWith payload: PKPushPayload,
299
+ for type: PKPushType,
300
+ completion: @escaping () -> Void
301
+ ) {
302
+ ${onIncomingPush}
303
+ }
304
+ `, { position: 'tail' });
305
+ }
306
+ else {
307
+ return (0, codeMod_1.insertContentsInsideSwiftFunctionBlock)(contents, functionSelector, onIncomingPush, { position: 'tail' });
127
308
  }
128
309
  }
129
310
  return contents;
130
311
  }
131
- function addDidReceiveIncomingPushCallback(contents) {
312
+ function addDidReceiveIncomingPushCallbackObjc(contents) {
132
313
  const onIncomingPush = `
133
314
  // process the payload and store it in the native module's cache
134
315
  NSDictionary *stream = payload.dictionaryPayload[@"stream"];
@@ -160,16 +341,17 @@ function addDidReceiveIncomingPushCallback(contents) {
160
341
  withCompletionHandler: nil];
161
342
  `;
162
343
  if (!contents.includes('[RNVoipPushNotificationManager didReceiveIncomingPushWithPayload')) {
163
- const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, DID_RECEIVE_INCOMING_PUSH);
344
+ const functionSelector = 'pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:';
345
+ const codeblock = (0, codeMod_1.findObjcFunctionCodeBlock)(contents, functionSelector);
164
346
  if (!codeblock) {
165
- return (0, addNewLinesToAppDelegate_1.default)(contents, [
347
+ return (0, addNewLinesToAppDelegateObjc_1.default)(contents, [
166
348
  '- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {',
167
349
  ...onIncomingPush.trim().split('\n'),
168
350
  '}',
169
351
  ]);
170
352
  }
171
353
  else {
172
- return (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, DID_RECEIVE_INCOMING_PUSH, onIncomingPush, { position: 'tail' });
354
+ return (0, codeMod_1.insertContentsInsideObjcFunctionBlock)(contents, functionSelector, onIncomingPush, { position: 'tail' });
173
355
  }
174
356
  }
175
357
  return contents;
@@ -1,5 +1,7 @@
1
1
  #import <React/RCTBridgeModule.h>
2
2
  #import <React/RCTEventEmitter.h>
3
+ #import <React/RCTUIManager.h>
4
+ #import <UIKit/UIKit.h>
3
5
  #import "StreamVideoReactNative.h"
4
6
  #import "WebRTCModule.h"
5
7
  #import "WebRTCModuleOptions.h"
@@ -29,6 +31,12 @@ void broadcastNotificationCallback(CFNotificationCenterRef center,
29
31
  }
30
32
  RCT_EXPORT_MODULE();
31
33
 
34
+ // the viewRegistry approach is taken from https://github.com/facebook/react-native/issues/50800#issuecomment-2823327307
35
+ #ifdef RCT_NEW_ARCH_ENABLED
36
+ @synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED;
37
+ #endif // RCT_NEW_ARCH_ENABLED
38
+ @synthesize bridge = _bridge;
39
+
32
40
  +(BOOL)requiresMainQueueSetup {
33
41
  return NO;
34
42
  }
@@ -225,6 +233,94 @@ RCT_EXPORT_METHOD(removeIncomingCall:(NSString *)cid
225
233
  });
226
234
  }
227
235
 
236
+ RCT_EXPORT_METHOD(captureRef:(nonnull NSNumber *)reactTag
237
+ options:(NSDictionary *)options
238
+ resolver:(RCTPromiseResolveBlock)resolve
239
+ rejecter:(RCTPromiseRejectBlock)reject)
240
+ {
241
+ #ifdef RCT_NEW_ARCH_ENABLED
242
+ [self.viewRegistry_DEPRECATED addUIBlock:^(RCTViewRegistry *viewRegistry) {
243
+ UIView *view = [self.viewRegistry_DEPRECATED viewForReactTag:reactTag];
244
+
245
+ #else
246
+ [self.bridge.uiManager
247
+ addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
248
+ UIView *view = [uiManager viewForReactTag:reactTag];
249
+ #endif
250
+
251
+ if (!view) {
252
+ reject(RCTErrorUnspecified, [NSString stringWithFormat:@"No view found with reactTag: %@", reactTag], nil);
253
+ return;
254
+ }
255
+
256
+ // Get capture options
257
+ NSString *format = options[@"format"] ? [options[@"format"] lowercaseString] : @"png";
258
+ CGFloat quality = options[@"quality"] ? [options[@"quality"] floatValue] : 1.0;
259
+ NSNumber *width = options[@"width"];
260
+ NSNumber *height = options[@"height"];
261
+
262
+ // Determine the size to render
263
+ CGSize size;
264
+ CGRect bounds = view.bounds;
265
+ if (width && height) {
266
+ size = CGSizeMake([width floatValue], [height floatValue]);
267
+ } else {
268
+ size = bounds.size;
269
+ }
270
+
271
+ // Abort if size is invalid
272
+ if (size.width <= 0 || size.height <= 0) {
273
+ reject(@"INVALID_SIZE", @"View has invalid size", nil);
274
+ return;
275
+ }
276
+
277
+ // Begin image context with appropriate scale
278
+ UIGraphicsBeginImageContextWithOptions(size, NO, 0);
279
+
280
+ // Calculate scaling if needed
281
+ CGRect drawRect = bounds;
282
+ if (width && height) {
283
+ CGFloat scaleX = size.width / bounds.size.width;
284
+ CGFloat scaleY = size.height / bounds.size.height;
285
+
286
+ // Apply transform to context for scaling if dimensions differ
287
+ CGContextRef context = UIGraphicsGetCurrentContext();
288
+ if (context) {
289
+ CGContextTranslateCTM(context, 0, size.height);
290
+ CGContextScaleCTM(context, scaleX, -scaleY);
291
+ drawRect = CGRectMake(0, 0, bounds.size.width, bounds.size.height);
292
+ }
293
+ }
294
+
295
+ BOOL success = [view drawViewHierarchyInRect:drawRect afterScreenUpdates:YES];
296
+
297
+ UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
298
+ UIGraphicsEndImageContext();
299
+
300
+ if (!success || !image) {
301
+ reject(@"CAPTURE_FAILED", @"Failed to capture view as image", nil);
302
+ return;
303
+ }
304
+
305
+ // Convert to base64 string based on format
306
+ NSString *base64;
307
+ if ([format isEqualToString:@"jpg"] || [format isEqualToString:@"jpeg"]) {
308
+ NSData *imageData = UIImageJPEGRepresentation(image, quality);
309
+ base64 = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
310
+ } else {
311
+ // Default to PNG
312
+ NSData *imageData = UIImagePNGRepresentation(image);
313
+ base64 = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
314
+ }
315
+
316
+ if (base64) {
317
+ resolve(base64);
318
+ } else {
319
+ reject(@"ENCODING_FAILED", @"Failed to encode image to base64", nil);
320
+ }
321
+ }];
322
+ }
323
+
228
324
  -(NSArray<NSString *> *)supportedEvents {
229
325
  return @[@"StreamVideoReactNative_Ios_Screenshare_Event", @"isLowPowerModeEnabled", @"thermalStateDidChange"];
230
326
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-native-sdk",
3
- "version": "1.12.0",
3
+ "version": "1.13.1",
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.21.0",
49
- "@stream-io/video-react-bindings": "1.5.19",
48
+ "@stream-io/video-client": "1.22.0",
49
+ "@stream-io/video-react-bindings": "1.6.0",
50
50
  "intl-pluralrules": "2.0.1",
51
51
  "lodash.merge": "^4.6.2",
52
52
  "react-native-url-polyfill": "1.3.0",
@@ -59,7 +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/react-native-webrtc": ">=125.0.7",
62
+ "@stream-io/react-native-webrtc": ">=125.2.1",
63
63
  "@stream-io/video-filters-react-native": ">=0.1.0",
64
64
  "expo": ">=47.0.0",
65
65
  "expo-build-properties": "*",
@@ -112,45 +112,43 @@
112
112
  }
113
113
  },
114
114
  "devDependencies": {
115
- "@expo/config-plugins": "~9.0.17",
116
- "@expo/config-types": "^52.0.5",
117
- "@expo/plist": "^0.2.2",
115
+ "@expo/config-plugins": "10.0.2",
116
+ "@expo/config-types": "^53.0.4",
117
+ "@expo/plist": "^0.3.4",
118
118
  "@notifee/react-native": "9.1.8",
119
119
  "@react-native-community/netinfo": "11.4.1",
120
120
  "@react-native-community/push-notification-ios": "1.11.0",
121
- "@react-native-firebase/app": "^21.13.0",
122
- "@react-native-firebase/messaging": "^21.13.0",
123
- "@react-native/babel-preset": "^0.76.9",
124
- "@stream-io/react-native-webrtc": "^125.2.0",
125
- "@stream-io/video-filters-react-native": "^0.2.8",
121
+ "@react-native-firebase/app": "^22.1.0",
122
+ "@react-native-firebase/messaging": "^22.1.0",
123
+ "@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",
126
126
  "@testing-library/jest-native": "^5.4.3",
127
- "@testing-library/react-native": "^12.9.0",
127
+ "@testing-library/react-native": "13.2.0",
128
128
  "@tsconfig/node14": "14.1.3",
129
129
  "@types/jest": "^29.5.14",
130
130
  "@types/lodash.merge": "^4.6.9",
131
- "@types/react": "^18.2.44",
131
+ "@types/react": "^19.1.3",
132
132
  "@types/react-native-incall-manager": "^4.0.3",
133
- "@types/react-test-renderer": "^18.3.1",
134
- "expo": "~52.0.44",
133
+ "@types/react-test-renderer": "^19.1.0",
134
+ "expo": "~53.0.8",
135
135
  "expo-build-properties": "^0.13.2",
136
136
  "expo-module-scripts": "^4.0.5",
137
137
  "expo-modules-core": "2.2.3",
138
138
  "expo-notifications": "~0.29.14",
139
139
  "jest": "^29.7.0",
140
- "react-native": "^0.76.9",
140
+ "react": "19.0.0",
141
+ "react-native": "0.79.2",
141
142
  "react-native-builder-bob": "~0.23",
142
143
  "react-native-callkeep": "^4.3.16",
143
144
  "react-native-gesture-handler": "^2.25.0",
144
- "react-native-incall-manager": "^4.2.0",
145
- "react-native-reanimated": "~3.16.7",
145
+ "react-native-incall-manager": "^4.2.1",
146
+ "react-native-reanimated": "~3.17.5",
146
147
  "react-native-svg": "15.11.2",
147
- "react-native-voip-push-notification": "3.3.2",
148
- "react-test-renderer": "^18.3.1",
148
+ "react-native-voip-push-notification": "3.3.3",
149
+ "react-test-renderer": "19.0.0",
149
150
  "rimraf": "^6.0.1",
150
- "typescript": "^5.8.2"
151
- },
152
- "resolutions": {
153
- "@types/react": "^18.2.44"
151
+ "typescript": "^5.8.3"
154
152
  },
155
153
  "react-native-builder-bob": {
156
154
  "source": "src",
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
- import { StyleSheet, View } from 'react-native';
2
+ import { Platform, StyleSheet, View } from 'react-native';
3
3
  import type { MediaStream } from '@stream-io/react-native-webrtc';
4
4
  import { RTCView } from '@stream-io/react-native-webrtc';
5
5
  import type { ParticipantViewProps } from './ParticipantView';
@@ -15,6 +15,7 @@ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
15
15
  import { ParticipantVideoFallback as DefaultParticipantVideoFallback } from './ParticipantVideoFallback';
16
16
  import { useTheme } from '../../../contexts/ThemeContext';
17
17
  import { useTrackDimensions } from '../../../hooks/useTrackDimensions';
18
+ import { useScreenshotIosContext } from '../../../contexts/internal/ScreenshotIosContext';
18
19
 
19
20
  const DEFAULT_VIEWPORT_VISIBILITY_STATE: Record<
20
21
  VideoTrackType,
@@ -58,9 +59,19 @@ export const VideoRenderer = ({
58
59
  useCallStateHooks();
59
60
  const { isParticipantVideoEnabled } = useIncomingVideoSettings();
60
61
  const callingState = useCallCallingState();
61
- const pendingVideoLayoutRef = useRef<SfuModels.VideoDimension>();
62
- const subscribedVideoLayoutRef = useRef<SfuModels.VideoDimension>();
62
+ const pendingVideoLayoutRef = useRef<SfuModels.VideoDimension | undefined>(
63
+ undefined,
64
+ );
65
+ const subscribedVideoLayoutRef = useRef<SfuModels.VideoDimension | undefined>(
66
+ undefined,
67
+ );
63
68
  const { direction } = useCameraState();
69
+ const viewRef = useRef(null);
70
+ const {
71
+ register: registerIosScreenshot,
72
+ deregister: deregisterIosScreenshot,
73
+ } = useScreenshotIosContext();
74
+
64
75
  const videoDimensions = useTrackDimensions(participant, trackType);
65
76
  const {
66
77
  isLocalParticipant,
@@ -86,6 +97,26 @@ export const VideoRenderer = ({
86
97
  isPublishingVideoTrack &&
87
98
  isParticipantVideoEnabled(participant.sessionId);
88
99
 
100
+ React.useEffect(() => {
101
+ if (
102
+ Platform.OS === 'ios' &&
103
+ registerIosScreenshot &&
104
+ viewRef.current &&
105
+ canShowVideo
106
+ ) {
107
+ registerIosScreenshot(participant, trackType, viewRef);
108
+ return () => {
109
+ deregisterIosScreenshot(participant, trackType);
110
+ };
111
+ }
112
+ }, [
113
+ participant,
114
+ trackType,
115
+ registerIosScreenshot,
116
+ canShowVideo,
117
+ deregisterIosScreenshot,
118
+ ]);
119
+
89
120
  const mirror =
90
121
  isLocalParticipant && !isScreenSharing && direction === 'front';
91
122
 
@@ -262,6 +293,7 @@ export const VideoRenderer = ({
262
293
  style={[styles.videoStream, videoRenderer.videoStream]}
263
294
  streamURL={videoStreamToRender.toURL()}
264
295
  mirror={mirror}
296
+ ref={viewRef}
265
297
  objectFit={
266
298
  objectFit ??
267
299
  (videoDimensions.width > videoDimensions.height