From ae57c43823bbe5f27f5444dcfb9ae1b8cb286640 Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Wed, 26 Nov 2025 16:06:54 +0100
Subject: [PATCH 01/13] Implement screen sharing for live streams
---
.../fishjam-chat/navigators/AppNavigator.tsx | 9 ++
.../screens/ConnectToLivestreamScreen.tsx | 23 ++++
.../LivestreamScreenSharingScreen.tsx | 88 +++++++++++++
.../useLivestreamScreenSharingStreamer.ts | 116 ++++++++++++++++++
.../livestream/hooks/useLivestreamStreamer.ts | 3 +-
.../src/livestream/index.ts | 1 +
6 files changed, 238 insertions(+), 2 deletions(-)
create mode 100644 internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx
create mode 100644 packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
diff --git a/internal/fishjam-chat/navigators/AppNavigator.tsx b/internal/fishjam-chat/navigators/AppNavigator.tsx
index 388414997..0e240ffcc 100644
--- a/internal/fishjam-chat/navigators/AppNavigator.tsx
+++ b/internal/fishjam-chat/navigators/AppNavigator.tsx
@@ -15,6 +15,7 @@ import { ConnectWithFishjamRoom } from '../screens/ConnectWithFishjamRoom';
import ConnectToLivestreamScreen from '../screens/ConnectToLivestreamScreen';
import LivestreamStreamerScreen from '../screens/LivestreamScreen/LivestreamStreamerScreen';
import LivestreamViewerScreen from '../screens/LivestreamScreen/LivestreamViewerScreen';
+import LivestreamScreenSharingScreen from '../screens/LivestreamScreen/LivestreamScreenSharingScreen';
export type AppRootStackParamList = {
Home: undefined;
@@ -31,6 +32,10 @@ export type AppRootStackParamList = {
fishjamId: string;
roomName: string;
};
+ LivestreamScreenSharingScreen: {
+ fishjamId: string;
+ roomName: string;
+ };
Room: {
isCameraOn: boolean;
userName?: string;
@@ -144,6 +149,10 @@ export default function AppNavigator() {
name="LivestreamViewerScreen"
component={LivestreamViewerScreen}
/>
+
{
+ try {
+ validateInputs();
+ setConnectionError(null);
+ setLoading(true);
+
+ navigation.navigate('LivestreamScreenSharingScreen', {
+ fishjamId,
+ roomName,
+ });
+ } catch (e) {
+ const message =
+ 'message' in (e as Error) ? (e as Error).message : 'Unknown error';
+ setConnectionError(message);
+ } finally {
+ setLoading(false);
+ }
+ };
return (
@@ -109,6 +127,11 @@ export default function ConnectToLivestreamScreen({ navigation }: Props) {
onPress={onTapConnectStreamerButton}
disabled={loading}
/>
+
diff --git a/internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx b/internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx
new file mode 100644
index 000000000..c9538e65e
--- /dev/null
+++ b/internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx
@@ -0,0 +1,88 @@
+import { useSandbox } from '@fishjam-cloud/react-native-client';
+import {
+ LivestreamStreamer,
+ useLivestreamScreenSharingStreamer,
+ VideoParameters,
+} from '@fishjam-cloud/react-native-client/livestream';
+import type { NativeStackScreenProps } from '@react-navigation/native-stack';
+import React, { useCallback } from 'react';
+import { Button, SafeAreaView, StyleSheet, View } from 'react-native';
+import { AppRootStackParamList } from '../../navigators/AppNavigator';
+import { BrandColors } from '../../utils/Colors';
+
+type Props = NativeStackScreenProps<
+ AppRootStackParamList,
+ 'LivestreamScreenSharingScreen'
+>;
+
+export default function LivestreamScreenSharingScreen({ route }: Props) {
+ const { fishjamId, roomName } = route.params;
+
+ const { getSandboxLivestream } = useSandbox({
+ fishjamId,
+ });
+
+ const {
+ connect,
+ disconnect,
+ isConnected,
+ whipClientRef,
+ } = useLivestreamScreenSharingStreamer({
+ audioEnabled: true,
+ videoParameters: VideoParameters.presetHD169,
+ });
+
+ const handleConnect = useCallback(async () => {
+ try {
+ const { streamerToken } = await getSandboxLivestream(roomName, false);
+ await connect(streamerToken);
+ } catch (err) {
+ console.log(err);
+ }
+ }, [connect, getSandboxLivestream, roomName]);
+
+ const handleDisconnect = useCallback(() => {
+ disconnect();
+ }, [disconnect]);
+
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#F1FAFE',
+ padding: 24,
+ },
+ box: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ gap: 20,
+ },
+ videoView: {
+ width: '100%',
+ height: '70%',
+ backgroundColor: '#E0E0E0',
+ borderRadius: 12,
+ overflow: 'hidden',
+ borderWidth: 1,
+ borderColor: BrandColors.darkBlue80,
+ },
+ whepView: {
+ flex: 1,
+ backgroundColor: '#000',
+ },
+});
diff --git a/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
new file mode 100644
index 000000000..333681555
--- /dev/null
+++ b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
@@ -0,0 +1,116 @@
+import { useCallback, useRef, useEffect } from 'react';
+import {
+ SenderAudioCodecName,
+ SenderVideoCodecName,
+ VideoParameters,
+ WhipClientViewRef,
+ useWhipConnectionState,
+} from 'react-native-whip-whep';
+import { FISHJAM_WHIP_URL } from '../../consts';
+
+/**
+ * @category Livestream
+ */
+export interface useLivestreamScreenSharingStreamerResult {
+ /**
+ * Callback used to start publishing the selected audio and video media streams.
+ *
+ * @remarks
+ * Calling {@link connect} multiple times will have the effect of only publishing the **last** specified inputs.
+ */
+ connect: (token: string, urlOverride?: string) => Promise;
+ /** Callback to stop publishing anything previously published with {@link connect} */
+ disconnect: () => Promise;
+ /** Utility flag which indicates the current connection status */
+ isConnected: boolean;
+ /**
+ * Reference to the WhipClient instance. Needs to be passed to the {@link LivestreamScreenSharingStreamer} component.
+ */
+ whipClientRef: React.RefObject;
+}
+
+export type UseLivestreamScreenSharingStreamerParams = {
+ /**
+ * If audio track should be enabled.
+ * Defaults to true.
+ */
+ audioEnabled?: boolean;
+ /**
+ * Set video parameters for the camera
+ */
+ videoParameters?: VideoParameters;
+ /**
+ * Set the preferred video codecs for sending the video.
+ * Use {@link WhipClient.getSupportedVideoCodecs} to get the list of supported video codecs.
+ */
+ preferredVideoCodecs?: SenderVideoCodecName[];
+ /**
+ * Set the preferred audio codecs for sending the audio.
+ * Use {@link WhipClient.getSupportedAudioCodecs} to get the list of supported audio codecs.
+ */
+ preferredAudioCodecs?: SenderAudioCodecName[];
+};
+
+/**
+ * Hook for publishing a screen sharing livestream, which can be then received with {@link useLivestreamViewer}
+ * @category Livestream
+ * @group Hooks
+ */
+export const useLivestreamScreenSharingStreamer = ({
+ audioEnabled,
+ videoParameters,
+ preferredVideoCodecs,
+ preferredAudioCodecs,
+}: UseLivestreamScreenSharingStreamerParams = {}): useLivestreamScreenSharingStreamerResult => {
+ const state = useWhipConnectionState();
+ const isConnected = state === 'connected';
+
+ const whipClientRef = useRef(null);
+
+ const connect = useCallback(async (token: string, urlOverride?: string) => {
+ const resolvedUrl = urlOverride ?? FISHJAM_WHIP_URL;
+
+ await whipClientRef.current?.connect({
+ authToken: token,
+ serverUrl: resolvedUrl,
+ });
+ }, []);
+
+ const disconnect = useCallback(async () => {
+ await whipClientRef.current?.disconnect();
+ }, []);
+
+ useEffect(() => {
+ try {
+ const initializeScreenShare = async () => {
+ await whipClientRef.current?.initializeScreenShare({
+ audioEnabled: audioEnabled ?? true,
+ videoEnabled: true,
+ videoParameters: videoParameters,
+ preferredVideoCodecs: preferredVideoCodecs,
+ preferredAudioCodecs: preferredAudioCodecs,
+ });
+ };
+ initializeScreenShare();
+ } catch {
+ console.error('Failed to initialize screen share');
+ }
+ const ref = whipClientRef.current;
+ return () => {
+ ref?.disconnect();
+ };
+ }, [
+ videoParameters,
+ audioEnabled,
+ preferredVideoCodecs,
+ preferredAudioCodecs,
+ whipClientRef,
+ ]);
+
+ return {
+ connect,
+ disconnect,
+ isConnected,
+ whipClientRef,
+ };
+};
diff --git a/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts b/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts
index e9cd49a42..d7126d444 100644
--- a/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts
+++ b/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts
@@ -117,11 +117,10 @@ export const useLivestreamStreamer = ({
await whipClientRef.current?.initializeCamera({
audioEnabled: audioEnabled ?? true,
videoEnabled: videoEnabled ?? true,
- videoDeviceId: camera?.id ?? cameras[0].id,
videoParameters: videoParameters,
preferredVideoCodecs: preferredVideoCodecs,
preferredAudioCodecs: preferredAudioCodecs,
- });
+ }, camera?.id ?? cameras[0].id);
};
initializeCamera();
} catch {
diff --git a/packages/react-native-client/src/livestream/index.ts b/packages/react-native-client/src/livestream/index.ts
index 5c24c2fdb..261bdc608 100644
--- a/packages/react-native-client/src/livestream/index.ts
+++ b/packages/react-native-client/src/livestream/index.ts
@@ -2,6 +2,7 @@ export { LivestreamStreamer } from './components/LivestreamStreamer';
export { LivestreamViewer } from './components/LivestreamViewer';
export { useLivestreamViewer } from './hooks/useLivestreamViewer';
export { useLivestreamStreamer } from './hooks/useLivestreamStreamer';
+export { useLivestreamScreenSharingStreamer } from './hooks/useLivestreamScreenSharingStreamer';
export { cameras, VideoParameters } from 'react-native-whip-whep';
export type { LivestreamStreamerProps } from './components/LivestreamStreamer';
export type { ConnectViewerConfig } from './hooks/useLivestreamViewer';
From 6609231ffb0ad485c970a51f723865a20771ed10 Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Fri, 28 Nov 2025 15:03:24 +0100
Subject: [PATCH 02/13] Fix linter issues
---
.../LivestreamScreenSharingScreen.tsx | 14 +++++---------
.../livestream/hooks/useLivestreamStreamer.ts | 17 ++++++++++-------
2 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx b/internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx
index c9538e65e..63850724b 100644
--- a/internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx
+++ b/internal/fishjam-chat/screens/LivestreamScreen/LivestreamScreenSharingScreen.tsx
@@ -22,15 +22,11 @@ export default function LivestreamScreenSharingScreen({ route }: Props) {
fishjamId,
});
- const {
- connect,
- disconnect,
- isConnected,
- whipClientRef,
- } = useLivestreamScreenSharingStreamer({
- audioEnabled: true,
- videoParameters: VideoParameters.presetHD169,
- });
+ const { connect, disconnect, isConnected, whipClientRef } =
+ useLivestreamScreenSharingStreamer({
+ audioEnabled: true,
+ videoParameters: VideoParameters.presetHD169,
+ });
const handleConnect = useCallback(async () => {
try {
diff --git a/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts b/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts
index d7126d444..39a368a9e 100644
--- a/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts
+++ b/packages/react-native-client/src/livestream/hooks/useLivestreamStreamer.ts
@@ -114,13 +114,16 @@ export const useLivestreamStreamer = ({
useEffect(() => {
try {
const initializeCamera = async () => {
- await whipClientRef.current?.initializeCamera({
- audioEnabled: audioEnabled ?? true,
- videoEnabled: videoEnabled ?? true,
- videoParameters: videoParameters,
- preferredVideoCodecs: preferredVideoCodecs,
- preferredAudioCodecs: preferredAudioCodecs,
- }, camera?.id ?? cameras[0].id);
+ await whipClientRef.current?.initializeCamera(
+ {
+ audioEnabled: audioEnabled ?? true,
+ videoEnabled: videoEnabled ?? true,
+ videoParameters: videoParameters,
+ preferredVideoCodecs: preferredVideoCodecs,
+ preferredAudioCodecs: preferredAudioCodecs,
+ },
+ camera?.id ?? cameras[0].id,
+ );
};
initializeCamera();
} catch {
From d468f0ca7db55df62dab04f96bbbaf80fc4cd0a0 Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Thu, 4 Dec 2025 13:20:25 +0100
Subject: [PATCH 03/13] Use the whip/whep config
---
internal/fishjam-chat/app.json | 10 ++
.../react-native-client/plugin/src/types.ts | 10 ++
.../plugin/src/withFishjam.ts | 101 +++++++++++++++++-
3 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/internal/fishjam-chat/app.json b/internal/fishjam-chat/app.json
index c2c7cca70..3fa418551 100644
--- a/internal/fishjam-chat/app.json
+++ b/internal/fishjam-chat/app.json
@@ -69,6 +69,16 @@
"enableScreensharing": true,
"enableVoIPBackgroundMode": true,
"supportsPictureInPicture": true
+ },
+ "livestream": {
+ "android": {
+ "enableScreensharing": true,
+ "supportsPictureInPicture": true
+ },
+ "ios": {
+ "enableScreensharing": true,
+ "supportsPictureInPicture": true
+ }
}
}
],
diff --git a/packages/react-native-client/plugin/src/types.ts b/packages/react-native-client/plugin/src/types.ts
index adb207f68..9484dbbe1 100644
--- a/packages/react-native-client/plugin/src/types.ts
+++ b/packages/react-native-client/plugin/src/types.ts
@@ -13,5 +13,15 @@ export type FishjamPluginOptions =
mainTargetName?: string;
broadcastExtensionTargetName?: string;
};
+ livestream?: {
+ android?: {
+ enableScreensharing?: boolean;
+ supportsPictureInPicture?: boolean;
+ };
+ ios?: {
+ enableScreensharing?: boolean;
+ supportsPictureInPicture?: boolean;
+ };
+ };
}
| undefined;
diff --git a/packages/react-native-client/plugin/src/withFishjam.ts b/packages/react-native-client/plugin/src/withFishjam.ts
index cb0d8079f..17fd32f7a 100644
--- a/packages/react-native-client/plugin/src/withFishjam.ts
+++ b/packages/react-native-client/plugin/src/withFishjam.ts
@@ -3,9 +3,106 @@ import { withFishjamAndroid } from './withFishjamAndroid';
import { FishjamPluginOptions } from './types';
import withFishjamIos from './withFishjamIos';
+/**
+ * Main Fishjam Expo config plugin.
+ *
+ * This plugin configures both iOS and Android platforms for Fishjam video calls
+ * and optionally for livestreaming functionality.
+ *
+ * ## Basic Usage (Video Calls)
+ *
+ * ```json
+ * {
+ * "plugins": [
+ * [
+ * "@fishjam-cloud/react-native-client",
+ * {
+ * "android": {
+ * "enableForegroundService": true,
+ * "supportsPictureInPicture": true
+ * },
+ * "ios": {
+ * "enableScreensharing": true,
+ * "enableVoIPBackgroundMode": true,
+ * "supportsPictureInPicture": true
+ * }
+ * }
+ * ]
+ * ]
+ * }
+ * ```
+ *
+ * ## Livestreaming Support
+ *
+ * If you're using the livestream functionality (WHIP/WHEP), add the `livestream` configuration.
+ * This will automatically configure the underlying `react-native-whip-whep` package:
+ *
+ * ```json
+ * {
+ * "plugins": [
+ * [
+ * "@fishjam-cloud/react-native-client",
+ * {
+ * "android": {
+ * "enableForegroundService": true,
+ * "supportsPictureInPicture": true
+ * },
+ * "ios": {
+ * "enableScreensharing": true,
+ * "enableVoIPBackgroundMode": true,
+ * "supportsPictureInPicture": true
+ * },
+ * "livestream": {
+ * "android": {
+ * "enableScreensharing": true,
+ * "supportsPictureInPicture": true
+ * },
+ * "ios": {
+ * "enableScreensharing": true,
+ * "supportsPictureInPicture": true
+ * }
+ * }
+ * }
+ * ]
+ * ]
+ * }
+ * ```
+ *
+ * The `livestream` configuration adds:
+ * - **Android**: ScreenCaptureService for screen sharing in livestreams
+ * - **iOS**: Broadcast extension for screen sharing in livestreams
+ *
+ * @param config - Expo config object
+ * @param options - Plugin configuration options
+ * @returns Modified config object
+ */
const withFishjam: ConfigPlugin = (config, options) => {
- withFishjamAndroid(config, options);
- withFishjamIos(config, options);
+ config = withFishjamAndroid(config, options);
+ config = withFishjamIos(config, options);
+
+ if (options?.livestream) {
+ try {
+ const withWhipWhep = require('react-native-whip-whep/plugin/build/withWhipWhep').default;
+
+ // Fishjam's iOS screensharing is enabled, disable whip-whep's screensharing
+ // This ensures only one broadcast extension is created (Fishjam's FishjamScreenBroadcastExtension)
+ // to avoid duplicate broadcast extension targets in the Xcode project
+ if (options?.ios?.enableScreensharing) {
+ options.livestream.ios = {
+ ...options.livestream.ios,
+ enableScreensharing: false,
+ };
+ }
+
+ config = withWhipWhep(config, options.livestream);
+ } catch (error) {
+ console.warn(
+ '[Fishjam] react-native-whip-whep plugin not found. Livestream configuration will be skipped. ' +
+ 'Make sure react-native-whip-whep is installed if you need livestream functionality.',
+ );
+ }
+ }
+
return config;
};
From 0aeafb3f1921917e5266341e67e0c13eb959c44a Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Thu, 4 Dec 2025 13:20:51 +0100
Subject: [PATCH 04/13] Fix the webRTC network monitoring crash on android
---
.../client/webrtc/PeerConnectionFactoryWrapper.kt | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt b/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt
index a32263fc3..a88663760 100644
--- a/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt
+++ b/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt
@@ -34,7 +34,10 @@ internal class PeerConnectionFactoryWrapper(
init {
PeerConnectionFactory.initialize(
- PeerConnectionFactory.InitializationOptions.builder(appContext).createInitializationOptions()
+ PeerConnectionFactory.InitializationOptions.builder(appContext)
+ .setFieldTrials("WebRTC-Network-UseNWPathMonitor/Disabled/")
+ .setEnableInternalTracer(false)
+ .createInitializationOptions()
)
eglBase = EglBase.create()
From 3ccf9d9db6757c1dae40caf08120a2a66cd045ee Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Thu, 4 Dec 2025 13:33:58 +0100
Subject: [PATCH 05/13] Lint
---
.../client/webrtc/PeerConnectionFactoryWrapper.kt | 3 ++-
packages/react-native-client/plugin/src/withFishjam.ts | 10 ++++++----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt b/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt
index a88663760..f4f4f1e90 100644
--- a/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt
+++ b/packages/android-client/FishjamClient/src/main/java/com/fishjamcloud/client/webrtc/PeerConnectionFactoryWrapper.kt
@@ -34,7 +34,8 @@ internal class PeerConnectionFactoryWrapper(
init {
PeerConnectionFactory.initialize(
- PeerConnectionFactory.InitializationOptions.builder(appContext)
+ PeerConnectionFactory.InitializationOptions
+ .builder(appContext)
.setFieldTrials("WebRTC-Network-UseNWPathMonitor/Disabled/")
.setEnableInternalTracer(false)
.createInitializationOptions()
diff --git a/packages/react-native-client/plugin/src/withFishjam.ts b/packages/react-native-client/plugin/src/withFishjam.ts
index 17fd32f7a..64a647a3e 100644
--- a/packages/react-native-client/plugin/src/withFishjam.ts
+++ b/packages/react-native-client/plugin/src/withFishjam.ts
@@ -82,8 +82,10 @@ const withFishjam: ConfigPlugin = (config, options) => {
if (options?.livestream) {
try {
- const withWhipWhep = require('react-native-whip-whep/plugin/build/withWhipWhep').default;
-
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const withWhipWhep =
+ require('react-native-whip-whep/plugin/build/withWhipWhep').default;
+
// Fishjam's iOS screensharing is enabled, disable whip-whep's screensharing
// This ensures only one broadcast extension is created (Fishjam's FishjamScreenBroadcastExtension)
// to avoid duplicate broadcast extension targets in the Xcode project
@@ -93,9 +95,9 @@ const withFishjam: ConfigPlugin = (config, options) => {
enableScreensharing: false,
};
}
-
+
config = withWhipWhep(config, options.livestream);
- } catch (error) {
+ } catch {
console.warn(
'[Fishjam] react-native-whip-whep plugin not found. Livestream configuration will be skipped. ' +
'Make sure react-native-whip-whep is installed if you need livestream functionality.',
From 0dbccb626d8a80968a81b09b4a644246f2401222 Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Thu, 4 Dec 2025 13:59:36 +0100
Subject: [PATCH 06/13] Fix lint issues and CR suggestions
---
packages/react-native-client/plugin/src/withFishjam.ts | 2 +-
.../livestream/hooks/useLivestreamScreenSharingStreamer.ts | 5 ++---
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/packages/react-native-client/plugin/src/withFishjam.ts b/packages/react-native-client/plugin/src/withFishjam.ts
index 64a647a3e..f44efa0f7 100644
--- a/packages/react-native-client/plugin/src/withFishjam.ts
+++ b/packages/react-native-client/plugin/src/withFishjam.ts
@@ -82,8 +82,8 @@ const withFishjam: ConfigPlugin = (config, options) => {
if (options?.livestream) {
try {
- // eslint-disable-next-line @typescript-eslint/no-require-imports
const withWhipWhep =
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
require('react-native-whip-whep/plugin/build/withWhipWhep').default;
// Fishjam's iOS screensharing is enabled, disable whip-whep's screensharing
diff --git a/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
index 333681555..c0507af54 100644
--- a/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
+++ b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
@@ -92,8 +92,8 @@ export const useLivestreamScreenSharingStreamer = ({
});
};
initializeScreenShare();
- } catch {
- console.error('Failed to initialize screen share');
+ } catch (error) {
+ console.error('Failed to initialize screen share:', error);
}
const ref = whipClientRef.current;
return () => {
@@ -104,7 +104,6 @@ export const useLivestreamScreenSharingStreamer = ({
audioEnabled,
preferredVideoCodecs,
preferredAudioCodecs,
- whipClientRef,
]);
return {
From 85e4e0849185d5447faa45afda29e6998bdcda69 Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Thu, 4 Dec 2025 15:14:13 +0100
Subject: [PATCH 07/13] CR improvements
---
packages/react-native-client/plugin/src/withFishjam.ts | 3 +--
.../livestream/hooks/useLivestreamScreenSharingStreamer.ts | 4 ++--
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/packages/react-native-client/plugin/src/withFishjam.ts b/packages/react-native-client/plugin/src/withFishjam.ts
index f44efa0f7..b09619f0d 100644
--- a/packages/react-native-client/plugin/src/withFishjam.ts
+++ b/packages/react-native-client/plugin/src/withFishjam.ts
@@ -99,8 +99,7 @@ const withFishjam: ConfigPlugin = (config, options) => {
config = withWhipWhep(config, options.livestream);
} catch {
console.warn(
- '[Fishjam] react-native-whip-whep plugin not found. Livestream configuration will be skipped. ' +
- 'Make sure react-native-whip-whep is installed if you need livestream functionality.',
+ '[Fishjam] react-native-whip-whep plugin not found. Livestream configuration will be skipped.',
);
}
}
diff --git a/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
index c0507af54..aee6b7a57 100644
--- a/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
+++ b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
@@ -24,7 +24,7 @@ export interface useLivestreamScreenSharingStreamerResult {
/** Utility flag which indicates the current connection status */
isConnected: boolean;
/**
- * Reference to the WhipClient instance. Needs to be passed to the {@link LivestreamScreenSharingStreamer} component.
+ * Reference to the WhipClient instance. Needs to be passed to the {@link LivestreamStreamer} component.
*/
whipClientRef: React.RefObject;
}
@@ -36,7 +36,7 @@ export type UseLivestreamScreenSharingStreamerParams = {
*/
audioEnabled?: boolean;
/**
- * Set video parameters for the camera
+ * Set video parameters for the screen share stream.
*/
videoParameters?: VideoParameters;
/**
From 112be4c3b94a0034fe866214ea1d086a4eece396 Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Tue, 16 Dec 2025 15:45:15 +0100
Subject: [PATCH 08/13] Update whip-whep version
---
packages/react-native-client/package.json | 2 +-
yarn.lock | 11 ++++++-----
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/packages/react-native-client/package.json b/packages/react-native-client/package.json
index 6a4906d27..fd35d005b 100644
--- a/packages/react-native-client/package.json
+++ b/packages/react-native-client/package.json
@@ -42,7 +42,7 @@
"dependencies": {
"promise-fs": "^2.1.1",
"protobufjs": "^7.4.0",
- "react-native-whip-whep": "0.6.0",
+ "react-native-whip-whep": "0.7.1",
"zod": "^3.25.71"
},
"devDependencies": {
diff --git a/yarn.lock b/yarn.lock
index a924d5baf..8af8ddc70 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3151,7 +3151,7 @@ __metadata:
promise-fs: "npm:^2.1.1"
protobufjs: "npm:^7.4.0"
react-dom: "npm:19.0.0"
- react-native-whip-whep: "npm:0.6.0"
+ react-native-whip-whep: "npm:0.7.1"
ts-proto: "npm:^2.7.5"
typedoc: "npm:^0.28.7"
typedoc-plugin-mark-react-functional-components: "npm:^0.2.2"
@@ -16308,16 +16308,17 @@ __metadata:
languageName: node
linkType: hard
-"react-native-whip-whep@npm:0.6.0":
- version: 0.6.0
- resolution: "react-native-whip-whep@npm:0.6.0"
+"react-native-whip-whep@npm:0.7.1":
+ version: 0.7.1
+ resolution: "react-native-whip-whep@npm:0.7.1"
dependencies:
+ promise-fs: "npm:^2.1.1"
zod: "npm:^3.25.71"
peerDependencies:
expo: "*"
react: "*"
react-native: "*"
- checksum: 10c0/e5efdf8c1ce911b300960ce028984a0d14dca3770d417a0f32cf07ec9b2805857d98e1a943d68b1a46176458e56901a031b713639e92c0a86f7f76e7344d16dd
+ checksum: 10c0/f50327955c765dd9272b39691173c74291488f6c71ac9fc5134edbf671911809dca30f59363ce94bb9d8c991ba05e58b1cd58dcf7552e6e00ac1da1c3a3ae56a
languageName: node
linkType: hard
From 522b125c0dc6d878ec2765ba79271184598a599a Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Wed, 17 Dec 2025 11:33:08 +0100
Subject: [PATCH 09/13] Use correct expo/metro-config version
---
package.json | 3 +++
yarn.lock | 53 +++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 51 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index 3306742c1..d03d5deb4 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,9 @@
"examples/minimal-react-native"
]
},
+ "resolutions": {
+ "@expo/metro-config": "~0.20.18"
+ },
"devDependencies": {
"typedoc": "^0.28.7",
"typedoc-material-theme": "^1.4.0",
diff --git a/yarn.lock b/yarn.lock
index 8af8ddc70..eb54a24b0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2860,6 +2860,28 @@ __metadata:
languageName: node
linkType: hard
+"@expo/config-plugins@npm:~10.1.2":
+ version: 10.1.2
+ resolution: "@expo/config-plugins@npm:10.1.2"
+ dependencies:
+ "@expo/config-types": "npm:^53.0.5"
+ "@expo/json-file": "npm:~9.1.5"
+ "@expo/plist": "npm:^0.3.5"
+ "@expo/sdk-runtime-versions": "npm:^1.0.0"
+ chalk: "npm:^4.1.2"
+ debug: "npm:^4.3.5"
+ getenv: "npm:^2.0.0"
+ glob: "npm:^10.4.2"
+ resolve-from: "npm:^5.0.0"
+ semver: "npm:^7.5.4"
+ slash: "npm:^3.0.0"
+ slugify: "npm:^1.6.6"
+ xcode: "npm:^3.0.1"
+ xml2js: "npm:0.6.0"
+ checksum: 10c0/d5ef0f002db40cb182058b2fe9df6f5f77ff09e18aa0bc8109047d75cd912487bace59bcff7104c6f68f6b49f89d0b387ab6f90f8069c63c9f3fccb9fb9b99de
+ languageName: node
+ linkType: hard
+
"@expo/config-types@npm:^53.0.5":
version: 53.0.5
resolution: "@expo/config-types@npm:53.0.5"
@@ -2888,6 +2910,27 @@ __metadata:
languageName: node
linkType: hard
+"@expo/config@npm:~11.0.13":
+ version: 11.0.13
+ resolution: "@expo/config@npm:11.0.13"
+ dependencies:
+ "@babel/code-frame": "npm:~7.10.4"
+ "@expo/config-plugins": "npm:~10.1.2"
+ "@expo/config-types": "npm:^53.0.5"
+ "@expo/json-file": "npm:^9.1.5"
+ deepmerge: "npm:^4.3.1"
+ getenv: "npm:^2.0.0"
+ glob: "npm:^10.4.2"
+ require-from-string: "npm:^2.0.2"
+ resolve-from: "npm:^5.0.0"
+ resolve-workspace-root: "npm:^2.0.0"
+ semver: "npm:^7.6.0"
+ slugify: "npm:^1.3.4"
+ sucrase: "npm:3.35.0"
+ checksum: 10c0/19cdbc4baa498ca9e55416fd1b2a202cca061e34984236b1f032f3d28cf72a4ddc824bc0cbe3d39c5b5f1117ef65be84c4b05bf62b6fa41d5d049b75af59a17c
+ languageName: node
+ linkType: hard
+
"@expo/devcert@npm:^1.1.2":
version: 1.1.4
resolution: "@expo/devcert@npm:1.1.4"
@@ -2970,15 +3013,15 @@ __metadata:
languageName: node
linkType: hard
-"@expo/metro-config@npm:0.20.17, @expo/metro-config@npm:~0.20.17":
- version: 0.20.17
- resolution: "@expo/metro-config@npm:0.20.17"
+"@expo/metro-config@npm:~0.20.18":
+ version: 0.20.18
+ resolution: "@expo/metro-config@npm:0.20.18"
dependencies:
"@babel/core": "npm:^7.20.0"
"@babel/generator": "npm:^7.20.5"
"@babel/parser": "npm:^7.20.0"
"@babel/types": "npm:^7.20.0"
- "@expo/config": "npm:~11.0.12"
+ "@expo/config": "npm:~11.0.13"
"@expo/env": "npm:~1.0.7"
"@expo/json-file": "npm:~9.1.5"
"@expo/spawn-async": "npm:^1.7.2"
@@ -2993,7 +3036,7 @@ __metadata:
minimatch: "npm:^9.0.0"
postcss: "npm:~8.4.32"
resolve-from: "npm:^5.0.0"
- checksum: 10c0/d3bb092635e2311a4be162966d87da484fa3543a4ac2640855ede2a44266e563411b352db8e95c1f5ce29387f24c85e2606bbda23bb932d00d3010d256446d0c
+ checksum: 10c0/91b45c399d7ec01d646013b6cd2116667721da53bc607c6c1df35203b816284555c7e13a079f51e29176e86c1058a70020f7900cf6a50b4bdc9e7cc29e0edb2a
languageName: node
linkType: hard
From d33ffe9151ceea03aa4df265980c6b5b7a3e5bb6 Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Wed, 17 Dec 2025 11:33:30 +0100
Subject: [PATCH 10/13] Catch initializeScreenshare errors correctly
---
.../livestream/hooks/useLivestreamScreenSharingStreamer.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
index aee6b7a57..f96f22b95 100644
--- a/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
+++ b/packages/react-native-client/src/livestream/hooks/useLivestreamScreenSharingStreamer.ts
@@ -91,7 +91,9 @@ export const useLivestreamScreenSharingStreamer = ({
preferredAudioCodecs: preferredAudioCodecs,
});
};
- initializeScreenShare();
+ initializeScreenShare().catch((error) => {
+ console.error('Failed to initialize screen share:', error);
+ });
} catch (error) {
console.error('Failed to initialize screen share:', error);
}
From 453a3352e71471fd732dfdfb6bb718ff375c9097 Mon Sep 17 00:00:00 2001
From: Milosz Filimowski
Date: Wed, 17 Dec 2025 15:18:34 +0100
Subject: [PATCH 11/13] bump example packages
---
internal/fishjam-chat/package.json | 4 +-
package.json | 3 -
yarn.lock | 471 ++++++++++++++++++++++++++++-
3 files changed, 470 insertions(+), 8 deletions(-)
diff --git a/internal/fishjam-chat/package.json b/internal/fishjam-chat/package.json
index 5e6669d09..35b0b3a2a 100644
--- a/internal/fishjam-chat/package.json
+++ b/internal/fishjam-chat/package.json
@@ -20,12 +20,12 @@
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
"@react-navigation/native-stack": "^6.11.0",
- "expo": "53.0.17",
+ "expo": "53.0.25",
"expo-build-properties": "~0.14.8",
"expo-device": "~7.1.4",
"expo-status-bar": "~2.2.3",
"react": "19.0.0",
- "react-native": "0.79.5",
+ "react-native": "0.79.6",
"react-native-gesture-handler": "~2.24.0",
"react-native-reanimated": "~3.17.4",
"react-native-safe-area-context": "5.4.0",
diff --git a/package.json b/package.json
index d03d5deb4..3306742c1 100644
--- a/package.json
+++ b/package.json
@@ -19,9 +19,6 @@
"examples/minimal-react-native"
]
},
- "resolutions": {
- "@expo/metro-config": "~0.20.18"
- },
"devDependencies": {
"typedoc": "^0.28.7",
"typedoc-material-theme": "^1.4.0",
diff --git a/yarn.lock b/yarn.lock
index eb54a24b0..7b0dff804 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2828,6 +2828,78 @@ __metadata:
languageName: node
linkType: hard
+"@expo/cli@npm:0.24.23":
+ version: 0.24.23
+ resolution: "@expo/cli@npm:0.24.23"
+ dependencies:
+ "@0no-co/graphql.web": "npm:^1.0.8"
+ "@babel/runtime": "npm:^7.20.0"
+ "@expo/code-signing-certificates": "npm:^0.0.5"
+ "@expo/config": "npm:~11.0.13"
+ "@expo/config-plugins": "npm:~10.1.2"
+ "@expo/devcert": "npm:^1.1.2"
+ "@expo/env": "npm:~1.0.7"
+ "@expo/image-utils": "npm:^0.7.6"
+ "@expo/json-file": "npm:^9.1.5"
+ "@expo/metro-config": "npm:~0.20.18"
+ "@expo/osascript": "npm:^2.2.5"
+ "@expo/package-manager": "npm:^1.8.6"
+ "@expo/plist": "npm:^0.3.5"
+ "@expo/prebuild-config": "npm:^9.0.12"
+ "@expo/schema-utils": "npm:^0.1.0"
+ "@expo/spawn-async": "npm:^1.7.2"
+ "@expo/ws-tunnel": "npm:^1.0.1"
+ "@expo/xcpretty": "npm:^4.3.0"
+ "@react-native/dev-middleware": "npm:0.79.6"
+ "@urql/core": "npm:^5.0.6"
+ "@urql/exchange-retry": "npm:^1.3.0"
+ accepts: "npm:^1.3.8"
+ arg: "npm:^5.0.2"
+ better-opn: "npm:~3.0.2"
+ bplist-creator: "npm:0.1.0"
+ bplist-parser: "npm:^0.3.1"
+ chalk: "npm:^4.0.0"
+ ci-info: "npm:^3.3.0"
+ compression: "npm:^1.7.4"
+ connect: "npm:^3.7.0"
+ debug: "npm:^4.3.4"
+ env-editor: "npm:^0.4.1"
+ freeport-async: "npm:^2.0.0"
+ getenv: "npm:^2.0.0"
+ glob: "npm:^10.4.2"
+ lan-network: "npm:^0.1.6"
+ minimatch: "npm:^9.0.0"
+ node-forge: "npm:^1.3.1"
+ npm-package-arg: "npm:^11.0.0"
+ ora: "npm:^3.4.0"
+ picomatch: "npm:^3.0.1"
+ pretty-bytes: "npm:^5.6.0"
+ pretty-format: "npm:^29.7.0"
+ progress: "npm:^2.0.3"
+ prompts: "npm:^2.3.2"
+ qrcode-terminal: "npm:0.11.0"
+ require-from-string: "npm:^2.0.2"
+ requireg: "npm:^0.2.2"
+ resolve: "npm:^1.22.2"
+ resolve-from: "npm:^5.0.0"
+ resolve.exports: "npm:^2.0.3"
+ semver: "npm:^7.6.0"
+ send: "npm:^0.19.0"
+ slugify: "npm:^1.3.4"
+ source-map-support: "npm:~0.5.21"
+ stacktrace-parser: "npm:^0.1.10"
+ structured-headers: "npm:^0.4.1"
+ tar: "npm:^7.4.3"
+ terminal-link: "npm:^2.1.1"
+ undici: "npm:^6.18.2"
+ wrap-ansi: "npm:^7.0.0"
+ ws: "npm:^8.12.1"
+ bin:
+ expo-internal: build/bin/cli
+ checksum: 10c0/7ec738056ee4421d14a559e4563c9b3b050f9b57616f84c7c7acff533126d3bf653c68d07cb476f914b0a7a5619dd7b6c5cdc3317e6a423344cf59522e95751a
+ languageName: node
+ linkType: hard
+
"@expo/code-signing-certificates@npm:^0.0.5":
version: 0.0.5
resolution: "@expo/code-signing-certificates@npm:0.0.5"
@@ -3013,7 +3085,34 @@ __metadata:
languageName: node
linkType: hard
-"@expo/metro-config@npm:~0.20.18":
+"@expo/metro-config@npm:0.20.17":
+ version: 0.20.17
+ resolution: "@expo/metro-config@npm:0.20.17"
+ dependencies:
+ "@babel/core": "npm:^7.20.0"
+ "@babel/generator": "npm:^7.20.5"
+ "@babel/parser": "npm:^7.20.0"
+ "@babel/types": "npm:^7.20.0"
+ "@expo/config": "npm:~11.0.12"
+ "@expo/env": "npm:~1.0.7"
+ "@expo/json-file": "npm:~9.1.5"
+ "@expo/spawn-async": "npm:^1.7.2"
+ chalk: "npm:^4.1.0"
+ debug: "npm:^4.3.2"
+ dotenv: "npm:~16.4.5"
+ dotenv-expand: "npm:~11.0.6"
+ getenv: "npm:^2.0.0"
+ glob: "npm:^10.4.2"
+ jsc-safe-url: "npm:^0.2.4"
+ lightningcss: "npm:~1.27.0"
+ minimatch: "npm:^9.0.0"
+ postcss: "npm:~8.4.32"
+ resolve-from: "npm:^5.0.0"
+ checksum: 10c0/d3bb092635e2311a4be162966d87da484fa3543a4ac2640855ede2a44266e563411b352db8e95c1f5ce29387f24c85e2606bbda23bb932d00d3010d256446d0c
+ languageName: node
+ linkType: hard
+
+"@expo/metro-config@npm:0.20.18, @expo/metro-config@npm:~0.20.17, @expo/metro-config@npm:~0.20.18":
version: 0.20.18
resolution: "@expo/metro-config@npm:0.20.18"
dependencies:
@@ -3104,6 +3203,31 @@ __metadata:
languageName: node
linkType: hard
+"@expo/prebuild-config@npm:^9.0.12":
+ version: 9.0.12
+ resolution: "@expo/prebuild-config@npm:9.0.12"
+ dependencies:
+ "@expo/config": "npm:~11.0.13"
+ "@expo/config-plugins": "npm:~10.1.2"
+ "@expo/config-types": "npm:^53.0.5"
+ "@expo/image-utils": "npm:^0.7.6"
+ "@expo/json-file": "npm:^9.1.5"
+ "@react-native/normalize-colors": "npm:0.79.6"
+ debug: "npm:^4.3.1"
+ resolve-from: "npm:^5.0.0"
+ semver: "npm:^7.6.0"
+ xml2js: "npm:0.6.0"
+ checksum: 10c0/7b0f8a9898e8a6d7cc1d15c6a4397ee8e6f3d7c1529eaed4e2c2fb764428820a35fc93aa93a521d286528e9eb52f9ea7c41f12be38a0bc53faa26fac3b063921
+ languageName: node
+ linkType: hard
+
+"@expo/schema-utils@npm:^0.1.0":
+ version: 0.1.8
+ resolution: "@expo/schema-utils@npm:0.1.8"
+ checksum: 10c0/9a600ac858bcd1bd24ccac3e86cbef996c2c58cb20ce61fb1fc753f36dce4a000510e61b803ad5cb221a16caa38b54b243f08ac08e0de69e4aa556798d877f02
+ languageName: node
+ linkType: hard
+
"@expo/sdk-runtime-versions@npm:^1.0.0":
version: 1.0.0
resolution: "@expo/sdk-runtime-versions@npm:1.0.0"
@@ -3243,13 +3367,13 @@ __metadata:
eslint-config-expo: "npm:~9.2.0"
eslint-config-prettier: "npm:^10.1.5"
eslint-plugin-prettier: "npm:^5.5.1"
- expo: "npm:53.0.17"
+ expo: "npm:53.0.25"
expo-build-properties: "npm:~0.14.8"
expo-device: "npm:~7.1.4"
expo-status-bar: "npm:~2.2.3"
prettier: "npm:^3.6.2"
react: "npm:19.0.0"
- react-native: "npm:0.79.5"
+ react-native: "npm:0.79.6"
react-native-gesture-handler: "npm:~2.24.0"
react-native-reanimated: "npm:~3.17.4"
react-native-safe-area-context: "npm:5.4.0"
@@ -4586,6 +4710,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/assets-registry@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/assets-registry@npm:0.79.6"
+ checksum: 10c0/2edab4c2ba6c58f7e79ccb1b8d5588a302cfc38cdf62201ce51ae4b402fce9999ac1c882f72d1d4d9e1d7229ba721edcd24a2ef1800f50981dc3679d5befa1eb
+ languageName: node
+ linkType: hard
+
"@react-native/babel-plugin-codegen@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/babel-plugin-codegen@npm:0.79.5"
@@ -4596,6 +4727,16 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/babel-plugin-codegen@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/babel-plugin-codegen@npm:0.79.6"
+ dependencies:
+ "@babel/traverse": "npm:^7.25.3"
+ "@react-native/codegen": "npm:0.79.6"
+ checksum: 10c0/dc1e22b31db6e11ab1444ca607ab2cf9430ba0b09aa0f1819204b04dd6b151b254b7605bb3f0c2dc1b4d5fdfbf96dd8f8d92e60251dc1dc08b1fa54deb6b1614
+ languageName: node
+ linkType: hard
+
"@react-native/babel-preset@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/babel-preset@npm:0.79.5"
@@ -4651,6 +4792,61 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/babel-preset@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/babel-preset@npm:0.79.6"
+ dependencies:
+ "@babel/core": "npm:^7.25.2"
+ "@babel/plugin-proposal-export-default-from": "npm:^7.24.7"
+ "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3"
+ "@babel/plugin-syntax-export-default-from": "npm:^7.24.7"
+ "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3"
+ "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3"
+ "@babel/plugin-transform-arrow-functions": "npm:^7.24.7"
+ "@babel/plugin-transform-async-generator-functions": "npm:^7.25.4"
+ "@babel/plugin-transform-async-to-generator": "npm:^7.24.7"
+ "@babel/plugin-transform-block-scoping": "npm:^7.25.0"
+ "@babel/plugin-transform-class-properties": "npm:^7.25.4"
+ "@babel/plugin-transform-classes": "npm:^7.25.4"
+ "@babel/plugin-transform-computed-properties": "npm:^7.24.7"
+ "@babel/plugin-transform-destructuring": "npm:^7.24.8"
+ "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2"
+ "@babel/plugin-transform-for-of": "npm:^7.24.7"
+ "@babel/plugin-transform-function-name": "npm:^7.25.1"
+ "@babel/plugin-transform-literals": "npm:^7.25.2"
+ "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7"
+ "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8"
+ "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7"
+ "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7"
+ "@babel/plugin-transform-numeric-separator": "npm:^7.24.7"
+ "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7"
+ "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7"
+ "@babel/plugin-transform-optional-chaining": "npm:^7.24.8"
+ "@babel/plugin-transform-parameters": "npm:^7.24.7"
+ "@babel/plugin-transform-private-methods": "npm:^7.24.7"
+ "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7"
+ "@babel/plugin-transform-react-display-name": "npm:^7.24.7"
+ "@babel/plugin-transform-react-jsx": "npm:^7.25.2"
+ "@babel/plugin-transform-react-jsx-self": "npm:^7.24.7"
+ "@babel/plugin-transform-react-jsx-source": "npm:^7.24.7"
+ "@babel/plugin-transform-regenerator": "npm:^7.24.7"
+ "@babel/plugin-transform-runtime": "npm:^7.24.7"
+ "@babel/plugin-transform-shorthand-properties": "npm:^7.24.7"
+ "@babel/plugin-transform-spread": "npm:^7.24.7"
+ "@babel/plugin-transform-sticky-regex": "npm:^7.24.7"
+ "@babel/plugin-transform-typescript": "npm:^7.25.2"
+ "@babel/plugin-transform-unicode-regex": "npm:^7.24.7"
+ "@babel/template": "npm:^7.25.0"
+ "@react-native/babel-plugin-codegen": "npm:0.79.6"
+ babel-plugin-syntax-hermes-parser: "npm:0.25.1"
+ babel-plugin-transform-flow-enums: "npm:^0.0.2"
+ react-refresh: "npm:^0.14.0"
+ peerDependencies:
+ "@babel/core": "*"
+ checksum: 10c0/9afdf7ad725d4c5eece4f6390be1bd33c39f8a1da55731c3493d5e61afd31e3c9911ca5e1c539cf8c4cbd9f48c0f9f14914a8b12437011451394ee0db8fd5cdc
+ languageName: node
+ linkType: hard
+
"@react-native/codegen@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/codegen@npm:0.79.5"
@@ -4666,6 +4862,23 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/codegen@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/codegen@npm:0.79.6"
+ dependencies:
+ "@babel/core": "npm:^7.25.2"
+ "@babel/parser": "npm:^7.25.3"
+ glob: "npm:^7.1.1"
+ hermes-parser: "npm:0.25.1"
+ invariant: "npm:^2.2.4"
+ nullthrows: "npm:^1.1.1"
+ yargs: "npm:^17.6.2"
+ peerDependencies:
+ "@babel/core": "*"
+ checksum: 10c0/2359c37016ede418d1fef43e27899599cf753c05481bbfb3ef4de86cdb2f39c9a475ed6f64cc13f267bd547cda10e0b5e62329f8b3fceb94df9058284f126524
+ languageName: node
+ linkType: hard
+
"@react-native/community-cli-plugin@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/community-cli-plugin@npm:0.79.5"
@@ -4687,6 +4900,27 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/community-cli-plugin@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/community-cli-plugin@npm:0.79.6"
+ dependencies:
+ "@react-native/dev-middleware": "npm:0.79.6"
+ chalk: "npm:^4.0.0"
+ debug: "npm:^2.2.0"
+ invariant: "npm:^2.2.4"
+ metro: "npm:^0.82.0"
+ metro-config: "npm:^0.82.0"
+ metro-core: "npm:^0.82.0"
+ semver: "npm:^7.1.3"
+ peerDependencies:
+ "@react-native-community/cli": "*"
+ peerDependenciesMeta:
+ "@react-native-community/cli":
+ optional: true
+ checksum: 10c0/74939b6b53fc504dc4020613874fd63b1af34aef5d4c68e2477e77517929ea4164fcea1b0c6fe8dc976774247413afc1db7862dff8835001f1ca2a70835f5e54
+ languageName: node
+ linkType: hard
+
"@react-native/debugger-frontend@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/debugger-frontend@npm:0.79.5"
@@ -4694,6 +4928,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/debugger-frontend@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/debugger-frontend@npm:0.79.6"
+ checksum: 10c0/e6c846ae9b38077c3a3ac9ca081bc546367414514cf31aacb2f5427821629a9907d1e180e1491008c6eb94b4c8acedf2ff2a660b8d80d85992a6dada9c76cc27
+ languageName: node
+ linkType: hard
+
"@react-native/dev-middleware@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/dev-middleware@npm:0.79.5"
@@ -4713,6 +4954,25 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/dev-middleware@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/dev-middleware@npm:0.79.6"
+ dependencies:
+ "@isaacs/ttlcache": "npm:^1.4.1"
+ "@react-native/debugger-frontend": "npm:0.79.6"
+ chrome-launcher: "npm:^0.15.2"
+ chromium-edge-launcher: "npm:^0.2.0"
+ connect: "npm:^3.6.5"
+ debug: "npm:^2.2.0"
+ invariant: "npm:^2.2.4"
+ nullthrows: "npm:^1.1.1"
+ open: "npm:^7.0.3"
+ serve-static: "npm:^1.16.2"
+ ws: "npm:^6.2.3"
+ checksum: 10c0/7aea0c75d67fd1a450c83423f043ab8061d0415c819e7dc6fbb64ec01f397312b26a593810ba9b09adf94e8e93231db7bf5d835aceb73c521d4156936a67ee6d
+ languageName: node
+ linkType: hard
+
"@react-native/gradle-plugin@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/gradle-plugin@npm:0.79.5"
@@ -4720,6 +4980,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/gradle-plugin@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/gradle-plugin@npm:0.79.6"
+ checksum: 10c0/67294f5cf139be91b3a691929178d5aeff0405bdd29d0c8a3282e502d6515279381caa13c055985fe58b379a1f95d09a3f22fbaf7a9b1711063421453ed6e9d8
+ languageName: node
+ linkType: hard
+
"@react-native/js-polyfills@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/js-polyfills@npm:0.79.5"
@@ -4727,6 +4994,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/js-polyfills@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/js-polyfills@npm:0.79.6"
+ checksum: 10c0/a2f78dac9ed1cf4224c863c00b6ef941097928150404f12951e6d938f6ea52869cbed128e6847ee3d4a34437ae48cc340a259e5967ece0adeba7cb4c6c7a2489
+ languageName: node
+ linkType: hard
+
"@react-native/normalize-colors@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/normalize-colors@npm:0.79.5"
@@ -4734,6 +5008,13 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/normalize-colors@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/normalize-colors@npm:0.79.6"
+ checksum: 10c0/2bc662b6846a92fe62bc54be34204ad90eeceb9747fdd6f439f3262a8b93a47c1a9f574368749c07636a9a746e2d5b842e9492f55bbfcbe164a1b7385f467ac1
+ languageName: node
+ linkType: hard
+
"@react-native/virtualized-lists@npm:0.79.5":
version: 0.79.5
resolution: "@react-native/virtualized-lists@npm:0.79.5"
@@ -4751,6 +5032,23 @@ __metadata:
languageName: node
linkType: hard
+"@react-native/virtualized-lists@npm:0.79.6":
+ version: 0.79.6
+ resolution: "@react-native/virtualized-lists@npm:0.79.6"
+ dependencies:
+ invariant: "npm:^2.2.4"
+ nullthrows: "npm:^1.1.1"
+ peerDependencies:
+ "@types/react": ^19.0.0
+ react: "*"
+ react-native: "*"
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 10c0/9fdfcc1b5901c5b16c3e77178a7e6a63bc9c7278272a0f534c0a500aaeda31338540ff2ef45c38b20f04b306d1bf7179ffe455c5fdf0e94890c5d102fe549c38
+ languageName: node
+ linkType: hard
+
"@react-navigation/bottom-tabs@npm:^6.6.1":
version: 6.6.1
resolution: "@react-navigation/bottom-tabs@npm:6.6.1"
@@ -7519,6 +7817,40 @@ __metadata:
languageName: node
linkType: hard
+"babel-preset-expo@npm:~13.2.4":
+ version: 13.2.4
+ resolution: "babel-preset-expo@npm:13.2.4"
+ dependencies:
+ "@babel/helper-module-imports": "npm:^7.25.9"
+ "@babel/plugin-proposal-decorators": "npm:^7.12.9"
+ "@babel/plugin-proposal-export-default-from": "npm:^7.24.7"
+ "@babel/plugin-syntax-export-default-from": "npm:^7.24.7"
+ "@babel/plugin-transform-export-namespace-from": "npm:^7.25.9"
+ "@babel/plugin-transform-flow-strip-types": "npm:^7.25.2"
+ "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8"
+ "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7"
+ "@babel/plugin-transform-parameters": "npm:^7.24.7"
+ "@babel/plugin-transform-private-methods": "npm:^7.24.7"
+ "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7"
+ "@babel/plugin-transform-runtime": "npm:^7.24.7"
+ "@babel/preset-react": "npm:^7.22.15"
+ "@babel/preset-typescript": "npm:^7.23.0"
+ "@react-native/babel-preset": "npm:0.79.6"
+ babel-plugin-react-native-web: "npm:~0.19.13"
+ babel-plugin-syntax-hermes-parser: "npm:^0.25.1"
+ babel-plugin-transform-flow-enums: "npm:^0.0.2"
+ debug: "npm:^4.3.4"
+ react-refresh: "npm:^0.14.2"
+ resolve-from: "npm:^5.0.0"
+ peerDependencies:
+ babel-plugin-react-compiler: ^19.0.0-beta-e993439-20250405
+ peerDependenciesMeta:
+ babel-plugin-react-compiler:
+ optional: true
+ checksum: 10c0/d50cf31c89484073930e205b04a439d52b870a88e6ba5256e6ab3a1c722638c58761518d634ad99e44a1105f61ab685d0e451d1de8b89670b259ff18c1f8eadc
+ languageName: node
+ linkType: hard
+
"babel-preset-jest@npm:^29.6.3":
version: 29.6.3
resolution: "babel-preset-jest@npm:29.6.3"
@@ -10382,6 +10714,19 @@ __metadata:
languageName: node
linkType: hard
+"expo-constants@npm:~17.1.8":
+ version: 17.1.8
+ resolution: "expo-constants@npm:17.1.8"
+ dependencies:
+ "@expo/config": "npm:~11.0.13"
+ "@expo/env": "npm:~1.0.7"
+ peerDependencies:
+ expo: "*"
+ react-native: "*"
+ checksum: 10c0/04ba3d5c7aa84e72e225429c956266b80be68e721a6da29b62b622be8e173aed6eb866f88516f40143594442bd4fbc2f641310df0bcdde09e69a8721042bd243
+ languageName: node
+ linkType: hard
+
"expo-device@npm:~7.1.4":
version: 7.1.4
resolution: "expo-device@npm:7.1.4"
@@ -10471,6 +10816,23 @@ __metadata:
languageName: node
linkType: hard
+"expo-modules-autolinking@npm:2.1.14":
+ version: 2.1.14
+ resolution: "expo-modules-autolinking@npm:2.1.14"
+ dependencies:
+ "@expo/spawn-async": "npm:^1.7.2"
+ chalk: "npm:^4.1.0"
+ commander: "npm:^7.2.0"
+ find-up: "npm:^5.0.0"
+ glob: "npm:^10.4.2"
+ require-from-string: "npm:^2.0.2"
+ resolve-from: "npm:^5.0.0"
+ bin:
+ expo-modules-autolinking: bin/expo-modules-autolinking.js
+ checksum: 10c0/3d416a5ca69c95f462f6aa138ebc5ef6ea4f57e668f773235576f39f21285cb78c9a9b6b499603ec578903922f4e1c6aef62f3cc3156a1525f4af863cd3c3532
+ languageName: node
+ linkType: hard
+
"expo-modules-core@npm:2.4.2":
version: 2.4.2
resolution: "expo-modules-core@npm:2.4.2"
@@ -10480,6 +10842,15 @@ __metadata:
languageName: node
linkType: hard
+"expo-modules-core@npm:2.5.0":
+ version: 2.5.0
+ resolution: "expo-modules-core@npm:2.5.0"
+ dependencies:
+ invariant: "npm:^2.2.4"
+ checksum: 10c0/9586d80644278fa4c15cc1e85f403934a07936574c050623b26a0dec8d8cbc8776ed68e988510621495d1227f52a127938a2aad2c997d05a449d0f1fd009da86
+ languageName: node
+ linkType: hard
+
"expo-navigation-bar@npm:~4.2.7":
version: 4.2.7
resolution: "expo-navigation-bar@npm:4.2.7"
@@ -10561,6 +10932,48 @@ __metadata:
languageName: node
linkType: hard
+"expo@npm:53.0.25":
+ version: 53.0.25
+ resolution: "expo@npm:53.0.25"
+ dependencies:
+ "@babel/runtime": "npm:^7.20.0"
+ "@expo/cli": "npm:0.24.23"
+ "@expo/config": "npm:~11.0.13"
+ "@expo/config-plugins": "npm:~10.1.2"
+ "@expo/fingerprint": "npm:0.13.4"
+ "@expo/metro-config": "npm:0.20.18"
+ "@expo/vector-icons": "npm:^14.0.0"
+ babel-preset-expo: "npm:~13.2.4"
+ expo-asset: "npm:~11.1.7"
+ expo-constants: "npm:~17.1.8"
+ expo-file-system: "npm:~18.1.11"
+ expo-font: "npm:~13.3.2"
+ expo-keep-awake: "npm:~14.1.4"
+ expo-modules-autolinking: "npm:2.1.14"
+ expo-modules-core: "npm:2.5.0"
+ react-native-edge-to-edge: "npm:1.6.0"
+ whatwg-url-without-unicode: "npm:8.0.0-3"
+ peerDependencies:
+ "@expo/dom-webview": "*"
+ "@expo/metro-runtime": "*"
+ react: "*"
+ react-native: "*"
+ react-native-webview: "*"
+ peerDependenciesMeta:
+ "@expo/dom-webview":
+ optional: true
+ "@expo/metro-runtime":
+ optional: true
+ react-native-webview:
+ optional: true
+ bin:
+ expo: bin/cli
+ expo-modules-autolinking: bin/autolinking
+ fingerprint: bin/fingerprint
+ checksum: 10c0/74e5564f0df14a7439209c590ed0613970ee4cd3a1509734e99531cb57409cbe8348286ba2e9e05cbd14b4ca9f628983caa7dff95dd785ba7cc5ea2a1a43611a
+ languageName: node
+ linkType: hard
+
"exponential-backoff@npm:^3.1.1":
version: 3.1.1
resolution: "exponential-backoff@npm:3.1.1"
@@ -16429,6 +16842,58 @@ __metadata:
languageName: node
linkType: hard
+"react-native@npm:0.79.6":
+ version: 0.79.6
+ resolution: "react-native@npm:0.79.6"
+ dependencies:
+ "@jest/create-cache-key-function": "npm:^29.7.0"
+ "@react-native/assets-registry": "npm:0.79.6"
+ "@react-native/codegen": "npm:0.79.6"
+ "@react-native/community-cli-plugin": "npm:0.79.6"
+ "@react-native/gradle-plugin": "npm:0.79.6"
+ "@react-native/js-polyfills": "npm:0.79.6"
+ "@react-native/normalize-colors": "npm:0.79.6"
+ "@react-native/virtualized-lists": "npm:0.79.6"
+ abort-controller: "npm:^3.0.0"
+ anser: "npm:^1.4.9"
+ ansi-regex: "npm:^5.0.0"
+ babel-jest: "npm:^29.7.0"
+ babel-plugin-syntax-hermes-parser: "npm:0.25.1"
+ base64-js: "npm:^1.5.1"
+ chalk: "npm:^4.0.0"
+ commander: "npm:^12.0.0"
+ event-target-shim: "npm:^5.0.1"
+ flow-enums-runtime: "npm:^0.0.6"
+ glob: "npm:^7.1.1"
+ invariant: "npm:^2.2.4"
+ jest-environment-node: "npm:^29.7.0"
+ memoize-one: "npm:^5.0.0"
+ metro-runtime: "npm:^0.82.0"
+ metro-source-map: "npm:^0.82.0"
+ nullthrows: "npm:^1.1.1"
+ pretty-format: "npm:^29.7.0"
+ promise: "npm:^8.3.0"
+ react-devtools-core: "npm:^6.1.1"
+ react-refresh: "npm:^0.14.0"
+ regenerator-runtime: "npm:^0.13.2"
+ scheduler: "npm:0.25.0"
+ semver: "npm:^7.1.3"
+ stacktrace-parser: "npm:^0.1.10"
+ whatwg-fetch: "npm:^3.0.0"
+ ws: "npm:^6.2.3"
+ yargs: "npm:^17.6.2"
+ peerDependencies:
+ "@types/react": ^19.0.0
+ react: ^19.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ bin:
+ react-native: cli.js
+ checksum: 10c0/626fffcb4eb1161fa2b1e0a89cf1c1e7c7418dfd1c1f5ae1006d43716ca859ededee286b41887ea273198e782927bc4071e8555ab767ca0f010d0b4156cdeec9
+ languageName: node
+ linkType: hard
+
"react-refresh@npm:^0.14.0, react-refresh@npm:^0.14.2":
version: 0.14.2
resolution: "react-refresh@npm:0.14.2"
From a31f3eca0c0e72c62f682e5c70719be212a2f98f Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Wed, 17 Dec 2025 15:21:50 +0100
Subject: [PATCH 12/13] Fix android plugin
---
internal/fishjam-chat/app.json | 10 --
.../react-native-client/plugin/src/types.ts | 10 --
.../plugin/src/withFishjam.ts | 98 -------------------
.../plugin/src/withFishjamAndroid.ts | 69 +++++++++++--
4 files changed, 63 insertions(+), 124 deletions(-)
diff --git a/internal/fishjam-chat/app.json b/internal/fishjam-chat/app.json
index fdf3cb45e..ca0a77e37 100644
--- a/internal/fishjam-chat/app.json
+++ b/internal/fishjam-chat/app.json
@@ -70,16 +70,6 @@
"enableVoIPBackgroundMode": true,
"supportsPictureInPicture": true,
"broadcastExtensionDisplayName": "Fishjam Screen Broadcast"
- },
- "livestream": {
- "android": {
- "enableScreensharing": true,
- "supportsPictureInPicture": true
- },
- "ios": {
- "enableScreensharing": true,
- "supportsPictureInPicture": true
- }
}
}
],
diff --git a/packages/react-native-client/plugin/src/types.ts b/packages/react-native-client/plugin/src/types.ts
index 29e220dae..c621f719e 100644
--- a/packages/react-native-client/plugin/src/types.ts
+++ b/packages/react-native-client/plugin/src/types.ts
@@ -14,15 +14,5 @@ export type FishjamPluginOptions =
broadcastExtensionTargetName?: string;
broadcastExtensionDisplayName?: string;
};
- livestream?: {
- android?: {
- enableScreensharing?: boolean;
- supportsPictureInPicture?: boolean;
- };
- ios?: {
- enableScreensharing?: boolean;
- supportsPictureInPicture?: boolean;
- };
- };
}
| undefined;
diff --git a/packages/react-native-client/plugin/src/withFishjam.ts b/packages/react-native-client/plugin/src/withFishjam.ts
index b09619f0d..2fe687abf 100644
--- a/packages/react-native-client/plugin/src/withFishjam.ts
+++ b/packages/react-native-client/plugin/src/withFishjam.ts
@@ -3,107 +3,9 @@ import { withFishjamAndroid } from './withFishjamAndroid';
import { FishjamPluginOptions } from './types';
import withFishjamIos from './withFishjamIos';
-/**
- * Main Fishjam Expo config plugin.
- *
- * This plugin configures both iOS and Android platforms for Fishjam video calls
- * and optionally for livestreaming functionality.
- *
- * ## Basic Usage (Video Calls)
- *
- * ```json
- * {
- * "plugins": [
- * [
- * "@fishjam-cloud/react-native-client",
- * {
- * "android": {
- * "enableForegroundService": true,
- * "supportsPictureInPicture": true
- * },
- * "ios": {
- * "enableScreensharing": true,
- * "enableVoIPBackgroundMode": true,
- * "supportsPictureInPicture": true
- * }
- * }
- * ]
- * ]
- * }
- * ```
- *
- * ## Livestreaming Support
- *
- * If you're using the livestream functionality (WHIP/WHEP), add the `livestream` configuration.
- * This will automatically configure the underlying `react-native-whip-whep` package:
- *
- * ```json
- * {
- * "plugins": [
- * [
- * "@fishjam-cloud/react-native-client",
- * {
- * "android": {
- * "enableForegroundService": true,
- * "supportsPictureInPicture": true
- * },
- * "ios": {
- * "enableScreensharing": true,
- * "enableVoIPBackgroundMode": true,
- * "supportsPictureInPicture": true
- * },
- * "livestream": {
- * "android": {
- * "enableScreensharing": true,
- * "supportsPictureInPicture": true
- * },
- * "ios": {
- * "enableScreensharing": true,
- * "supportsPictureInPicture": true
- * }
- * }
- * }
- * ]
- * ]
- * }
- * ```
- *
- * The `livestream` configuration adds:
- * - **Android**: ScreenCaptureService for screen sharing in livestreams
- * - **iOS**: Broadcast extension for screen sharing in livestreams
- *
- * @param config - Expo config object
- * @param options - Plugin configuration options
- * @returns Modified config object
- */
const withFishjam: ConfigPlugin = (config, options) => {
config = withFishjamAndroid(config, options);
config = withFishjamIos(config, options);
-
- if (options?.livestream) {
- try {
- const withWhipWhep =
- // eslint-disable-next-line @typescript-eslint/no-require-imports
- require('react-native-whip-whep/plugin/build/withWhipWhep').default;
-
- // Fishjam's iOS screensharing is enabled, disable whip-whep's screensharing
- // This ensures only one broadcast extension is created (Fishjam's FishjamScreenBroadcastExtension)
- // to avoid duplicate broadcast extension targets in the Xcode project
- if (options?.ios?.enableScreensharing) {
- options.livestream.ios = {
- ...options.livestream.ios,
- enableScreensharing: false,
- };
- }
-
- config = withWhipWhep(config, options.livestream);
- } catch {
- console.warn(
- '[Fishjam] react-native-whip-whep plugin not found. Livestream configuration will be skipped.',
- );
- }
- }
-
return config;
};
diff --git a/packages/react-native-client/plugin/src/withFishjamAndroid.ts b/packages/react-native-client/plugin/src/withFishjamAndroid.ts
index 244cfa36b..c1ba004dd 100644
--- a/packages/react-native-client/plugin/src/withFishjamAndroid.ts
+++ b/packages/react-native-client/plugin/src/withFishjamAndroid.ts
@@ -18,7 +18,7 @@ const withFishjamForegroundService: ConfigPlugin = (
const mainApplication = getMainApplicationOrThrow(configuration.modResults);
mainApplication.service = mainApplication.service || [];
- const newService = {
+ const fishjamService = {
$: {
'android:name':
'io.fishjam.reactnative.foregroundService.FishjamForegroundService',
@@ -27,14 +27,70 @@ const withFishjamForegroundService: ConfigPlugin = (
},
};
- const existingServiceIndex = mainApplication.service.findIndex(
- (service) => service.$['android:name'] === newService.$['android:name'],
+ const whipWhepService = {
+ $: {
+ 'android:name':
+ 'com.swmansion.reactnativeclient.foregroundService.ScreenCaptureService',
+ 'android:foregroundServiceType': 'mediaProjection',
+ 'android:stopWithTask': 'true',
+ },
+ };
+
+ // Add Fishjam service
+ const existingFishjamServiceIndex = mainApplication.service.findIndex(
+ (service) => service.$['android:name'] === fishjamService.$['android:name'],
+ );
+
+ if (existingFishjamServiceIndex !== -1) {
+ mainApplication.service[existingFishjamServiceIndex] = fishjamService;
+ } else {
+ mainApplication.service.push(fishjamService);
+ }
+
+ // Add WhipWhep service
+ const existingWhipWhepServiceIndex = mainApplication.service.findIndex(
+ (service) => service.$['android:name'] === whipWhepService.$['android:name'],
);
- if (existingServiceIndex !== -1) {
- mainApplication.service[existingServiceIndex] = newService;
+ if (existingWhipWhepServiceIndex !== -1) {
+ mainApplication.service[existingWhipWhepServiceIndex] = whipWhepService;
} else {
- mainApplication.service.push(newService);
+ mainApplication.service.push(whipWhepService);
+ }
+
+ return configuration;
+ });
+
+const withFishjamForegroundServicePermission: ConfigPlugin = (
+ config,
+ props,
+) =>
+ withAndroidManifest(config, (configuration) => {
+ if (!props?.android?.enableForegroundService) {
+ return configuration;
+ }
+
+ const mainApplication = configuration.modResults;
+ if (!mainApplication.manifest) {
+ return configuration;
+ }
+
+ if (!mainApplication.manifest['uses-permission']) {
+ mainApplication.manifest['uses-permission'] = [];
+ }
+
+ const permissions = mainApplication.manifest['uses-permission'];
+
+ const hasForegroundServicePermission = permissions.some(
+ (perm) => perm.$?.['android:name'] === 'android.permission.FOREGROUND_SERVICE',
+ );
+
+ if (!hasForegroundServicePermission) {
+ permissions.push({
+ $: {
+ 'android:name': 'android.permission.FOREGROUND_SERVICE',
+ },
+ });
}
return configuration;
@@ -61,6 +117,7 @@ export const withFishjamAndroid: ConfigPlugin = (
config,
props,
) => {
+ config = withFishjamForegroundServicePermission(config, props);
config = withFishjamForegroundService(config, props);
config = withFishjamPictureInPicture(config, props);
return config;
From 6aea42b245b430e8831bc24e51127d19161758ba Mon Sep 17 00:00:00 2001
From: Anna Olak <15946812+anna1901@users.noreply.github.com>
Date: Wed, 17 Dec 2025 15:24:13 +0100
Subject: [PATCH 13/13] lint code
---
.../plugin/src/withFishjamAndroid.ts | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/packages/react-native-client/plugin/src/withFishjamAndroid.ts b/packages/react-native-client/plugin/src/withFishjamAndroid.ts
index c1ba004dd..53f277807 100644
--- a/packages/react-native-client/plugin/src/withFishjamAndroid.ts
+++ b/packages/react-native-client/plugin/src/withFishjamAndroid.ts
@@ -38,7 +38,8 @@ const withFishjamForegroundService: ConfigPlugin = (
// Add Fishjam service
const existingFishjamServiceIndex = mainApplication.service.findIndex(
- (service) => service.$['android:name'] === fishjamService.$['android:name'],
+ (service) =>
+ service.$['android:name'] === fishjamService.$['android:name'],
);
if (existingFishjamServiceIndex !== -1) {
@@ -49,7 +50,8 @@ const withFishjamForegroundService: ConfigPlugin = (
// Add WhipWhep service
const existingWhipWhepServiceIndex = mainApplication.service.findIndex(
- (service) => service.$['android:name'] === whipWhepService.$['android:name'],
+ (service) =>
+ service.$['android:name'] === whipWhepService.$['android:name'],
);
if (existingWhipWhepServiceIndex !== -1) {
@@ -61,10 +63,9 @@ const withFishjamForegroundService: ConfigPlugin = (
return configuration;
});
-const withFishjamForegroundServicePermission: ConfigPlugin = (
- config,
- props,
-) =>
+const withFishjamForegroundServicePermission: ConfigPlugin<
+ FishjamPluginOptions
+> = (config, props) =>
withAndroidManifest(config, (configuration) => {
if (!props?.android?.enableForegroundService) {
return configuration;
@@ -82,7 +83,8 @@ const withFishjamForegroundServicePermission: ConfigPlugin
const permissions = mainApplication.manifest['uses-permission'];
const hasForegroundServicePermission = permissions.some(
- (perm) => perm.$?.['android:name'] === 'android.permission.FOREGROUND_SERVICE',
+ (perm) =>
+ perm.$?.['android:name'] === 'android.permission.FOREGROUND_SERVICE',
);
if (!hasForegroundServicePermission) {