Skip to content

Commit 713ce92

Browse files
authored
feat: Add user status on voice call widget (#37217)
1 parent 157c0d1 commit 713ce92

File tree

9 files changed

+155
-27
lines changed

9 files changed

+155
-27
lines changed

.changeset/itchy-books-report.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@rocket.chat/ui-voip": minor
3+
---
4+
5+
Introduces a presence indicator in the call widget and transfer modal (status bullet).

packages/ui-voip/src/v2/MediaCallContext.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { UserStatus } from '@rocket.chat/core-typings';
12
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
23
import type { Device } from '@rocket.chat/ui-contexts';
34
import { keepPreviousData, useQuery } from '@tanstack/react-query';
@@ -11,6 +12,7 @@ type InternalPeerInfo = {
1112
username?: string;
1213
avatarUrl?: string;
1314
callerId?: string;
15+
status?: UserStatus;
1416
};
1517

1618
type ExternalPeerInfo = {
@@ -191,6 +193,7 @@ export const usePeerAutocomplete = (onSelectPeer: (peerInfo: PeerInfo) => void,
191193
userId: localInfo.value,
192194
displayName: localInfo.label,
193195
avatarUrl: localInfo.avatarUrl,
196+
status: localInfo.status as UserStatus,
194197
});
195198
},
196199
value: peerInfo && 'userId' in peerInfo ? peerInfo.userId : undefined,

packages/ui-voip/src/v2/MediaCallProvider.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ const MediaCallProvider = ({ children }: { children: React.ReactNode }) => {
217217
value: user._id,
218218
label,
219219
identifier,
220+
status: user.status,
220221
avatarUrl: getAvatarPath({ username: user.username, etag: user.avatarETag }),
221222
};
222223
}) || []

packages/ui-voip/src/v2/MockedMediaCallProvider.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { UserStatus } from '@rocket.chat/core-typings';
12
import { useState } from 'react';
23

34
import MediaCallContext from './MediaCallContext';
@@ -21,6 +22,7 @@ const MediaCallProviderMock = ({
2122
avatarUrl,
2223
username: 'john.doe',
2324
callerId: '1234567890',
25+
status: UserStatus.ONLINE,
2426
});
2527
const [widgetState, setWidgetState] = useState<State>(state);
2628
const [muted, setMuted] = useState(false);

packages/ui-voip/src/v2/__snapshots__/MediaCallWidget.spec.tsx.snap

