Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions packages/client/src/store/CallState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class CallState {
private callStatsReportSubject = new BehaviorSubject<
CallStatsReport | undefined
>(undefined);
private durationSubject = new BehaviorSubject<number>(0);

// These are tracks that were delivered to the Subscriber's onTrack event
// that we couldn't associate with a participant yet.
Expand Down Expand Up @@ -285,6 +286,11 @@ export class CallState {
*/
thumbnails$: Observable<ThumbnailResponse | undefined>;

/**
* Will provide the count of seconds since the call started.
*/
duration$: Observable<number>;

readonly logger = getLogger(['CallState']);

/**
Expand Down Expand Up @@ -391,6 +397,11 @@ export class CallState {
this.recording$ = duc(this.recordingSubject);
this.transcribing$ = duc(this.transcribingSubject);

this.duration$ = duc(this.durationSubject);
setInterval(() => {
this.setDuration((d) => d + 1);
}, 1000);

this.eventHandlers = {
// these events are not updating the call state:
'call.closed_caption': undefined,
Expand Down Expand Up @@ -515,6 +526,23 @@ export class CallState {
return this.setCurrentValue(this.participantCountSubject, count);
};

/**
* The number of seconds since the start of the call.
*/
get duration() {
return this.getCurrentValue(this.duration$);
}

/**
* Sets the number of seconds since the start of the call.
*
* @internal
* @param duration the duration of the call in seconds.
*/
setDuration = (duration: Patch<number>) => {
return this.setCurrentValue(this.durationSubject, duration);
};

/**
* The time the call session actually started.
* Useful for displaying the call duration.
Expand Down
10 changes: 10 additions & 0 deletions packages/react-bindings/src/hooks/callStateHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,16 @@ export const useParticipantCount = () => {
return useObservableValue(participantCount$);
};

/**
* Returns the duration of the call in seconds.
*
* @category Call State
*/
export const useCallDuration = () => {
const { duration$ } = useCallState();
return useObservableValue(duration$);
};

/**
* Returns the approximate anonymous participant count of the active call.
* The regular participants are not included in this count. It is computed on the server.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import React, { useEffect, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import InCallManager from 'react-native-incall-manager';

import {
CallTopView as DefaultCallTopView,
CallTopViewProps,
} from '../CallTopView';
import {
CallParticipantsGrid,
CallParticipantsGridProps,
Expand Down Expand Up @@ -48,7 +43,7 @@ type CallContentComponentProps = ParticipantViewComponentProps &
/**
* Component to customize the CallTopView component.
*/
CallTopView?: React.ComponentType<CallTopViewProps> | null;
CallTopView?: React.ComponentType<any> | null;
/**
* Component to customize the CallControls component.
*/
Expand All @@ -71,7 +66,6 @@ export type CallContentProps = Pick<
HangUpCallButtonProps,
'onHangupCallHandler'
> &
Pick<CallTopViewProps, 'onBackPressed'> &
CallContentComponentProps & {
/**
* This switches the participant's layout between the grid and the spotlight mode.
Expand All @@ -97,10 +91,9 @@ export type CallContentProps = Pick<
};

export const CallContent = ({
onBackPressed,
onHangupCallHandler,
CallParticipantsList,
CallTopView = DefaultCallTopView,
CallTopView,
CallControls = DefaultCallControls,
FloatingParticipantView = DefaultFloatingParticipantView,
ScreenShareOverlay = DefaultScreenShareOverlay,
Expand Down Expand Up @@ -204,16 +197,16 @@ export const CallContent = ({
/>
)}
<View style={[styles.container, callContent.container]}>
<View style={[styles.content, callContent.callParticipantsContainer]}>
<View style={[styles.container, callContent.callParticipantsContainer]}>
{!isInPiPMode && CallTopView && (
<CallTopView onHangupCallHandler={onHangupCallHandler} />
)}
<View
style={[styles.view, callContent.topContainer]}
// "box-none" disallows the container view to be not take up touches
// and allows only the top and floating view (its child views) to take up the touches
pointerEvents="box-none"
>
{!isInPiPMode && CallTopView && (
<CallTopView onBackPressed={onBackPressed} />
)}
{showFloatingView && FloatingParticipantView && (
<FloatingParticipantView
participant={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ interface CallControlsButtonProps {
* The background color of the button rendered.
*/
color?: ColorValue;
/**
* The background color of the disabled button.
*/
disabledColor?: ColorValue;
/**
* Boolean to enable/disable the button
*/
Expand Down Expand Up @@ -49,6 +53,7 @@ export const CallControlsButton = (
children,
disabled,
color: colorProp,
disabledColor: disabledColorProp,
style: styleProp,
size,
testID,
Expand All @@ -68,7 +73,7 @@ export const CallControlsButton = (
styles.container,
{
backgroundColor: disabled
? colors.buttonPrimaryDisabled
? disabledColorProp || colors.buttonPrimaryDisabled
: colorProp || colors.buttonSecondaryDefault,
opacity: pressed ? 0.2 : 1,
height: size || roundButtonSizes.lg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ButtonTestIds } from '../../../constants/TestIds';
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
import { CallingState } from '@stream-io/video-client';
import { useTheme } from '../../../contexts/ThemeContext';
import { IconWrapper } from '../../../icons/IconWrapper';

/**
* The props for the Hang up call button in the Call Controls.
Expand Down Expand Up @@ -42,7 +43,7 @@ export const HangUpCallButton = ({
const { useCallCallingState } = useCallStateHooks();
const callingState = useCallCallingState();
const {
theme: { colors, hangupCallButton },
theme: { colors, hangupCallButton, variants },
} = useTheme();

const onPress = async () => {
Expand Down Expand Up @@ -72,15 +73,12 @@ export const HangUpCallButton = ({
size={size}
testID={ButtonTestIds.HANG_UP_CALL}
>
<PhoneDown color={colors.base1} />
<IconWrapper>
<PhoneDown
color={colors.iconPrimaryDefault}
size={variants.iconSizes.md}
/>
</IconWrapper>
</CallControlsButton>
);
};

// TODO: Check if this style is needed
// This was passed to CallControlsButton as style prop
// const styles = StyleSheet.create({
// button: {
// shadowColor: theme.light.error,
// },
// });
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const RejectCallButton = ({
theme: {
colors,
rejectCallButton,
variants: { buttonSizes },
variants: { buttonSizes, iconSizes },
},
} = useTheme();
const rejectCallHandler = async () => {
Expand Down Expand Up @@ -69,7 +69,7 @@ export const RejectCallButton = ({
// svgContainerStyle={theme.icon.lg}
style={rejectCallButton}
>
<PhoneDown color={colors.base1} />
<PhoneDown color={colors.iconPrimaryDefault} size={iconSizes.md} />
</CallControlsButton>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { OwnCapability } from '@stream-io/video-client';
import { Restricted, useCallStateHooks } from '@stream-io/video-react-bindings';
import React from 'react';
import { CallControlsButton } from './CallControlsButton';
import { CameraSwitch } from '../../../icons';
import { CameraSwitch, IconWrapper } from '../../../icons';
import { useTheme } from '../../../contexts/ThemeContext';

/**
Expand All @@ -28,7 +28,7 @@ export const ToggleCameraFaceButton = ({
const isVideoEnabledInCall = callSettings?.video.enabled;

const {
theme: { colors, toggleCameraFaceButton },
theme: { colors, toggleCameraFaceButton, defaults },
} = useTheme();
const onPress = async () => {
if (onPressHandler) {
Expand All @@ -47,17 +47,23 @@ export const ToggleCameraFaceButton = ({
<Restricted requiredGrants={[OwnCapability.SEND_VIDEO]}>
<CallControlsButton
onPress={onPress}
color={direction === 'back' ? colors.background4 : colors.base1}
color={colors.sheetPrimary}
disabledColor={colors.sheetPrimary}
disabled={optimisticIsMute}
style={toggleCameraFaceButton}
>
<CameraSwitch
color={
direction === 'front' || direction === undefined
? colors.base5
: colors.base1
}
/>
<IconWrapper>
<CameraSwitch
size={defaults.iconSize}
color={
optimisticIsMute
? colors.buttonPrimaryDisabled
: direction === 'front' || direction === undefined
? colors.iconPrimaryDefault
: colors.buttonPrimaryDefault
}
/>
</IconWrapper>
</CallControlsButton>
</Restricted>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const CallParticipantsGrid = ({
style={[
styles.container,
landscapeStyles,
{ backgroundColor: colors.background2 },
{ backgroundColor: colors.sheetPrimary },
callParticipantsGrid.container,
]}
testID={ComponentTestIds.CALL_PARTICIPANTS_GRID}
Expand All @@ -118,7 +118,5 @@ export const CallParticipantsGrid = ({
};

const styles = StyleSheet.create({
container: {
flex: 1,
},
container: { flex: 1 },
});
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,7 @@ export const CallParticipantsList = ({
};

const styles = StyleSheet.create({
flexed: {
flex: 1,
},
flexed: { flex: 1 },
participantWrapperHorizontal: {
// note: if marginHorizontal is changed, be sure to change the width calculation in calculateParticipantViewSize function
marginHorizontal: 8,
Expand Down
Loading
Loading