Skip to content

Commit

Permalink
fix(react-native): bring FloatingParticipantView vertically down belo…
Browse files Browse the repository at this point in the history
…w CallTopView (#957)

![simulator_screenshot_7CCE4C3B-2351-4A1B-B5B8-BE258F91029D](https://github.com/GetStream/stream-video-js/assets/39884168/9b776fca-6991-417e-940a-00d5ed345433)

---------

Co-authored-by: Vishal Narkhede <[email protected]>
  • Loading branch information
khushal87 and vishalnarkhede authored Aug 23, 2023
1 parent f32aec5 commit 84a250e
Show file tree
Hide file tree
Showing 19 changed files with 151 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { act, render, screen, within } from '../utils/RNTLTools';
import {
CallParticipantsGrid,
CallParticipantsList,
LocalParticipantView,
ParticipantView,
VideoRenderer,
} from '../../src/components';
Expand Down Expand Up @@ -43,7 +42,6 @@ describe('CallParticipantsGrid', () => {
<CallParticipantsGrid
CallParticipantsList={CallParticipantsList}
ParticipantView={ParticipantView}
LocalParticipantView={LocalParticipantView}
VideoRenderer={VideoRenderer}
/>,
{
Expand All @@ -60,11 +58,6 @@ describe('CallParticipantsGrid', () => {
screen.getByTestId(`participant-${P_IDS.REMOTE_1}-is-not-speaking`),
);

// shows the local participant floating view?
expect(
await screen.findByTestId(ComponentTestIds.LOCAL_PARTICIPANT),
).toBeVisible();

expect(
participant1.getByTestId(ComponentTestIds.PARTICIPANT_MEDIA_STREAM),
).toHaveProp('streamURL', 'video-test-url');
Expand Down Expand Up @@ -123,9 +116,7 @@ describe('CallParticipantsGrid', () => {
await simulateOnViewableItemsChanged(visibleParticipantsItems);

// Locating and verifying that all ParticipantViews are rendered
const localParticipant = within(
screen.getByTestId(`participant-${P_IDS.LOCAL_1}-is-not-speaking`),
);

const participant2 = within(
screen.getByTestId(`participant-${P_IDS.REMOTE_2}-is-not-speaking`),
);
Expand All @@ -134,9 +125,6 @@ describe('CallParticipantsGrid', () => {
);

// Verifying that the local partic.'s video/audio are rendered within their respective participant
expect(
localParticipant.getByTestId(ComponentTestIds.PARTICIPANT_MEDIA_STREAM),
).toHaveProp('streamURL', 'video-test-url');
expect(
participant2.getByTestId(ComponentTestIds.PARTICIPANT_MEDIA_STREAM),
).toHaveProp('streamURL', 'video-test-url');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import ParticipantVideoFallback from '../../common-content/ui-components/call/ca
import VideoRenderer from '../../common-content/ui-components/call/call-content/video-renderer.mdx';
import OnHangupCallHandler from '../../common-content/ui-components/call/call-content/on-hangup-call-handler.mdx';
import ParticipantView from '../../common-content/ui-components/call/call-content/participant-view.mdx';
import LocalParticipantView from '../../common-content/ui-components/call/call-content/local-participant-view.mdx';
import FloatingParticipantView from '../../common-content/ui-components/call/call-content/floating-participant-view.mdx';
import CallParticipantsList from '../../common-content/ui-components/call/call-content/call-participants-list.mdx';

The `CallContent` is the highest-level UI component that allows you to build your own call screen with full UI elements. So you don't need to take care much about each feature that you need to build a video call screen with this component.
Expand Down Expand Up @@ -131,9 +131,9 @@ When a screen is shared, the layout automatically changes to `spotlight` mode.

<ParticipantView />

### `LocalParticipantView`
### `FloatingParticipantView`

<LocalParticipantView />
<FloatingParticipantView />

### `CallParticipantsList`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
id: local-participant-view
title: LocalParticipantView
id: floating-participant-view
title: FloatingParticipantView
---

import ParticipantLabel from '../../common-content/ui-components/call/call-content/participant-label.mdx';
Expand All @@ -10,25 +10,21 @@ import ParticipantVideoFallback from '../../common-content/ui-components/call/ca
import VideoRenderer from '../../common-content/ui-components/call/call-content/video-renderer.mdx';
import ParticipantView from '../../common-content/ui-components/call/call-content/participant-view.mdx';

LocalParticipantView displays the local video stream of the local participant. It is a floating view by default that can be dragged in the call area.
FloatingParticipantView displays the local video stream of the local participant. It is a floating view that can be dragged in the call area.

When the video is muted, the video muted icon is shown in a disabled background.

:::note
By default, the `LocalParticipantView` renders in a floating mode.
:::

![Preview of the LocalParticipantView component in floating mode.](../../assets/04-ui-components/local-participant-view.jpg)
![Preview of the FloatingParticipantView component in floating mode.](../../assets/04-ui-components/floating-participant-view.jpg)

## General Usage

In order to use the `LocalParticipantView` as a standalone component, you should use the following code:
In order to use the `FloatingParticipantView` as a standalone component, you should use the following code:

```tsx {4}
import { LocalParticipantView } from '@stream-io/video-react-native-sdk';
import { FloatingParticipantView } from '@stream-io/video-react-native-sdk';

const App = () => {
return <LocalParticipantView />;
return <FloatingParticipantView />;
};
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Prop to customize the `LocalParticipantView` component.
Prop to customize the `FloatingParticipantView` component.

| Type | Default Value |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ComponentType`\| `undefined` | [`LocalParticipantView`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Participant/LocalParticipantView/index.tsx) |
| Type | Default Value |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ComponentType`\| `undefined` | [`FloatingParticipantView`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Participant/FloatingParticipantView/index.tsx) |
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,38 @@ import {
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
import { CallingState } from '@stream-io/video-client';
import { useIncallManager } from '../../../hooks';
import { CallParticipantsListComponentProps } from '../CallParticipantsList';
import { ParticipantViewComponentProps } from '../../Participant';
import { Z_INDEX } from '../../../constants';
import { useDebouncedValue } from '../../../utils/hooks';
import {
FloatingParticipantView as DefaultFloatingParticipantView,
FloatingParticipantViewProps,
ParticipantViewComponentProps,
} from '../../Participant';
import { useTheme } from '../../../contexts';

export type CallParticipantsComponentProps =
CallParticipantsListComponentProps &
Pick<
CallParticipantsGridProps,
'CallParticipantsList' | 'LocalParticipantView'
> & {
/**
* Component to customize the CallTopView component.
*/
CallTopView?: React.ComponentType<CallTopViewProps> | null;
/**
* Component to customize the CallControls component.
*/
CallControls?: React.ComponentType<CallControlProps> | null;
};
export type CallParticipantsComponentProps = Pick<
CallParticipantsGridProps,
| 'CallParticipantsList'
| 'ParticipantLabel'
| 'ParticipantNetworkQualityIndicator'
| 'ParticipantReaction'
| 'ParticipantVideoFallback'
| 'ParticipantView'
| 'VideoRenderer'
> & {
/**
* Component to customize the CallTopView component.
*/
CallTopView?: React.ComponentType<CallTopViewProps> | null;
/**
* Component to customize the CallControls component.
*/
CallControls?: React.ComponentType<CallControlProps> | null;
/**
* Component to customize the FloatingParticipantView.
*/
FloatingParticipantView?: React.ComponentType<FloatingParticipantViewProps> | null;
};

export type CallContentProps = Pick<CallControlProps, 'onHangupCallHandler'> &
Pick<
Expand All @@ -56,13 +69,13 @@ export const CallContent = ({
CallParticipantsList,
CallTopView = DefaultCallTopView,
CallControls = DefaultCallControls,
FloatingParticipantView = DefaultFloatingParticipantView,
ParticipantLabel,
ParticipantNetworkQualityIndicator,
ParticipantReaction,
ParticipantVideoFallback,
ParticipantView,
ParticipantsInfoBadge,
LocalParticipantView,
VideoRenderer,
layout,
}: CallContentProps) => {
Expand All @@ -71,7 +84,15 @@ export const CallContent = ({
} = useTheme();
const { useHasOngoingScreenShare } = useCallStateHooks();
const hasScreenShare = useHasOngoingScreenShare();
const { useRemoteParticipants } = useCallStateHooks();

const _remoteParticipants = useRemoteParticipants();
const remoteParticipants = useDebouncedValue(_remoteParticipants, 300); // we debounce the remote participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
const showFloatingView =
remoteParticipants.length > 0 && remoteParticipants.length < 3;

const showSpotlightLayout = hasScreenShare || layout === 'spotlight';

/**
* This hook is used to handle IncallManager specs of the application.
*/
Expand Down Expand Up @@ -101,7 +122,6 @@ export const CallContent = ({
...participantViewProps,
ParticipantView,
CallParticipantsList,
LocalParticipantView,
};

const callParticipantsSpotlightProps: CallParticipantsSpotlightProps = {
Expand All @@ -113,13 +133,19 @@ export const CallContent = ({
return (
<View style={[styles.container, callContent.container]}>
<View style={[styles.container, callContent.callParticipantsContainer]}>
{CallTopView && (
<CallTopView
onBackPressed={onBackPressed}
onParticipantInfoPress={onParticipantInfoPress}
ParticipantsInfoBadge={ParticipantsInfoBadge}
/>
)}
<View style={[styles.view, callContent.topContainer]}>
{CallTopView && (
<CallTopView
onBackPressed={onBackPressed}
onParticipantInfoPress={onParticipantInfoPress}
ParticipantsInfoBadge={ParticipantsInfoBadge}
/>
)}
{showFloatingView && FloatingParticipantView && (
<FloatingParticipantView {...participantViewProps} />
)}
</View>

{showSpotlightLayout ? (
<CallParticipantsSpotlight {...callParticipantsSpotlightProps} />
) : (
Expand All @@ -135,4 +161,8 @@ export const CallContent = ({

const styles = StyleSheet.create({
container: { flex: 1 },
view: {
...StyleSheet.absoluteFillObject,
zIndex: Z_INDEX.IN_FRONT,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,12 @@ import {
CallParticipantsListComponentProps,
} from '../CallParticipantsList/CallParticipantsList';
import { ComponentTestIds } from '../../../constants/TestIds';
import {
LocalParticipantView as DefaultLocalParticipantView,
LocalParticipantViewProps,
} from '../../Participant';
import { useTheme } from '../../../contexts/ThemeContext';

/**
* Props for the CallParticipantsGrid component.
*/
export type CallParticipantsGridProps = CallParticipantsListComponentProps & {
/**
* Component to customize the LocalParticipantView.
*/
LocalParticipantView?: React.ComponentType<LocalParticipantViewProps> | null;
/**
* Component to customize the CallParticipantsList.
*/
Expand All @@ -33,7 +25,6 @@ export type CallParticipantsGridProps = CallParticipantsListComponentProps & {
*/
export const CallParticipantsGrid = ({
CallParticipantsList = DefaultCallParticipantsList,
LocalParticipantView = DefaultLocalParticipantView,
ParticipantLabel,
ParticipantNetworkQualityIndicator,
ParticipantReaction,
Expand Down Expand Up @@ -72,9 +63,6 @@ export const CallParticipantsGrid = ({
]}
testID={ComponentTestIds.CALL_PARTICIPANTS_GRID}
>
{showFloatingView && LocalParticipantView && (
<LocalParticipantView {...participantViewProps} />
)}
{CallParticipantsList && (
<CallParticipantsList
participants={participants}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ import {
View,
StyleSheet,
Text,
Pressable,
StyleProp,
ViewStyle,
Pressable,
} from 'react-native';
import {
ParticipantsInfoBadge as DefaultParticipantsInfoBadge,
ParticipantsInfoBadgeProps,
} from './ParticipantsInfoBadge';
import { Back } from '../../../icons/Back';
import { Z_INDEX } from '../../../constants';
import { TopViewBackground } from '../../../icons';
import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
import { CallingState } from '@stream-io/video-client';
Expand Down Expand Up @@ -50,6 +49,7 @@ export const CallTopView = ({
style: styleProp,
ParticipantsInfoBadge = DefaultParticipantsInfoBadge,
}: CallTopViewProps) => {
const [callTopViewHeight, setCallTopViewHeight] = useState<number>(0);
const {
theme: {
colors,
Expand All @@ -61,18 +61,19 @@ export const CallTopView = ({
const { useCallCallingState } = useCallStateHooks();
const callingState = useCallCallingState();
const { t } = useI18n();
const [headerHeight, setHeaderHeight] = useState<number>(0);
const isCallReconnecting = callingState === CallingState.RECONNECTING;

const onLayout: React.ComponentProps<typeof View>['onLayout'] = (event) => {
const { height } = event.nativeEvent.layout;
setHeaderHeight(height);
if (setCallTopViewHeight) {
setCallTopViewHeight(height);
}
};

return (
<View style={[styles.container, styleProp, callTopView.container]}>
<View style={[styleProp, callTopView.container]}>
{/* Component for the background of the CallTopView. Since it has a Linear Gradient, an SVG is used to render it. */}
<TopViewBackground height={headerHeight} width={'100%'} />
<TopViewBackground height={callTopViewHeight} width={'100%'} />
<View style={[styles.content, callTopView.content]} onLayout={onLayout}>
<View style={styles.leftElement}>
{onBackPressed && (
Expand Down Expand Up @@ -131,16 +132,12 @@ export const CallTopView = ({
};

const styles = StyleSheet.create({
container: {
zIndex: Z_INDEX.IN_FRONT,
position: 'absolute',
left: 0,
right: 0,
},
content: {
position: 'absolute',
top: 0,
flexDirection: 'row',
paddingVertical: 24,
paddingTop: 24,
paddingBottom: 12,
alignItems: 'center',
},
backIconContainer: {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-native-sdk/src/components/Call/Lobby/Lobby.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { useLocalVideoStream } from '../../../hooks';
import { Avatar } from '../../utility/Avatar';
import { StreamVideoParticipant } from '@stream-io/video-client';
import { LOCAL_VIDEO_VIEW_STYLE } from '../../../constants';
import { LOBBY_VIDEO_VIEW_HEIGHT } from '../../../constants';
import { RTCView } from '@stream-io/react-native-webrtc';
import { LobbyControls as DefaultLobbyControls } from '../CallControls/LobbyControls';
import {
Expand Down Expand Up @@ -216,8 +216,8 @@ const styles = StyleSheet.create({
textAlign: 'center',
},
videoContainer: {
height: LOCAL_VIDEO_VIEW_STYLE.height * 2,
borderRadius: LOCAL_VIDEO_VIEW_STYLE.borderRadius * 2,
height: LOBBY_VIDEO_VIEW_HEIGHT,
borderRadius: 20,
justifyContent: 'space-between',
alignItems: 'center',
overflow: 'hidden',
Expand Down
Loading

0 comments on commit 84a250e

Please sign in to comment.