Skip to content

Commit f29756d

Browse files
authored
feat(design-v2): bottom controls drawer component with reactions flow (#1539)
This PR: - adds the More action buttons drawer component - the emoji reactions are implemented and that completes the reactions flow - the other buttons are not functional and their actions/flows will be implemented in other PRs <img alt="Screenshot at Oct 29 14-24-22" src="https://github.com/user-attachments/assets/2c64405e-5c12-4db2-89ac-199ff08652bb" alt="ios-after" width="250" height="540"/>
1 parent 57c88b2 commit f29756d

File tree

18 files changed

+555
-43
lines changed

18 files changed

+555
-43
lines changed

packages/react-native-sdk/src/components/Participant/FloatingParticipantView/FloatingView/AnimatedFloatingView.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
floatingChildViewContainerStyle,
1616
} from './common';
1717
import { getLogger } from '@stream-io/video-client';
18+
import { useTheme } from '../../../..';
1819

1920
const AnimatedFloatingView = ({
2021
initialAlignment,
@@ -29,6 +30,7 @@ const AnimatedFloatingView = ({
2930
const translateRef = useRef(new Animated.ValueXY());
3031
const opacity = useRef(new Animated.Value(0));
3132

33+
const { theme } = useTheme();
3234
const [rectangle, setRectangle] = React.useState<LayoutRectangle>();
3335

3436
// we need to force update the component when the rectangle is available
@@ -58,6 +60,7 @@ const AnimatedFloatingView = ({
5860
width: rectangle.width,
5961
height: rectangle.height,
6062
},
63+
topOffset: theme.floatingParticipantsView.topPosition,
6164
});
6265
const { x, y } = snapAlignments[initialAlignment];
6366
snapAlignmentsRef.current = snapAlignments;
@@ -66,7 +69,7 @@ const AnimatedFloatingView = ({
6669
opacity.current.setValue(1);
6770
forceUpdate();
6871
// any time the dependency changes, we need to snap to the new alignment
69-
}, [initialAlignment, rectangle, containerWidth, containerHeight]);
72+
}, [initialAlignment, rectangle, containerWidth, containerHeight, theme]);
7073

7174
const panResponder = useRef(
7275
PanResponder.create({

packages/react-native-sdk/src/components/Participant/FloatingParticipantView/FloatingView/ReanimatedFloatingView.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
floatingChildViewContainerStyle,
88
FloatingViewProps,
99
} from './common';
10+
import { useTheme } from '../../../..';
1011
type GestureHandlerExportsType = typeof import('react-native-gesture-handler');
1112
type ReanimatedNamespaceType = typeof import('react-native-reanimated').default;
1213
type ReanimatedExportsType = typeof import('react-native-reanimated');
@@ -54,6 +55,7 @@ try {
5455
// we don't want to show the floating view until we have the layout rectangle
5556
const opacity = useSharedValue(0);
5657
const [rectangle, setRectangle] = React.useState<LayoutRectangle>();
58+
const { theme } = useTheme();
5759

5860
const snapAlignments = useMemo(() => {
5961
if (!rectangle) {
@@ -64,7 +66,6 @@ try {
6466
[FloatingViewAlignment.bottomRight]: { x: 0, y: 0 },
6567
};
6668
}
67-
6869
return getSnapAlignments({
6970
rootContainerDimensions: {
7071
width: containerWidth,
@@ -74,8 +75,9 @@ try {
7475
width: rectangle.width,
7576
height: rectangle.height,
7677
},
78+
topOffset: theme.floatingParticipantsView.topPosition,
7779
});
78-
}, [rectangle, containerWidth, containerHeight]);
80+
}, [rectangle, containerWidth, containerHeight, theme]);
7981

8082
const dragGesture = Gesture.Pan()
8183
.onStart((_e) => {

packages/react-native-sdk/src/components/Participant/FloatingParticipantView/FloatingView/common.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,22 @@ export type SnapAlignments = Record<
2323
export function getSnapAlignments({
2424
rootContainerDimensions,
2525
floatingViewDimensions,
26+
topOffset,
2627
}: {
2728
rootContainerDimensions: { width: number; height: number };
2829
floatingViewDimensions: { width: number; height: number };
30+
topOffset: number;
2931
}): SnapAlignments {
3032
const right = rootContainerDimensions.width - floatingViewDimensions.width;
3133
const bottom = rootContainerDimensions.height - floatingViewDimensions.height;
3234
const snapOffsets = {
3335
[FloatingViewAlignment.topLeft]: {
3436
x: 0,
35-
y: 0,
37+
y: topOffset,
3638
},
3739
[FloatingViewAlignment.topRight]: {
3840
x: right,
39-
y: 0,
41+
y: topOffset,
4042
},
4143
[FloatingViewAlignment.bottomLeft]: {
4244
x: 0,

packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantReaction.tsx

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect } from 'react';
1+
import React, { useEffect, useMemo } from 'react';
22
import { StyleSheet, Text, View } from 'react-native';
33
import { useCall } from '@stream-io/video-react-bindings';
44
import { Z_INDEX, defaultEmojiReactions } from '../../../constants';
@@ -32,12 +32,9 @@ export const ParticipantReaction = ({
3232
}: ParticipantReactionProps) => {
3333
const { reaction, sessionId } = participant;
3434
const call = useCall();
35+
const styles = useStyles();
3536
const {
36-
theme: {
37-
typefaces,
38-
variants: { iconSizes },
39-
participantReaction,
40-
},
37+
theme: { typefaces, participantReaction },
4138
} = useTheme();
4239

4340
useEffect(() => {
@@ -60,26 +57,34 @@ export const ParticipantReaction = ({
6057
);
6158

6259
return (
63-
<View
64-
style={[
65-
styles.container,
66-
{
67-
height: iconSizes.md,
68-
width: iconSizes.md,
69-
},
70-
participantReaction.container,
71-
]}
72-
>
73-
<Text style={[participantReaction.reaction, typefaces.heading6]}>
74-
{currentReaction?.icon}
75-
</Text>
76-
</View>
60+
currentReaction?.icon != null && (
61+
<View style={[styles.container, participantReaction.container]}>
62+
<Text style={[participantReaction.reaction, typefaces.heading6]}>
63+
{currentReaction?.icon}
64+
</Text>
65+
</View>
66+
)
7767
);
7868
};
7969

80-
const styles = StyleSheet.create({
81-
container: {
82-
alignSelf: 'flex-start',
83-
zIndex: Z_INDEX.IN_FRONT,
84-
},
85-
});
70+
const useStyles = () => {
71+
const { theme } = useTheme();
72+
return useMemo(
73+
() =>
74+
StyleSheet.create({
75+
container: {
76+
alignSelf: 'flex-end',
77+
marginRight: theme.variants.spacingSizes.md,
78+
marginTop: theme.variants.spacingSizes.md,
79+
height: theme.variants.roundButtonSizes.md,
80+
width: theme.variants.roundButtonSizes.md,
81+
borderRadius: theme.variants.borderRadiusSizes.sm,
82+
backgroundColor: theme.colors.sheetOverlay,
83+
alignItems: 'center',
84+
justifyContent: 'center',
85+
zIndex: Z_INDEX.IN_FRONT,
86+
},
87+
}),
88+
[theme]
89+
);
90+
};

packages/react-native-sdk/src/constants/index.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,48 @@ export const FLOATING_VIDEO_VIEW_STYLE = {
99
export const LOBBY_VIDEO_VIEW_HEIGHT = 240;
1010

1111
export const defaultEmojiReactions: StreamReactionType[] = [
12+
{
13+
type: 'reaction',
14+
emoji_code: ':rolling_on_the_floor_laughing:',
15+
custom: {},
16+
icon: '🤣',
17+
},
1218
{
1319
type: 'reaction',
1420
emoji_code: ':like:',
1521
custom: {},
1622
icon: '👍',
1723
},
1824
{
19-
type: 'raised-hand',
20-
emoji_code: ':raise-hand:',
25+
type: 'reaction',
26+
emoji_code: ':rocket:',
2127
custom: {},
22-
icon: '✋',
28+
icon: '🚀',
29+
},
30+
{
31+
type: 'reaction',
32+
emoji_code: ':dislike:',
33+
custom: {},
34+
icon: '👎',
2335
},
2436
{
2537
type: 'reaction',
2638
emoji_code: ':fireworks:',
2739
custom: {},
2840
icon: '🎉',
2941
},
42+
{
43+
type: 'reaction',
44+
emoji_code: ':raised-hands:',
45+
custom: {},
46+
icon: '🙌',
47+
},
48+
{
49+
type: 'raised-hand',
50+
emoji_code: ':raised-hand:',
51+
custom: {},
52+
icon: '✋',
53+
},
3054
];
3155

3256
export const Z_INDEX = {

packages/react-native-sdk/src/icons/CameraSwitch.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ type Props = {
88
};
99

1010
export const CameraSwitch = ({ color, size }: Props) => (
11-
<Svg viewBox={`0 0 24 24`} width={size} height={size}>
11+
<Svg viewBox={'0 0 24 24'} width={size} height={size}>
1212
<Path
1313
d="M20 5.5H16.83L15.59 4.15C15.22 3.74 14.68 3.5 14.12 3.5H9.88C9.32 3.5 8.78 3.74 8.4 4.15L7.17 5.5H4C2.9 5.5 2 6.4 2 7.5V19.5C2 20.6 2.9 21.5 4 21.5H20C21.1 21.5 22 20.6 22 19.5V7.5C22 6.4 21.1 5.5 20 5.5ZM13.67 18.2C13.15 18.39 12.59 18.5 12 18.5C9.24 18.5 7 16.26 7 13.5H5L7.5 11L10 13.5H8C8 15.71 9.79 17.5 12 17.5C12.46 17.5 12.91 17.42 13.32 17.27C13.51 17.2 13.71 17.24 13.85 17.38C14.11 17.64 14.01 18.07 13.67 18.2ZM16.5 16L14 13.5H16C16 11.29 14.21 9.5 12 9.5C11.54 9.5 11.09 9.58 10.68 9.73C10.49 9.8 10.29 9.76 10.15 9.62C9.89 9.36 9.99 8.93 10.33 8.8C10.85 8.61 11.41 8.5 12 8.5C14.76 8.5 17 10.74 17 13.5H19L16.5 16Z"
1414
fill={color}

packages/react-native-sdk/src/icons/Effects.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ type Props = {
88
};
99

1010
export const Effects = ({ color, size }: Props) => (
11-
<Svg viewBox={`0 0 24 24`} width={size} height={size}>
11+
<Svg viewBox={'0 0 24 24'} width={size} height={size}>
1212
<Path
1313
d="M19.1065 8.00002L19.8965 6.25002L21.6465 5.46002C22.0365 5.28002 22.0365 4.73002 21.6465 4.55002L19.8965 3.76002L19.1065 2.00002C18.9265 1.61002 18.3765 1.61002 18.1965 2.00002L17.4065 3.75002L15.6465 4.54002C15.2565 4.72002 15.2565 5.27002 15.6465 5.45002L17.3965 6.24002L18.1865 8.00002C18.3665 8.39002 18.9265 8.39002 19.1065 8.00002ZM11.1465 9.50002L9.55652 6.00002C9.20652 5.22002 8.08652 5.22002 7.73652 6.00002L6.14652 9.50002L2.64652 11.09C1.86652 11.45 1.86652 12.56 2.64652 12.91L6.14652 14.5L7.73652 18C8.09652 18.78 9.20652 18.78 9.55652 18L11.1465 14.5L14.6465 12.91C15.4265 12.55 15.4265 11.44 14.6465 11.09L11.1465 9.50002ZM18.1865 16L17.3965 17.75L15.6465 18.54C15.2565 18.72 15.2565 19.27 15.6465 19.45L17.3965 20.24L18.1865 22C18.3665 22.39 18.9165 22.39 19.0965 22L19.8865 20.25L21.6465 19.46C22.0365 19.28 22.0365 18.73 21.6465 18.55L19.8965 17.76L19.1065 16C18.9265 15.61 18.3665 15.61 18.1865 16Z"
1414
fill={color}

packages/react-native-sdk/src/theme/theme.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export type Theme = {
9595
container: ViewStyle;
9696
participantViewContainer: ViewStyle;
9797
videoFallback: ViewStyle;
98+
topPosition: number;
9899
};
99100
chatButton: {
100101
container: ViewStyle;
@@ -479,6 +480,7 @@ export const defaultTheme: Theme = {
479480
container: {},
480481
participantViewContainer: {},
481482
videoFallback: {},
483+
topPosition: 0,
482484
},
483485
participantLabel: {
484486
container: {},
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { Svg, Path } from 'react-native-svg';
3+
import { ColorValue } from 'react-native/types';
4+
5+
type Props = {
6+
color: ColorValue;
7+
size: number;
8+
};
9+
10+
const ClosedCaptions = ({ color, size }: Props) => (
11+
<Svg viewBox={'0 0 24 24'} width={size} height={size}>
12+
<Path
13+
d="M19 4H5C3.89 4 3 4.9 3 6V18C3 19.1 3.89 20 5 20H19C20.1 20 21 19.1 21 18V6C21 4.9 20.1 4 19 4ZM11 10.5C11 10.78 10.78 11 10.5 11H10C9.72 11 9.5 10.78 9.5 10.5H7.5V13.5H9.5C9.5 13.22 9.72 13 10 13H10.5C10.78 13 11 13.22 11 13.5V14C11 14.55 10.55 15 10 15H7C6.45 15 6 14.55 6 14V10C6 9.45 6.45 9 7 9H10C10.55 9 11 9.45 11 10V10.5ZM18 10.5C18 10.78 17.78 11 17.5 11H17C16.72 11 16.5 10.78 16.5 10.5H14.5V13.5H16.5C16.5 13.22 16.72 13 17 13H17.5C17.78 13 18 13.22 18 13.5V14C18 14.55 17.55 15 17 15H14C13.45 15 13 14.55 13 14V10C13 9.45 13.45 9 14 9H17C17.55 9 18 9.45 18 10V10.5Z"
14+
fill={color}
15+
/>
16+
</Svg>
17+
);
18+
19+
export default ClosedCaptions;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { Svg, Path } from 'react-native-svg';
3+
import { ColorValue } from 'react-native/types';
4+
5+
type Props = {
6+
color: ColorValue;
7+
size: number;
8+
};
9+
10+
const Feedback = ({ color, size }: Props) => (
11+
<Svg viewBox={'0 0 24 24'} width={size} height={size}>
12+
<Path
13+
d="M19.9949 2H4.00488C2.90488 2 2.00488 2.9 2.00488 4V22L5.99488 18H19.9949C21.0949 18 21.9949 17.1 21.9949 16V4C21.9949 2.9 21.0949 2 19.9949 2ZM12.9949 14H10.9949V12H12.9949V14ZM12.9949 9C12.9949 9.55 12.5449 10 11.9949 10C11.4449 10 10.9949 9.55 10.9949 9V7C10.9949 6.45 11.4449 6 11.9949 6C12.5449 6 12.9949 6.45 12.9949 7V9Z"
14+
fill={color}
15+
/>
16+
</Svg>
17+
);
18+
19+
export default Feedback;

0 commit comments

Comments
 (0)