@fishjam-cloud/react-native-client 0.25.0-rc.1 → 0.25.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishjam-cloud/react-native-client",
3
- "version": "0.25.0-rc.1",
3
+ "version": "0.25.0-rc.3",
4
4
  "description": "React Native client library for Fishjam",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Fishjam Team",
@@ -10,7 +10,8 @@
10
10
  "react-native": "src/index.ts",
11
11
  "files": [
12
12
  "dist/**",
13
- "plugin/**",
13
+ "plugin/build/**",
14
+ "plugin/broadcastExtensionFiles/**",
14
15
  "app.plugin.js"
15
16
  ],
16
17
  "repository": {
@@ -44,16 +45,16 @@
44
45
  "react-native": "*"
45
46
  },
46
47
  "dependencies": {
47
- "@fishjam-cloud/react-client": "0.25.0-rc.1",
48
+ "@fishjam-cloud/react-client": "0.25.0-rc.3",
48
49
  "@fishjam-cloud/react-native-webrtc": "git+https://github.com/fishjam-cloud/fishjam-react-native-webrtc.git#9753272f9e6c992d8a05263e668e2198ec858160",
50
+ "promise-fs": "^2.1.1",
49
51
  "react-native-get-random-values": "^1.11.0"
50
52
  },
51
53
  "devDependencies": {
52
54
  "@types/promise-fs": "^2.1.2",
53
55
  "eslint-config-expo": "~9.2.0",
54
56
  "eslint-plugin-prettier": "^5.5.1",
55
- "expo-module-scripts": "^5.0.7",
56
- "promise-fs": "^2.1.1"
57
+ "expo-module-scripts": "^5.0.7"
57
58
  },
58
59
  "packageManager": "yarn@4.12.0",
59
60
  "stableVersion": "0.23.0"