Lines changed: 107 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,24 @@ exports[`renders IncomingCall without crashing 1`] = `
7474
class="rcx-box rcx-box--full rcx-css-1tbw8nv"
7575
>
7676
<div
77-
class="rcx-box rcx-box--full rcx-css-ca8vtn"
78-
>
79-
John Doe
77+
class="rcx-box rcx-box--full rcx-css-tfsv0s"
78+
>
79+
<svg
80+
class="rcx-status-bullet rcx-status-bullet--online undefined rcx-status-bullet--small"
81+
height="24"
82+
viewBox="0 0 24 24"
83+
width="24"
84+
xmlns="http://www.w3.org/2000/svg"
85+
>
86+
<path
87+
d="M24 12.0001C24 18.6275 18.6274 24.0001 12 24.0001C5.37255 24.0001 -3.05176e-05 18.6275 -3.05176e-05 12.0001C-3.05176e-05 5.37271 5.37255 0.00012207 12 0.00012207C18.6274 0.00012207 24 5.37271 24 12.0001Z"
88+
/>
89+
</svg>
90+
<div
91+
class="rcx-box rcx-box--full rcx-css-nc7kyx"
92+
>
93+
John Doe
94+
</div>
8095
</div>
8196
<div
8297
class="rcx-box rcx-box--full rcx-css-1c8uhnw"
@@ -222,9 +237,24 @@ exports[`renders IncomingCallTransfer without crashing 1`] = `
222237
class="rcx-box rcx-box--full rcx-css-1tbw8nv"
223238
>
224239
<div
225-
class="rcx-box rcx-box--full rcx-css-ca8vtn"
226-
>
227-
John Doe
240+
class="rcx-box rcx-box--full rcx-css-tfsv0s"
241+
>
242+
<svg
243+
class="rcx-status-bullet rcx-status-bullet--online undefined rcx-status-bullet--small"
244+
height="24"
245+
viewBox="0 0 24 24"
246+
width="24"
247+
xmlns="http://www.w3.org/2000/svg"
248+
>
249+
<path
250+
d="M24 12.0001C24 18.6275 18.6274 24.0001 12 24.0001C5.37255 24.0001 -3.05176e-05 18.6275 -3.05176e-05 12.0001C-3.05176e-05 5.37271 5.37255 0.00012207 12 0.00012207C18.6274 0.00012207 24 5.37271 24 12.0001Z"
251+
/>
252+
</svg>
253+
<div
254+
class="rcx-box rcx-box--full rcx-css-nc7kyx"
255+
>
256+
John Doe
257+
</div>
228258
</div>
229259
<div
230260
class="rcx-box rcx-box--full rcx-css-1c8uhnw"
@@ -428,9 +458,24 @@ exports[`renders NewCall without crashing 1`] = `
428458
class="rcx-box rcx-box--full rcx-css-1tbw8nv"
429459
>
430460
<div
431-
class="rcx-box rcx-box--full rcx-css-ca8vtn"
461+
class="rcx-box rcx-box--full rcx-css-tfsv0s"
432462
>
433-
John Doe
463+
<svg
464+
class="rcx-status-bullet rcx-status-bullet--online undefined rcx-status-bullet--small"
465+
height="24"
466+
viewBox="0 0 24 24"
467+
width="24"
468+
xmlns="http://www.w3.org/2000/svg"
469+
>
470+
<path
471+
d="M24 12.0001C24 18.6275 18.6274 24.0001 12 24.0001C5.37255 24.0001 -3.05176e-05 18.6275 -3.05176e-05 12.0001C-3.05176e-05 5.37271 5.37255 0.00012207 12 0.00012207C18.6274 0.00012207 24 5.37271 24 12.0001Z"
472+
/>
473+
</svg>
474+
<div
475+
class="rcx-box rcx-box--full rcx-css-nc7kyx"
476+
>
477+
John Doe
478+
</div>
434479
</div>
435480
<div
436481
class="rcx-box rcx-box--full rcx-css-1c8uhnw"
@@ -570,9 +615,24 @@ exports[`renders OngoingCall without crashing 1`] = `
570615
class="rcx-box rcx-box--full rcx-css-1tbw8nv"
571616
>
572617
<div
573-
class="rcx-box rcx-box--full rcx-css-ca8vtn"
574-
>
575-
John Doe
618+
class="rcx-box rcx-box--full rcx-css-tfsv0s"
619+
>
620+
<svg
621+
class="rcx-status-bullet rcx-status-bullet--online undefined rcx-status-bullet--small"
622+
height="24"
623+
viewBox="0 0 24 24"
624+
width="24"
625+
xmlns="http://www.w3.org/2000/svg"
626+
>
627+
<path
628+
d="M24 12.0001C24 18.6275 18.6274 24.0001 12 24.0001C5.37255 24.0001 -3.05176e-05 18.6275 -3.05176e-05 12.0001C-3.05176e-05 5.37271 5.37255 0.00012207 12 0.00012207C18.6274 0.00012207 24 5.37271 24 12.0001Z"
629+
/>
630+
</svg>
631+
<div
632+
class="rcx-box rcx-box--full rcx-css-nc7kyx"
633+
>
634+
John Doe
635+
</div>
576636
</div>
577637
<div
578638
class="rcx-box rcx-box--full rcx-css-1c8uhnw"
@@ -744,9 +804,24 @@ exports[`renders OutgoingCall without crashing 1`] = `
744804
class="rcx-box rcx-box--full rcx-css-1tbw8nv"
745805
>
746806
<div
747-
class="rcx-box rcx-box--full rcx-css-ca8vtn"
748-
>
749-
John Doe
807+
class="rcx-box rcx-box--full rcx-css-tfsv0s"
808+
>
809+
<svg
810+
class="rcx-status-bullet rcx-status-bullet--online undefined rcx-status-bullet--small"
811+
height="24"
812+
viewBox="0 0 24 24"
813+
width="24"
814+
xmlns="http://www.w3.org/2000/svg"
815+
>
816+
<path
817+
d="M24 12.0001C24 18.6275 18.6274 24.0001 12 24.0001C5.37255 24.0001 -3.05176e-05 18.6275 -3.05176e-05 12.0001C-3.05176e-05 5.37271 5.37255 0.00012207 12 0.00012207C18.6274 0.00012207 24 5.37271 24 12.0001Z"
818+
/>
819+
</svg>
820+
<div
821+
class="rcx-box rcx-box--full rcx-css-nc7kyx"
822+
>
823+
John Doe
824+
</div>
750825
</div>
751826
<div
752827
class="rcx-box rcx-box--full rcx-css-1c8uhnw"
@@ -875,9 +950,24 @@ exports[`renders OutgoingCallTransfer without crashing 1`] = `
875950
class="rcx-box rcx-box--full rcx-css-1tbw8nv"
876951
>
877952
<div
878-
class="rcx-box rcx-box--full rcx-css-ca8vtn"
879-
>
880-
John Doe
953+
class="rcx-box rcx-box--full rcx-css-tfsv0s"
954+
>
955+
<svg
956+
class="rcx-status-bullet rcx-status-bullet--online undefined rcx-status-bullet--small"
957+
height="24"
958+
viewBox="0 0 24 24"
959+
width="24"
960+
xmlns="http://www.w3.org/2000/svg"
961+
>
962+
<path
963+
d="M24 12.0001C24 18.6275 18.6274 24.0001 12 24.0001C5.37255 24.0001 -3.05176e-05 18.6275 -3.05176e-05 12.0001C-3.05176e-05 5.37271 5.37255 0.00012207 12 0.00012207C18.6274 0.00012207 24 5.37271 24 12.0001Z"
964+
/>
965+
</svg>
966+
<div
967+
class="rcx-box rcx-box--full rcx-css-nc7kyx"
968+
>
969+
John Doe
970+
</div>
881971
</div>
882972
<div
883973
class="rcx-box rcx-box--full rcx-css-1c8uhnw"

