diff --git a/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx b/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx
index 0ec66f8df2..7aaadc3b34 100644
--- a/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx
+++ b/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx
@@ -33,6 +33,10 @@ import {
ScreenShareOverlayProps,
} from '../../utility/ScreenShareOverlay';
import RTCViewPipIOS from './RTCViewPipIOS';
+import {
+ CallParticipantsFullscreen,
+ CallParticipantsFullscreenProps,
+} from '../CallLayout/CallParticipantsFullscreen';
export type StreamReactionType = StreamReaction & {
icon: string;
@@ -68,9 +72,9 @@ export type CallContentProps = Pick<
> &
CallContentComponentProps & {
/**
- * This switches the participant's layout between the grid and the spotlight mode.
+ * This switches the participant's layout between the grid, spotlight and fullscreen mode.
*/
- layout?: 'grid' | 'spotlight';
+ layout?: 'grid' | 'spotlight' | 'fullscreen';
/**
* Reactions that are to be supported in the call
*/
@@ -116,12 +120,8 @@ export const CallContent = ({
const {
theme: { callContent },
} = useTheme();
- const {
- useCallSettings,
- useHasOngoingScreenShare,
- useRemoteParticipants,
- useLocalParticipant,
- } = useCallStateHooks();
+ const { useCallSettings, useRemoteParticipants, useLocalParticipant } =
+ useCallStateHooks();
useAutoEnterPiPEffect(disablePictureInPicture);
@@ -132,14 +132,9 @@ export const CallContent = ({
const remoteParticipants = useDebouncedValue(_remoteParticipants, 300); // we debounce the remote participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
const localParticipant = useLocalParticipant();
const isInPiPMode = useIsInPiPMode(disablePictureInPicture);
- const hasScreenShare = useHasOngoingScreenShare();
- const showSpotlightLayout = hasScreenShare || layout === 'spotlight';
-
- const showFloatingView =
- !showSpotlightLayout &&
- !isInPiPMode &&
- remoteParticipants.length > 0 &&
- remoteParticipants.length < 3;
+ const isFullScreen = layout === 'fullscreen';
+ const showFloatingView = isFullScreen && remoteParticipants.length === 1;
+
const isRemoteParticipantInFloatingView =
showFloatingView &&
showRemoteParticipantInFloatingView &&
@@ -174,6 +169,13 @@ export const CallContent = ({
const callParticipantsGridProps: CallParticipantsGridProps = {
...participantViewProps,
landscape,
+ ParticipantView,
+ CallParticipantsList,
+ supportedReactions,
+ };
+
+ const callParticipantsFullscreenProps: CallParticipantsFullscreenProps = {
+ ...participantViewProps,
showLocalParticipant: isRemoteParticipantInFloatingView,
ParticipantView,
CallParticipantsList,
@@ -189,6 +191,21 @@ export const CallContent = ({
supportedReactions,
};
+ const renderCallParticipants = (selectedLayout: string) => {
+ switch (selectedLayout) {
+ case 'fullscreen':
+ return (
+
+ );
+ case 'spotlight':
+ return (
+
+ );
+ default:
+ return ;
+ }
+ };
+
return (
<>
{!disablePictureInPicture && (
@@ -197,7 +214,7 @@ export const CallContent = ({
/>
)}
-
+
{!isInPiPMode && CallTopView && (
)}
@@ -220,11 +237,7 @@ export const CallContent = ({
/>
)}
- {showSpotlightLayout ? (
-
- ) : (
-
- )}
+ {renderCallParticipants(layout)}
{!isInPiPMode && CallControls && (
diff --git a/packages/react-native-sdk/src/components/Call/CallControls/ToggleCameraFaceButton.tsx b/packages/react-native-sdk/src/components/Call/CallControls/ToggleCameraFaceButton.tsx
index ac27479f69..10e85a0e0a 100644
--- a/packages/react-native-sdk/src/components/Call/CallControls/ToggleCameraFaceButton.tsx
+++ b/packages/react-native-sdk/src/components/Call/CallControls/ToggleCameraFaceButton.tsx
@@ -28,7 +28,7 @@ export const ToggleCameraFaceButton = ({
const isVideoEnabledInCall = callSettings?.video.enabled;
const {
- theme: { colors, toggleCameraFaceButton, defaults },
+ theme: { colors, toggleCameraFaceButton, variants },
} = useTheme();
const onPress = async () => {
if (onPressHandler) {
@@ -54,7 +54,7 @@ export const ToggleCameraFaceButton = ({
>
&
+ Pick & {
+ /**
+ * Boolean to decide if local participant will be visible in the grid when there is 1:1 call.
+ */
+ showLocalParticipant?: boolean;
+ };
+
+/**
+ * Component used to display a participant in fullscreen mode.
+ */
+export const CallParticipantsFullscreen = ({
+ CallParticipantsList = DefaultCallParticipantsList,
+ ParticipantLabel,
+ ParticipantNetworkQualityIndicator,
+ ParticipantReaction,
+ ParticipantVideoFallback,
+ ParticipantView,
+ VideoRenderer,
+ supportedReactions,
+ showLocalParticipant,
+}: CallParticipantsFullscreenProps) => {
+ const {
+ theme: { colors, callParticipantsFullscreen },
+ } = useTheme();
+ const { useRemoteParticipants, useLocalParticipant } = useCallStateHooks();
+ const remoteParticipants = useDebouncedValue(useRemoteParticipants(), 300);
+ const localParticipant = useLocalParticipant();
+
+ let participants =
+ showLocalParticipant && localParticipant
+ ? [localParticipant]
+ : remoteParticipants;
+
+ if (remoteParticipants.length === 0 && localParticipant) {
+ participants = [localParticipant];
+ }
+
+ const participantViewProps: CallParticipantsListComponentProps = {
+ ParticipantView,
+ ParticipantLabel,
+ ParticipantNetworkQualityIndicator,
+ ParticipantReaction,
+ ParticipantVideoFallback,
+ VideoRenderer,
+ };
+
+ return (
+
+ {CallParticipantsList && (
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: { flex: 1 },
+});
diff --git a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx
index d71bb8c128..ea9ca6e627 100644
--- a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx
+++ b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx
@@ -12,6 +12,7 @@ import { CallContentProps } from '../CallContent';
import { ParticipantViewComponentProps } from '../../Participant';
import { useIsInPiPMode } from '../../../hooks/useIsInPiPMode';
import { StreamVideoParticipant } from '@stream-io/video-client';
+// import { generateMockParticipants } from '.';
/**
* Props for the CallParticipantsGrid component.
@@ -22,10 +23,6 @@ export type CallParticipantsGridProps = ParticipantViewComponentProps &
'supportedReactions' | 'CallParticipantsList' | 'disablePictureInPicture'
> &
Pick & {
- /**
- * Boolean to decide if local participant will be visible in the grid when there is 1:1 call.
- */
- showLocalParticipant?: boolean;
/**
* Check if device is in landscape mode.
* This will apply the landscape mode styles to the component.
@@ -44,7 +41,6 @@ export const CallParticipantsGrid = ({
ParticipantVideoFallback,
ParticipantView,
VideoRenderer,
- showLocalParticipant = false,
supportedReactions,
landscape,
disablePictureInPicture,
@@ -64,19 +60,11 @@ export const CallParticipantsGrid = ({
flexDirection: landscape ? 'row' : 'column',
};
- const isInPiPMode = useIsInPiPMode(disablePictureInPicture);
-
- const showFloatingView =
- !isInPiPMode &&
- remoteParticipants.length > 0 &&
- remoteParticipants.length < 3;
-
- let participants = showFloatingView
- ? showLocalParticipant && localParticipant
- ? [localParticipant]
- : remoteParticipants
- : allParticipants;
+ let participants = allParticipants;
+ // console.log('🚀 ~ participants:', participants);
+ // let participants = generateMockParticipants(9);
+ const isInPiPMode = useIsInPiPMode(disablePictureInPicture);
if (isInPiPMode) {
participants =
remoteParticipants.length > 0
diff --git a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx
index e58164a908..9f13d2fb17 100644
--- a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx
+++ b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import {
hasScreenShare,
speakerLayoutSortPreset,
@@ -18,6 +18,7 @@ import {
import { useTheme } from '../../../contexts/ThemeContext';
import { CallContentProps } from '../CallContent';
import { useIsInPiPMode } from '../../../hooks/useIsInPiPMode';
+// import { generateMockParticipants } from '.';
/**
* Props for the CallParticipantsSpotlight component.
@@ -56,17 +57,19 @@ export const CallParticipantsSpotlight = ({
disablePictureInPicture,
}: CallParticipantsSpotlightProps) => {
const {
- theme: { colors, callParticipantsSpotlight },
+ theme: { callParticipantsSpotlight, variants },
} = useTheme();
+ const styles = useStyles(landscape);
const { useParticipants } = useCallStateHooks();
const _allParticipants = useParticipants({
sortBy: speakerLayoutSortPreset,
});
- const allParticipants = useDebouncedValue(_allParticipants, 300); // we debounce the participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
+ let allParticipants = useDebouncedValue(_allParticipants, 300); // we debounce the participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
+ // allParticipants = generateMockParticipants(10); // for testing
const [participantInSpotlight, ...otherParticipants] = allParticipants;
const isScreenShareOnSpotlight =
participantInSpotlight && hasScreenShare(participantInSpotlight);
- const isUserAloneInCall = _allParticipants?.length === 1;
+ const isUserAloneInCall = allParticipants?.length === 1;
const isInPiP = useIsInPiPMode(disablePictureInPicture);
@@ -88,7 +91,7 @@ export const CallParticipantsSpotlight = ({
};
const spotlightContainerLandscapeStyles: ViewStyle = {
- marginHorizontal: landscape ? 0 : 8,
+ marginHorizontal: landscape ? 0 : variants.spacingSizes.xs,
};
return (
@@ -97,15 +100,12 @@ export const CallParticipantsSpotlight = ({
style={[
styles.container,
landscapeStyles,
- {
- backgroundColor: colors.background2,
- },
callParticipantsSpotlight.container,
]}
>
{participantInSpotlight &&
ParticipantView &&
- (participantInSpotlight.isLocalParticipant && ScreenShareOverlay ? (
+ (isScreenShareOnSpotlight && ScreenShareOverlay ? (
) : (
{
+ const { theme } = useTheme();
+ return useMemo(
+ () =>
+ StyleSheet.create({
+ container: {
+ flex: 1,
+ padding: theme.variants.spacingSizes.xs,
+ backgroundColor: theme.colors.sheetPrimary,
+ },
+ fullScreenSpotlightContainer: {
+ flex: 1,
+ },
+ spotlightContainer: {
+ flex: landscape ? 3 : 4,
+ overflow: 'hidden',
+ borderRadius: theme.variants.borderRadiusSizes.md,
+ marginHorizontal: theme.variants.spacingSizes.xs,
+ },
+ callParticipantsListContainer: {
+ flex: 1,
+ flexDirection: 'row',
+ backgroundColor: theme.colors.sheetPrimary,
+ marginLeft: landscape ? theme.variants.spacingSizes.sm : 0,
+ },
+ }),
+ [theme, landscape]
+ );
+};
diff --git a/packages/react-native-sdk/src/components/Call/CallLayout/index.ts b/packages/react-native-sdk/src/components/Call/CallLayout/index.ts
index 7a8274996f..abcb8fec49 100644
--- a/packages/react-native-sdk/src/components/Call/CallLayout/index.ts
+++ b/packages/react-native-sdk/src/components/Call/CallLayout/index.ts
@@ -1,2 +1,221 @@
export * from './CallParticipantsGrid';
export * from './CallParticipantsSpotlight';
+
+// TODO: before merge remove this
+export const generateMockParticipants = (count: number) => {
+ const mockParticipants = [
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=john&name=john',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 361467495, seconds: '1729747222' },
+ name: 'john',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: 'dd179c0a-2b5d-41b7-be4b-fa8758f92d5a',
+ trackLookupPrefix: '9c7a05feecd46070',
+ userId: 'john',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=Marko&name=Marko',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 227903648, seconds: '1729747225' },
+ name: 'Marko',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: '05a4a2da-3760-4c01-b193-ede42027a3cw',
+ trackLookupPrefix: 'f055633e656610db',
+ userId: 'Marko',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=kristian&name=kristian',
+ isDominantSpeaker: false,
+ isLocalParticipant: true,
+ isSpeaking: false,
+ joinedAt: { nanos: 782654015, seconds: '1729747234' },
+ name: 'kristian',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: '0c907764-84ee-40b4-a7b5-b02c8e6eeae4',
+ trackLookupPrefix: '03ded86c7006b12f',
+ userId: 'kristian',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=sarah&name=sarah',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 782654015, seconds: '1729747234' },
+ name: 'Sarah',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: '0c907764-84ee-40b4-a7b5-b02c8e6eeaef4',
+ trackLookupPrefix: '03ded86c7006b12f',
+ userId: 'sarah',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=alice&name=alice',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 123456789, seconds: '1729747240' },
+ name: 'Alice',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: 'f8d4a2cd-4599-41a8-a457-4e23876e1ac2',
+ trackLookupPrefix: 'b9d0f9c25f83c63a',
+ userId: 'alice',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=bob&name=bob',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 987654321, seconds: '1729747250' },
+ name: 'Bob',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: 'a4d3f2b9-783d-4b87-9c02-e8c9a8a75d3f',
+ trackLookupPrefix: 'd3b2c9a75d3f4b87',
+ userId: 'bob',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=charlie&name=charlie',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 543210987, seconds: '1729747260' },
+ name: 'Charlie',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: 'b5f2a3c4-2d6e-4f89-8a9e-1c3d5e6f7a89',
+ trackLookupPrefix: 'c3d5e6f7a89f4f89',
+ userId: 'charlie',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=david&name=david',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 876543210, seconds: '1729747270' },
+ name: 'David',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: 'c2d5f3a4-9e7b-4c89-a03e-1b5f2d3e4a5b',
+ trackLookupPrefix: 'f2d3e4a5b3c9e7b4',
+ userId: 'david',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=emma&name=emma',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 654321098, seconds: '1729747280' },
+ name: 'Emma',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: 'd3a5c4b2-8e7f-4c89-a2b1-3d5f6e7a8c90',
+ trackLookupPrefix: 'e7a8c90b4c89f4a7',
+ userId: 'emma',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ {
+ audioLevel: 0,
+ connectionQuality: 3,
+ custom: { fields: {} },
+ image: 'https://getstream.io/random_png/?id=frank&name=frank',
+ isDominantSpeaker: false,
+ isLocalParticipant: false,
+ isSpeaking: false,
+ joinedAt: { nanos: 321098765, seconds: '1729747290' },
+ name: 'Frank',
+ publishedTracks: [],
+ reaction: undefined,
+ roles: ['user'],
+ sessionId: 'e2b1d4c3-5f8e-4a7b-90c3-f5a6e8d7c9b0',
+ trackLookupPrefix: 'f8e7a6d9b0c4f5b3',
+ userId: 'frank',
+ viewportVisibilityState: {
+ screenShareTrack: 'UNKNOWN',
+ videoTrack: 'VISIBLE',
+ },
+ },
+ ];
+
+ // Limit the returned participants to the requested count
+ return mockParticipants.slice(0, count);
+};
diff --git a/packages/react-native-sdk/src/components/Call/CallParticipantsList/CallParticipantsList.tsx b/packages/react-native-sdk/src/components/Call/CallParticipantsList/CallParticipantsList.tsx
index afc4a53309..f1a2304d83 100644
--- a/packages/react-native-sdk/src/components/Call/CallParticipantsList/CallParticipantsList.tsx
+++ b/packages/react-native-sdk/src/components/Call/CallParticipantsList/CallParticipantsList.tsx
@@ -21,6 +21,7 @@ import {
ParticipantViewProps,
} from '../../Participant/ParticipantView';
import { CallContentProps } from '../CallContent';
+import { useTheme } from '../../..';
type FlatListProps = React.ComponentProps<
typeof FlatList
@@ -71,7 +72,6 @@ export type CallParticipantsListProps = CallParticipantsListComponentProps &
* hence it should be used only in a flex parent container
*/
export const CallParticipantsList = ({
- numberOfColumns = 2,
horizontal,
participants,
ParticipantView = DefaultParticipantView,
@@ -82,7 +82,10 @@ export const CallParticipantsList = ({
VideoRenderer,
supportedReactions,
landscape,
+ numberOfColumns = landscape ? 3 : 2,
}: CallParticipantsListProps) => {
+ const styles = useStyles();
+ const { theme } = useTheme();
const [containerLayout, setContainerLayout] = useState({
width: 0,
height: 0,
@@ -166,15 +169,31 @@ export const CallParticipantsList = ({
});
const itemContainerStyle = useMemo>(() => {
- const style = { width: itemWidth, height: itemHeight };
+ const style = {
+ width: itemWidth,
+ height: itemHeight,
+ marginHorizontal: theme.variants.spacingSizes.xs,
+ marginVertical: theme.variants.spacingSizes.xs,
+ };
+
if (horizontal) {
- return [styles.participantWrapperHorizontal, style];
+ const participantWrapperHorizontal = {
+ // note: if marginHorizontal is changed, be sure to change the width calculation in calculateParticipantViewSize function
+ marginHorizontal: theme.variants.spacingSizes.xs,
+ borderRadius: theme.variants.borderRadiusSizes.md,
+ };
+ return [participantWrapperHorizontal, style];
}
+
if (landscape) {
- return [styles.landScapeStyle, style];
+ const landscapeStyle = {
+ marginVertical: theme.variants.spacingSizes.xs,
+ borderRadius: theme.variants.borderRadiusSizes.md,
+ };
+ return [landscapeStyle, style];
}
return style;
- }, [itemWidth, itemHeight, horizontal, landscape]);
+ }, [itemWidth, itemHeight, horizontal, landscape, theme]);
const participantProps: ParticipantViewComponentProps = {
ParticipantLabel,
@@ -208,9 +227,9 @@ export const CallParticipantsList = ({
[itemContainerStyle]
);
- // in vertical mode, only when there are more than 2 participants in a call, the participants should be displayed in a grid
- // else we display them both in a stretched row on the screen
- const shouldWrapByColumns = !!horizontal || participants.length > 2;
+ // in vertical mode, only when there are more than 3 participants in a call, the participants should be displayed in a grid
+ // else we display them in a stretched rows on the screen
+ const shouldWrapByColumns = !!horizontal || participants.length > 3;
if (!shouldWrapByColumns) {
return (
@@ -251,17 +270,19 @@ export const CallParticipantsList = ({
);
};
-const styles = StyleSheet.create({
- flexed: { flex: 1 },
- participantWrapperHorizontal: {
- // note: if marginHorizontal is changed, be sure to change the width calculation in calculateParticipantViewSize function
- marginHorizontal: 8,
- borderRadius: 10,
- },
- landScapeStyle: {
- borderRadius: 10,
- },
-});
+const useStyles = () => {
+ const { theme } = useTheme();
+ return useMemo(
+ () =>
+ StyleSheet.create({
+ flexed: {
+ flex: 1,
+ margin: theme.variants.spacingSizes.xs,
+ },
+ }),
+ [theme]
+ );
+};
/**
* This function calculates the size of the participant view based on the size of the container (the phone's screen size) and the number of participants.
@@ -292,15 +313,16 @@ function calculateParticipantViewSize({
// special case: if there are 4 or less participants, we display them in 2 rows
itemHeight = containerHeight / 2;
} else {
- // generally, we display the participants in 3 rows
- itemHeight = containerHeight / 3;
+ // generally, we display the participants in 2 rows
+ itemHeight = containerHeight / 2;
}
}
let itemWidth = containerWidth / numberOfColumns;
- if (horizontal) {
- // in horizontal mode we apply margin of 8 to the participant view and that should be subtracted from the width
- itemWidth = itemWidth - 8 * 2;
+ itemWidth = itemWidth - 4 * 2;
+ if (!horizontal) {
+ // in vertical mode we apply margin of 4 to the participant view and that should be subtracted from the width
+ itemHeight = itemHeight - 4 * 2;
}
return { itemHeight, itemWidth };
diff --git a/packages/react-native-sdk/src/components/Call/Lobby/Lobby.tsx b/packages/react-native-sdk/src/components/Call/Lobby/Lobby.tsx
index 23db831fba..e4f59c65bf 100644
--- a/packages/react-native-sdk/src/components/Call/Lobby/Lobby.tsx
+++ b/packages/react-native-sdk/src/components/Call/Lobby/Lobby.tsx
@@ -1,4 +1,4 @@
-import React, { ComponentType } from 'react';
+import React, { ComponentType, useMemo } from 'react';
import { StyleSheet, Text, View, ViewStyle } from 'react-native';
import {
useCallStateHooks,
@@ -63,6 +63,7 @@ export const Lobby = ({
const {
theme: { colors, lobby, typefaces },
} = useTheme();
+ const styles = useStyles();
const connectedUser = useConnectedUser();
const { useCameraState, useCallSettings } = useCallStateHooks();
const callSettings = useCallSettings();
@@ -159,6 +160,7 @@ const ParticipantStatus = () => {
const {
theme: { colors, typefaces, lobby },
} = useTheme();
+ const styles = useStyles();
const connectedUser = useConnectedUser();
const participantLabel = connectedUser?.name ?? connectedUser?.id;
return (
@@ -186,51 +188,62 @@ const ParticipantStatus = () => {
);
};
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'space-evenly',
- },
- topContainer: {
- flex: 2,
- justifyContent: 'space-evenly',
- paddingHorizontal: 12,
- },
- heading: {
- textAlign: 'center',
- },
- subHeading: {
- textAlign: 'center',
- },
- videoContainer: {
- height: LOBBY_VIDEO_VIEW_HEIGHT,
- borderRadius: 20,
- justifyContent: 'space-between',
- alignItems: 'center',
- overflow: 'hidden',
- padding: 8,
- },
- topView: {},
- bottomContainer: {
- flex: 2,
- justifyContent: 'space-evenly',
- paddingHorizontal: 12,
- },
- participantStatusContainer: {
- alignSelf: 'flex-start',
- flexDirection: 'row',
- alignItems: 'center',
- padding: 8,
- borderRadius: 5,
- },
- avatarContainer: {
- flex: 2,
- justifyContent: 'center',
- },
- userNameLabel: {
- flexShrink: 1,
- },
- audioMutedIconContainer: {
- marginLeft: 8,
- },
-});
+const useStyles = () => {
+ const { theme } = useTheme();
+ return useMemo(
+ () =>
+ StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'space-evenly',
+ paddingRight: theme.variants.insets.right,
+ paddingLeft: theme.variants.insets.left,
+ paddingTop: theme.variants.insets.top,
+ paddingBottom: theme.variants.insets.bottom,
+ },
+ topContainer: {
+ flex: 2,
+ justifyContent: 'space-evenly',
+ paddingHorizontal: 12,
+ },
+ heading: {
+ textAlign: 'center',
+ },
+ subHeading: {
+ textAlign: 'center',
+ },
+ videoContainer: {
+ height: LOBBY_VIDEO_VIEW_HEIGHT,
+ borderRadius: 20,
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ overflow: 'hidden',
+ padding: 8,
+ },
+ topView: {},
+ bottomContainer: {
+ flex: 2,
+ justifyContent: 'space-evenly',
+ paddingHorizontal: 12,
+ },
+ participantStatusContainer: {
+ alignSelf: 'flex-start',
+ flexDirection: 'row',
+ alignItems: 'center',
+ padding: 8,
+ borderRadius: 5,
+ },
+ avatarContainer: {
+ flex: 2,
+ justifyContent: 'center',
+ },
+ userNameLabel: {
+ flexShrink: 1,
+ },
+ audioMutedIconContainer: {
+ marginLeft: 8,
+ },
+ }),
+ [theme]
+ );
+};
diff --git a/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantLabel.tsx b/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantLabel.tsx
index 5d4ee2ad81..36be9835c3 100644
--- a/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantLabel.tsx
+++ b/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantLabel.tsx
@@ -97,19 +97,11 @@ export const ParticipantLabel = ({
]}
>
-
+
{participantLabel}
-
+
{isPinningEnabled && (
@@ -152,6 +144,9 @@ const useStyles = () => {
userNameLabel: {
flexShrink: 1,
marginTop: 2,
+ fontSize: 13,
+ fontWeight: '400',
+ color: theme.colors.iconPrimaryDefault,
},
screenShareIconContainer: {
marginRight: theme.variants.spacingSizes.sm,
diff --git a/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantView.tsx b/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantView.tsx
index 76b6e74921..758dd7538b 100644
--- a/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantView.tsx
+++ b/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantView.tsx
@@ -149,7 +149,13 @@ export const ParticipantView = ({
videoZOrder={videoZOrder}
/>
)}
-
+
{ParticipantLabel && (
)}
@@ -169,10 +175,7 @@ const useStyles = () => {
container: {
justifyContent: 'space-between',
overflow: 'hidden',
- borderWidth: 2,
- borderColor: 'transparent',
- margin: theme.variants.spacingSizes.sm,
- borderRadius: 16,
+ borderRadius: theme.variants.borderRadiusSizes.md,
},
footerContainer: {
flexDirection: 'row',
@@ -182,6 +185,7 @@ const useStyles = () => {
highligtedContainer: {
borderWidth: 2,
},
+ networkIndicatorOnly: { justifyContent: 'flex-end' },
}),
[theme]
);
diff --git a/packages/react-native-sdk/src/constants/TestIds.ts b/packages/react-native-sdk/src/constants/TestIds.ts
index aadf5b2682..f5f38f92de 100644
--- a/packages/react-native-sdk/src/constants/TestIds.ts
+++ b/packages/react-native-sdk/src/constants/TestIds.ts
@@ -10,6 +10,7 @@ export enum ComponentTestIds {
CALL_PARTICIPANTS_LIST = 'call-participants-list',
CALL_PARTICIPANTS_SPOTLIGHT = 'call-participants-spotlight',
CALL_PARTICIPANTS_GRID = 'call-participants-grid',
+ CALL_PARTICIPANTS_FULLSCREEN = 'call-participants-fullscreen',
LOCAL_PARTICIPANT = 'local-participant',
PARTICIPANT_MEDIA_STREAM = 'participant-media-stream',
PARTICIPANTS_INFO = 'participants-info',
diff --git a/packages/react-native-sdk/src/constants/index.ts b/packages/react-native-sdk/src/constants/index.ts
index 42241a3ef0..3e893573b6 100644
--- a/packages/react-native-sdk/src/constants/index.ts
+++ b/packages/react-native-sdk/src/constants/index.ts
@@ -1,9 +1,9 @@
import { StreamReactionType } from '../components';
export const FLOATING_VIDEO_VIEW_STYLE = {
- height: 140,
- width: 80,
- borderRadius: 10,
+ height: 228,
+ width: 140,
+ borderRadius: 16,
};
export const LOBBY_VIDEO_VIEW_HEIGHT = 240;
diff --git a/packages/react-native-sdk/src/icons/CameraSwitch.tsx b/packages/react-native-sdk/src/icons/CameraSwitch.tsx
index a4fc8aa5f9..62bb662cd2 100644
--- a/packages/react-native-sdk/src/icons/CameraSwitch.tsx
+++ b/packages/react-native-sdk/src/icons/CameraSwitch.tsx
@@ -8,7 +8,7 @@ type Props = {
};
export const CameraSwitch = ({ color, size }: Props) => (
-