@@ -1,19 +0,0 @@
1
- export type FishjamPluginOptions =
2
- | {
3
- android?: {
4
- enableForegroundService?: boolean;
5
- enableScreensharing?: boolean;
6
- supportsPictureInPicture?: boolean;
7
- };
8
- ios?: {
9
- enableScreensharing?: boolean;
10
- supportsPictureInPicture?: boolean;
11
- broadcastExtensionTargetName?: string;
12
- broadcastExtensionDisplayName?: string;
13
- appGroupContainerId?: string;
14
- mainTargetName?: string;
15
- iphoneDeploymentTarget?: string;
16
- enableVoIPBackgroundMode?: boolean;
17
- };
18
- }
19
- | undefined;
@@ -1,42 +0,0 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
2
-
3
- import type { FishjamPluginOptions } from './types';
4
- import { withFishjamAndroid } from './withFishjamAndroid';
5
- import { withFishjamIos } from './withFishjamIos';
6
-
7
- /**
8
- * Main Fishjam Expo config plugin.
9
- *
10
- * This plugin configures both iOS and Android platforms for Picture-in-Picture support.
11
- *
12
- * ## Usage
13
- *
14
- * ```json
15
- * {
16
- * "plugins": [
17
- * [
18
- * "@fishjam-cloud/react-native-client",
19
- * {
20
- * "android": {
21
- * "supportsPictureInPicture": true
22
- * },
23
- * "ios": {
24
- * "supportsPictureInPicture": true
25
- * }
26
- * }
27
- * ]
28
- * ]
29
- * }
30
- * ```
31
- *
32
- * @param config - Expo config object
33
- * @param options - Plugin configuration options
34
- * @returns Modified config object
35
- */
36
- const withFishjam: ConfigPlugin<FishjamPluginOptions> = (config, options) => {
37
- config = withFishjamAndroid(config, options);
38
- config = withFishjamIos(config, options);
39
- return config;
40
- };
41
-
42
- export default withFishjam;
@@ -1,112 +0,0 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
2
- import { AndroidConfig, withAndroidManifest } from '@expo/config-plugins';
3
- import { getMainApplicationOrThrow } from '@expo/config-plugins/build/android/Manifest';
4
-
5
- import type { FishjamPluginOptions } from './types';
6
-
7
- const withFishjamPictureInPicture: ConfigPlugin<FishjamPluginOptions> = (config, props) =>
8
- withAndroidManifest(config, (configuration) => {
9
- const activity = AndroidConfig.Manifest.getMainActivityOrThrow(configuration.modResults);
10
-
11
- if (props?.android?.supportsPictureInPicture) {
12
- activity.$['android:supportsPictureInPicture'] = 'true';
13
- } else {
14
- delete activity.$['android:supportsPictureInPicture'];
15
- }
16
- return configuration;
17
- });
18
-
19
- const withFishjamForegroundService: ConfigPlugin<FishjamPluginOptions> = (config, props) =>
20
- withAndroidManifest(config, async (configuration) => {
21
- if (!props?.android?.enableForegroundService) {
22
- return configuration;
23
- }
24
-
25
- const mainApplication = getMainApplicationOrThrow(configuration.modResults);
26
- mainApplication.service = mainApplication.service || [];
27
-
28
- const webRTCForegroundService = {
29
- $: {
30
- 'android:name': 'com.oney.WebRTCModule.foregroundService.WebRTCForegroundService',
31
- 'android:foregroundServiceType': 'camera|microphone',
32
- 'android:stopWithTask': 'true',
33
- },
34
- };
35
-
36
- const existingWebRTCForegroundServiceIndex = mainApplication.service.findIndex(
37
- (service) => service.$['android:name'] === webRTCForegroundService.$['android:name'],
38
- );
39
-
40
- if (existingWebRTCForegroundServiceIndex !== -1) {
41
- mainApplication.service[existingWebRTCForegroundServiceIndex] = webRTCForegroundService;
42
- } else {
43
- mainApplication.service.push(webRTCForegroundService);
44
- }
45
-
46
- if (props?.android?.enableScreensharing) {
47
- const mediaProjectionService = {
48
- $: {
49
- 'android:name': 'com.oney.WebRTCModule.MediaProjectionService',
50
- 'android:foregroundServiceType': 'mediaProjection',
51
- 'android:stopWithTask': 'true',
52
- },
53
- };
54
-
55
- const existingMediaProjectionServiceIndex = mainApplication.service.findIndex(
56
- (service) => service.$['android:name'] === mediaProjectionService.$['android:name'],
57
- );
58
-
59
- if (existingMediaProjectionServiceIndex !== -1) {
60
- mainApplication.service[existingMediaProjectionServiceIndex] = mediaProjectionService;
61
- } else {
62
- mainApplication.service.push(mediaProjectionService);
63
- }
64
- }
65
- return configuration;
66
- });
67
-
68
- const withFishjamForegroundServicePermission: ConfigPlugin<FishjamPluginOptions> = (config, props) =>
69
- withAndroidManifest(config, (configuration) => {
70
- if (!props?.android?.enableForegroundService) {
71
- return configuration;
72
- }
73
-
74
- const mainApplication = configuration.modResults;
75
- if (!mainApplication.manifest) {
76
- return configuration;
77
- }
78
-
79
- if (!mainApplication.manifest['uses-permission']) {
80
- mainApplication.manifest['uses-permission'] = [];
81
- }
82
-
83
- const permissions = mainApplication.manifest['uses-permission'];
84
-
85
- const foregroundServicePermissions = [
86
- 'android.permission.FOREGROUND_SERVICE',
87
- 'android.permission.FOREGROUND_SERVICE_CAMERA',
88
- 'android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION',
89
- 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
90
- ];
91
-
92
- foregroundServicePermissions.forEach((permissionName) => {
93
- const hasPermission = permissions.some((perm) => perm.$?.['android:name'] === permissionName);
94
-
95
- if (!hasPermission) {
96
- permissions.push({
97
- $: {
98
- 'android:name': permissionName,
99
- },
100
- });
101
- }
102
- });
103
-
104
- return configuration;
105
- });
106
-
107
- export const withFishjamAndroid: ConfigPlugin<FishjamPluginOptions> = (config, props) => {
108
- config = withFishjamForegroundServicePermission(config, props);
109
- config = withFishjamForegroundService(config, props);
110
- config = withFishjamPictureInPicture(config, props);
111
- return config;
112
- };
@@ -1,336 +0,0 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
2
- import { withEntitlementsPlist, withInfoPlist, withPodfileProperties, withXcodeProject } from '@expo/config-plugins';
3
- import * as path from 'path';
4
- import * as fs from 'promise-fs';
5
-
6
- import type { FishjamPluginOptions } from './types';
7
-
8
- function getSbeTargetName(props: FishjamPluginOptions) {
9
- return props?.ios?.broadcastExtensionTargetName || 'ScreenBroadcastExtension';
10
- }
11
-
12
- function getSbeDisplayName(props: FishjamPluginOptions) {
13
- return props?.ios?.broadcastExtensionDisplayName || 'ScreenBroadcast';
14
- }
15
-
16
- const TARGETED_DEVICE_FAMILY = `"1,2"`;
17
- const IPHONEOS_DEPLOYMENT_TARGET = '15.1';
18
- const GROUP_IDENTIFIER_TEMPLATE_REGEX = /{{GROUP_IDENTIFIER}}/gm;
19
- const BUNDLE_IDENTIFIER_TEMPLATE_REGEX = /{{BUNDLE_IDENTIFIER}}/gm;
20
- const DISPLAY_NAME_TEMPLATE_REGEX = /{{DISPLAY_NAME}}/gm;
21
-
22
- /**
23
- * A helper function for updating a value in a file for given regex
24
- */
25
- async function updateFileWithRegex(
26
- iosPath: string,
27
- fileName: string,
28
- regex: RegExp,
29
- value: string,
30
- props: FishjamPluginOptions,
31
- ) {
32
- const targetName = getSbeTargetName(props);
33
- const filePath = `${iosPath}/${targetName}/${fileName}`;
34
- let file = await fs.readFile(filePath, { encoding: 'utf-8' });
35
- file = file.replace(regex, value);
36
- await fs.writeFile(filePath, file);
37
- }
38
-
39
- /**
40
- * Adds "App Group" permission
41
- * App Group allows your app and the ScreenBroadcastExtension to communicate with each other.
42
- */
43
- const withAppGroupPermissions: ConfigPlugin<FishjamPluginOptions> = (config, props) => {
44
- const APP_GROUP_KEY = 'com.apple.security.application-groups';
45
- const bundleIdentifier = config.ios?.bundleIdentifier || '';
46
- const groupIdentifier = props?.ios?.appGroupContainerId || `group.${bundleIdentifier}`;
47
- const mainTarget = props?.ios?.mainTargetName || '';
48
-
49
- config.ios ??= {};
50
- config.ios.entitlements ??= {};
51
- config.ios.entitlements[APP_GROUP_KEY] ??= [];
52
-
53
- const entitlementsArray = config.ios.entitlements[APP_GROUP_KEY] as string[];
54
- if (!entitlementsArray.includes(groupIdentifier)) {
55
- entitlementsArray.push(groupIdentifier);
56
- }
57
-
58
- config = withEntitlementsPlist(config, (newConfig) => {
59
- const modResultsArray = (newConfig.modResults[APP_GROUP_KEY] as string[]) || [];
60
- if (!modResultsArray.includes(groupIdentifier)) {
61
- modResultsArray.push(groupIdentifier);
62
- }
63
- newConfig.modResults[APP_GROUP_KEY] = modResultsArray;
64
- return newConfig;
65
- });
66
-
67
- // eslint-disable-next-line no-shadow
68
- config = withXcodeProject(config, (props) => {
69
- const xcodeProject = props.modResults;
70
- const targets = xcodeProject.getFirstTarget();
71
- const project = xcodeProject.getFirstProject();
72
-
73
- if (!targets || !project) {
74
- return props;
75
- }
76
-
77
- const targetUuid = targets.uuid;
78
- const projectUuid = project.uuid;
79
-
80
- const projectObj = xcodeProject.hash.project.objects.PBXProject[projectUuid];
81
- projectObj.attributes ??= {};
82
- projectObj.attributes.TargetAttributes ??= {};
83
- projectObj.attributes.TargetAttributes[targetUuid] ??= {};
84
- projectObj.attributes.TargetAttributes[targetUuid].SystemCapabilities ??= {};
85
-
86
- projectObj.attributes.TargetAttributes[targetUuid].SystemCapabilities['com.apple.ApplicationGroups.iOS'] = {
87
- enabled: 1,
88
- };
89
-
90
- const mainTargetName = mainTarget || props.modRequest.projectName;
91
- const entitlementsFilePath = `${mainTargetName}/${mainTargetName}.entitlements`;
92
- const configurations = xcodeProject.pbxXCBuildConfigurationSection();
93
-
94
- Object.keys(configurations).forEach((key) => {
95
- // eslint-disable-next-line no-shadow
96
- const config = configurations[key];
97
- if (config.buildSettings?.PRODUCT_NAME?.includes(mainTargetName)) {
98
- if (!config.buildSettings.CODE_SIGN_ENTITLEMENTS) {
99
- config.buildSettings.CODE_SIGN_ENTITLEMENTS = entitlementsFilePath;
100
- }
101
- }
102
- });
103
-
104
- return props;
105
- });
106
-
107
- return config;
108
- };
109
-
110
- /**
111
- * Adds constants to Info.plist
112
- * In order to dynamically retrieve extension's bundleId and group name we need to store it in Info.plist.
113
- */
114
- const withInfoPlistConstants: ConfigPlugin<FishjamPluginOptions> = (config, props) =>
115
- withInfoPlist(config, (configuration) => {
116
- const bundleIdentifier = configuration.ios?.bundleIdentifier || '';
117
- const groupIdentifier = props?.ios?.appGroupContainerId || `group.${bundleIdentifier}`;
118
- configuration.modResults['RTCScreenSharingExtension'] = `${bundleIdentifier}.${getSbeTargetName(props)}`;
119
- configuration.modResults['RTCAppGroupIdentifier'] = groupIdentifier;
120
- return configuration;
121
- });
122
-
123
- /**
124
- * Updates and copies required extension files.
125
- * Our extension needs to be properly setup inside the Xcode project. In order to do that we need to copy the files and update the pbxproj.
126
- */
127
- const withFishjamSBE: ConfigPlugin<FishjamPluginOptions> = (config, options) =>
128
- withXcodeProject(config, async (props) => {
129
- const appName = props.modRequest.projectName || '';
130
- const iosPath = props.modRequest.platformProjectRoot;
131
- const bundleIdentifier = props.ios?.bundleIdentifier;
132
- const groupIdentifier = options?.ios?.appGroupContainerId || `group.${bundleIdentifier}`;
133
- const xcodeProject = props.modResults;
134
- const targetName = getSbeTargetName(options);
135
-
136
- const pluginDir = require.resolve('@fishjam-cloud/react-native-client/package.json');
137
- const extensionSourceDir = path.join(pluginDir, '../plugin/broadcastExtensionFiles/');
138
-
139
- const projPath = `${iosPath}/${appName}.xcodeproj/project.pbxproj`;
140
- const templateTargetName = 'ScreenBroadcastExtension';
141
-
142
- const extFiles = [
143
- 'SampleHandler.swift',
144
- 'SampleUploader.swift',
145
- `${templateTargetName}.entitlements`,
146
- `Info.plist`,
147
- 'SocketConnection.swift',
148
- 'DarwinNotificationCenter.swift',
149
- 'Atomic.swift',
150
- ];
151
-
152
- const destFiles = [
153
- 'SampleHandler.swift',
154
- 'SampleUploader.swift',
155
- `${targetName}.entitlements`,
156
- `Info.plist`,
157
- 'SocketConnection.swift',
158
- 'DarwinNotificationCenter.swift',
159
- 'Atomic.swift',
160
- ];
161
-
162
- await xcodeProject.parse(async function (err: Error) {
163
- if (err) {
164
- console.error(`Error parsing iOS project: ${JSON.stringify(err)}`);
165
- return;
166
- }
167
- if (xcodeProject.pbxTargetByName(targetName)) {
168
- // eslint-disable-next-line no-console
169
- console.log(`${targetName} already exists in project. Skipping...`);
170
- return;
171
- }
172
- try {
173
- await fs.mkdir(`${iosPath}/${targetName}`, { recursive: true });
174
- for (let i = 0; i < extFiles.length; i++) {
175
- const srcFile = `${extensionSourceDir}${extFiles[i]}`;
176
- const destFile = `${iosPath}/${targetName}/${destFiles[i]}`;
177
- await fs.copyFile(srcFile, destFile);
178
- }
179
- } catch (e) {
180
- console.error('Error copying extension files: ', e);
181
- throw e;
182
- }
183
-
184
- try {
185
- await updateFileWithRegex(
186
- iosPath,
187
- `${targetName}.entitlements`,
188
- GROUP_IDENTIFIER_TEMPLATE_REGEX,
189
- groupIdentifier,
190
- options,
191
- );
192
- await updateFileWithRegex(
193
- iosPath,
194
- 'SampleHandler.swift',
195
- GROUP_IDENTIFIER_TEMPLATE_REGEX,
196
- groupIdentifier,
197
- options,
198
- );
199
- await updateFileWithRegex(
200
- iosPath,
201
- 'SampleUploader.swift',
202
- BUNDLE_IDENTIFIER_TEMPLATE_REGEX,
203
- bundleIdentifier || '',
204
- options,
205
- );
206
- await updateFileWithRegex(
207
- iosPath,
208
- 'Info.plist',
209
- DISPLAY_NAME_TEMPLATE_REGEX,
210
- getSbeDisplayName(options),
211
- options,
212
- );
213
- } catch (e) {
214
- console.error('Error updating extension files: ', e);
215
- }
216
-
217
- // Create new PBXGroup for the extension
218
- const extGroup = xcodeProject.addPbxGroup(extFiles, targetName, targetName);
219
-
220
- // Add the new PBXGroup to the top level group. This makes the
221
- // files / folder appear in the file explorer in Xcode.
222
- const groups = xcodeProject.hash.project.objects['PBXGroup'];
223
- Object.keys(groups).forEach(function (key) {
224
- if (groups[key].name === undefined) {
225
- xcodeProject.addToPbxGroup(extGroup.uuid, key);
226
- }
227
- });
228
-
229
- // WORK AROUND for codeProject.addTarget BUG
230
- // Xcode projects don't contain these if there is only one target
231
- // An upstream fix should be made to the code referenced in this link:
232
- // - https://github.com/apache/cordova-node-xcode/blob/8b98cabc5978359db88dc9ff2d4c015cba40f150/lib/pbxProject.js#L860
233
- const projObjects = xcodeProject.hash.project.objects;
234
- projObjects['PBXTargetDependency'] = projObjects['PBXTargetDependency'] || {};
235
- projObjects['PBXContainerItemProxy'] = projObjects['PBXContainerItemProxy'] || {};
236
-
237
- // Add the SBE target
238
- // This adds PBXTargetDependency and PBXContainerItemProxy for you
239
- const sbeTarget = xcodeProject.addTarget(
240
- targetName,
241
- 'app_extension',
242
- targetName,
243
- `${bundleIdentifier}.${targetName}`,
244
- );
245
-
246
- // Add build phases to the new target
247
- xcodeProject.addBuildPhase(
248
- [
249
- 'SampleHandler.swift',
250
- 'SampleUploader.swift',
251
- 'SocketConnection.swift',
252
- 'DarwinNotificationCenter.swift',
253
- 'Atomic.swift',
254
- ],
255
- 'PBXSourcesBuildPhase',
256
- 'Sources',
257
- sbeTarget.uuid,
258
- );
259
- xcodeProject.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', sbeTarget.uuid);
260
-
261
- xcodeProject.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', sbeTarget.uuid);
262
-
263
- xcodeProject.addFramework('ReplayKit.framework', {
264
- target: sbeTarget.uuid,
265
- });
266
-
267
- // Edit the Deployment info of the new Target, only IphoneOS and Targeted Device Family
268
- // However, can be more
269
- const configurations = xcodeProject.pbxXCBuildConfigurationSection();
270
- for (const key in configurations) {
271
- if (
272
- typeof configurations[key].buildSettings !== 'undefined' &&
273
- configurations[key].buildSettings.PRODUCT_NAME === `"${targetName}"`
274
- ) {
275
- const buildSettingsObj = configurations[key].buildSettings;
276
- buildSettingsObj.IPHONEOS_DEPLOYMENT_TARGET =
277
- options?.ios?.iphoneDeploymentTarget ?? IPHONEOS_DEPLOYMENT_TARGET;
278
- buildSettingsObj.TARGETED_DEVICE_FAMILY = TARGETED_DEVICE_FAMILY;
279
- buildSettingsObj.CODE_SIGN_ENTITLEMENTS = `${targetName}/${targetName}.entitlements`;
280
- buildSettingsObj.CODE_SIGN_STYLE = 'Automatic';
281
- buildSettingsObj.INFOPLIST_FILE = `${targetName}/Info.plist`;
282
- buildSettingsObj.SWIFT_VERSION = '5.0';
283
- buildSettingsObj.MARKETING_VERSION = '1.0.0';
284
- buildSettingsObj.CURRENT_PROJECT_VERSION = '1';
285
- buildSettingsObj.ENABLE_BITCODE = 'NO';
286
- }
287
- }
288
-
289
- await fs.writeFile(projPath, xcodeProject.writeSync());
290
- });
291
-
292
- return props;
293
- });
294
-
295
- /**
296
- * Adds iOS VoIP background mode to keep the app running during calls
297
- */
298
- const withFishjamVoIPBackgroundMode: ConfigPlugin<FishjamPluginOptions> = (config, props) =>
299
- withInfoPlist(config, (configuration) => {
300
- if (props?.ios?.enableVoIPBackgroundMode) {
301
- const backgroundModes = new Set(configuration.modResults.UIBackgroundModes ?? []);
302
- backgroundModes.add('voip');
303
-
304
- configuration.modResults.UIBackgroundModes = Array.from(backgroundModes);
305
- }
306
-
307
- return configuration;
308
- });
309
-
310
- const withFishjamPictureInPicture: ConfigPlugin<FishjamPluginOptions> = (config, props) =>
311
- withInfoPlist(config, (configuration) => {
312
- if (props?.ios?.supportsPictureInPicture) {
313
- const backgroundModes = new Set(configuration.modResults.UIBackgroundModes ?? []);
314
- backgroundModes.add('audio');
315
- configuration.modResults.UIBackgroundModes = Array.from(backgroundModes);
316
- }
317
-
318
- return configuration;
319
- });
320
-
321
- const withFishjamIos: ConfigPlugin<FishjamPluginOptions> = (config, props) => {
322
- if (props?.ios?.enableScreensharing) {
323
- config = withAppGroupPermissions(config, props);
324
- config = withInfoPlistConstants(config, props);
325
- config = withFishjamSBE(config, props);
326
- }
327
- config = withPodfileProperties(config, (configuration) => {
328
- configuration.modResults['ios.deploymentTarget'] = props?.ios?.iphoneDeploymentTarget ?? IPHONEOS_DEPLOYMENT_TARGET;
329
- return configuration;
330
- });
331
- config = withFishjamPictureInPicture(config, props);
332
- config = withFishjamVoIPBackgroundMode(config, props);
333
- return config;
334
- };
335
-
336
- export { withFishjamIos };
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "expo-module-scripts/tsconfig.plugin",
3
- "compilerOptions": {
4
- "outDir": "build",
5
- "rootDir": "src"
6
- },
7
- "include": ["./src"],
8
- "exclude": ["**/__mocks__/*", "**/__tests__/*"]
9
- }
@@ -1 +0,0 @@
1
- {"root":["./src/types.ts","./src/withFishjam.ts","./src/withFishjamAndroid.ts","./src/withFishjamIos.ts"],"version":"5.8.3"}