packages/ui-voip/src/v2/components/PeerAutocomplete.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { AutoComplete, Option, Avatar, Field, FieldRow, FieldDescription, FieldError } from '@rocket.chat/fuselage';
1+
import { UserStatus } from '@rocket.chat/core-typings';
2+
import { AutoComplete, Option, Avatar, Field, FieldRow, FieldDescription, FieldError, StatusBullet, Box } from '@rocket.chat/fuselage';
23
import { useId } from 'react';
34
import { useTranslation } from 'react-i18next';
45

@@ -7,6 +8,7 @@ import { isFirstPeerAutocompleteOption } from '../MediaCallContext';
78
export type PeerAutocompleteOptions = {
89
value: string; // user id
910
label: string; // name or username
11+
status?: UserStatus;
1012
identifier?: string | number; // extension number
1113
avatarUrl?: string;
1214
};
@@ -44,7 +46,19 @@ const PeerAutocomplete = ({ options, filter, value, onChangeValue, onChangeFilte
4446
return <Option key={value} label={label} icon='phone-out' {...props} />;
4547
}
4648
const thisOption = options.find((option) => option.value === value);
47-
return <Option key={value} label={label} avatar={<Avatar size='x20' url={thisOption?.avatarUrl || ''} />} {...props} />;
49+
return (
50+
<Option
51+
key={value}
52+
label={
53+
<Box display='flex' flexDirection='row' alignItems='center'>
54+
<StatusBullet status={thisOption?.status} />
55+
<Box mis={4}>{label}</Box>
56+
</Box>
57+
}
58+
avatar={<Avatar size='x20' url={thisOption?.avatarUrl || ''} />}
59+
{...props}
60+
/>
61+
);
4862
}}
4963
renderSelected={() => null}
5064
/>

packages/ui-voip/src/v2/components/PeerInfo/InternalUser.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
import { Avatar, Box, Icon } from '@rocket.chat/fuselage';
1+
import { UserStatus } from '@rocket.chat/core-typings';
2+
import { Avatar, Box, Icon, StatusBullet } from '@rocket.chat/fuselage';
23

34
type InternalUserProps = {
45
displayName: string;
6+
status?: UserStatus;
57
avatarUrl?: string;
68
callerId?: string | number;
79
};
810

9-
const InternalUser = ({ displayName, avatarUrl, callerId }: InternalUserProps) => {
11+
const InternalUser = ({ displayName, avatarUrl, callerId, status }: InternalUserProps) => {
1012
return (
1113
<Box display='flex' flexDirection='row' id='rcx-media-call-widget-caller-info'>
1214
<Box mie={8}>{avatarUrl ? <Avatar url={avatarUrl} size='x20' /> : <Icon name='user' size='x20' />}</Box>
1315
<Box display='flex' flexDirection='column'>
14-
<Box display='flex' flexDirection='column' fontScale='p2b' color='default'>
15-
{displayName}
16+
<Box display='flex' flexDirection='row' alignItems='center' fontScale='p2b' color='default'>
17+
{status && <StatusBullet status={status} size='small' />}
18+
<Box mis={4}>{displayName}</Box>
1619
</Box>
1720
{callerId && (
1821
<Box fontScale='c1' color='secondary-info'>

packages/ui-voip/src/v2/components/PeerInfo/PeerInfo.stories.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { UserStatus } from '@rocket.chat/core-typings';
12
import type { Meta, StoryFn } from '@storybook/react';
23

34
import { PeerInfo } from '.';
@@ -13,6 +14,7 @@ export const InternalUser: StoryFn<typeof PeerInfo> = () => {
1314
<PeerInfo
1415
callerId='1234'
1516
displayName='John Doe'
17+
status={UserStatus.ONLINE}
1618
avatarUrl='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC
1719
4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
1820
IyMjIyMjIyMjIyMjIyMjL/wAARCAAoACgDASIAAhEBAxEB/8QAGwAAAgIDAQAAAAAAAAAAAAAAAAcEBgIDBQj/xAAuEAACAQQAAwcEAQUAAA

packages/ui-voip/src/v2/useMediaSession.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { UserStatus } from '@rocket.chat/core-typings';
12
import { MediaSignalingSession, CallState, CallRole } from '@rocket.chat/media-signaling';
2-
import { useUserAvatarPath } from '@rocket.chat/ui-contexts';
3+
import { useUserAvatarPath, useUserPresence } from '@rocket.chat/ui-contexts';
34
import { useEffect, useReducer, useMemo } from 'react';
45

56
import type { ConnectionState, PeerInfo, State } from './MediaCallContext';
@@ -62,8 +63,8 @@ const deriveConnectionStateFromCallState = (callState: CallState): ConnectionSta
6263
const reducer = (
6364
reducerState: SessionInfo,
6465
action: {
65-
type: 'toggleWidget' | 'selectPeer' | 'instance_updated' | 'reset' | 'mute' | 'hold';
66-
payload?: Partial<SessionInfo>;
66+
type: 'toggleWidget' | 'selectPeer' | 'instance_updated' | 'status_updated' | 'reset' | 'mute' | 'hold';
67+
payload?: Partial<SessionInfo> & { status?: UserStatus };
6768
},
6869
): SessionInfo => {
6970
if (action.type === 'mute') {
@@ -299,8 +300,15 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSession
299300
};
300301
}, [instance]);
301302

303+
const status = useUserPresence(mediaSession.peerInfo && 'userId' in mediaSession.peerInfo ? mediaSession.peerInfo.userId : undefined);
304+
305+
const peerInfo = useMemo(() => {
306+
return mediaSession.peerInfo ? { ...mediaSession.peerInfo, status: status?.status } : undefined;
307+
}, [mediaSession.peerInfo, status]);
308+
302309
return {
303310
...mediaSession,
311+
peerInfo,
304312
...cbs,
305-
};
313+
} as MediaSession;
306314
};

0 commit comments

Comments
 (0)