Skip to content

Commit 6991e6e

Browse files
committed
feat(WebexMember): split component into space members and meeting participants
1 parent be778a9 commit 6991e6e

18 files changed

+1267
-978
lines changed

.storybook/preview.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ export const parameters = {
4141
'Platform',
4242
[
4343
'Webex Avatar',
44-
'Webex Member Roster',
45-
'Webex Member',
4644
],
4745
'Messaging',
4846
[
4947
'Webex Messaging',
5048
'Webex Activity Stream',
5149
'Webex Activity',
50+
'Webex Member',
51+
'Webex Member Roster',
5252
],
5353
'Meetings',
5454
[
@@ -58,7 +58,9 @@ export const parameters = {
5858
'Webex In-Meeting',
5959
'Webex Local Media',
6060
'Webex Remote Media',
61-
'Webex Meeting Control'
61+
'Webex Meeting Control',
62+
'Webex Meeting Participant',
63+
'Webex Meeting Roster',
6264
],
6365
],
6466
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import {DestinationType} from '@webex/component-adapter-interfaces';
4+
import Spinner from '../generic/Spinner/Spinner';
5+
import Icon from '../generic/Icon/Icon';
6+
import {
7+
useMembers,
8+
useMe,
9+
useOrganization,
10+
usePerson,
11+
} from '../hooks';
12+
import WebexAvatar from '../WebexAvatar/WebexAvatar';
13+
import webexComponentClasses from '../helpers';
14+
15+
/**
16+
* Displays a webex meeting participant.
17+
*
18+
* @param {object} props Data passed to the component
19+
* @param {string} props.className Custom CSS class to apply
20+
* @param {string} props.meetingID ID of the meeting for which to get members
21+
* @param {boolean} props.displayStatus Whether or not to display the user's status
22+
* @param {string} props.personID ID of the member for which to display avatar
23+
* @param {object} props.style Custom style to apply
24+
* @returns {object} JSX of the component
25+
*
26+
*/
27+
export default function WebexMeetingParticipant({
28+
className,
29+
meetingID,
30+
displayStatus,
31+
personID,
32+
style,
33+
}) {
34+
const {displayName, orgID, emails} = usePerson(personID);
35+
const me = useMe();
36+
const members = useMembers(meetingID, DestinationType.MEETING);
37+
const member = members
38+
.find((itemMember) => itemMember.ID === personID);
39+
const organization = useOrganization(orgID);
40+
41+
const isMuted = member?.muted;
42+
const isSpeaking = member?.speaking;
43+
const isExternal = orgID !== undefined && me.orgID !== undefined && me.orgID !== orgID;
44+
const isSharing = member?.sharing;
45+
const isInMeeting = member?.inMeeting;
46+
const showMe = me.ID === personID;
47+
const isHost = member?.host;
48+
const isGuest = member?.guest;
49+
50+
const roles = [
51+
showMe && 'You',
52+
isHost && 'Host',
53+
isSharing && 'Presenter',
54+
].filter((role) => role);
55+
const emailDomain = emails?.[0]?.split('@')[1] || <i>Unknown organization</i>;
56+
57+
const [cssClasses, sc] = webexComponentClasses('meeting-participant', className);
58+
59+
return (
60+
<div className={cssClasses} style={style}>
61+
<WebexAvatar personID={personID} displayStatus={displayStatus} className={sc('avatar')} />
62+
<div className={sc('details')}>
63+
<div className={sc('name')}>
64+
{(displayName ?? <Spinner size={18} />) || <i>Name not available</i>}
65+
{isGuest && <span className={sc('guest')}> (Guest)</span>}
66+
</div>
67+
{roles.length > 0 && <div className={sc('roles')}>{roles.join(', ')}</div>}
68+
{isExternal && <div className={sc('organization')}>{organization.name || emailDomain}</div>}
69+
</div>
70+
{isInMeeting && isSharing && <Icon name="content-share" size={16} className={sc('sharing')} />}
71+
{isInMeeting && isSpeaking && <Icon name="microphone" size={16} className={sc('speaking')} />}
72+
{isInMeeting && isMuted && <Icon name="microphone-muted" size={16} className={sc('muted')} />}
73+
</div>
74+
);
75+
}
76+
77+
WebexMeetingParticipant.propTypes = {
78+
className: PropTypes.string,
79+
meetingID: PropTypes.string.isRequired,
80+
displayStatus: PropTypes.bool,
81+
personID: PropTypes.string.isRequired,
82+
style: PropTypes.shape(),
83+
};
84+
85+
WebexMeetingParticipant.defaultProps = {
86+
className: '',
87+
displayStatus: false,
88+
style: undefined,
89+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
$C: #{$WEBEX_COMPONENTS_CLASS_PREFIX}-meeting-participant;
2+
3+
.#{$C} {
4+
display: flex;
5+
align-items: center;
6+
color: var(--wxc-text-color);
7+
8+
.#{$C}__avatar {
9+
height: 2rem;
10+
width: 2rem;
11+
margin-top: 0.5rem;
12+
margin-bottom: 0.5rem;
13+
}
14+
15+
.#{$C}__details {
16+
flex: 1;
17+
min-width: 0;
18+
margin-left: 0.75rem;
19+
}
20+
21+
.#{$C}__name {
22+
white-space: nowrap;
23+
overflow: hidden;
24+
text-overflow: ellipsis;
25+
margin-top: 0.063rem;
26+
27+
.#{$C}__guest {
28+
color: var(--wxc-secondary-text-color);
29+
}
30+
}
31+
32+
.#{$C}__roles {
33+
font-size: 0.875rem;
34+
line-height: 1rem;
35+
color: var(--wxc-secondary-text-color);
36+
}
37+
38+
.#{$C}__organization {
39+
font-size: 0.875rem;
40+
line-height: 1rem;
41+
color: var(--wxc-warning-color);
42+
}
43+
44+
.#{$C}__sharing {
45+
margin-right: 2.5rem;
46+
}
47+
48+
.#{$C}__speaking {
49+
color: var(--wxc-speaking-color);
50+
}
51+
52+
.#{$C}__muted {
53+
color: var(--wxc-muted-color);
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import WebexMeetingParticipant from './WebexMeetingParticipant';
3+
4+
export default {
5+
title: 'Meetings/Webex Meeting Participant',
6+
component: WebexMeetingParticipant,
7+
decorators: [(Story) => <div style={{width: '20rem'}}><Story /></div>],
8+
};
9+
10+
const Template = (args) => <WebexMeetingParticipant {...args} />;
11+
12+
export const Muted = Template.bind({});
13+
Muted.args = {
14+
meetingID: 'meeting2',
15+
personID: 'user2',
16+
};
17+
18+
export const Host = Template.bind({});
19+
Host.args = {
20+
meetingID: 'meeting2',
21+
personID: 'user4',
22+
};
23+
24+
export const Guest = Template.bind({});
25+
Guest.args = {
26+
meetingID: 'meeting2',
27+
personID: 'user6',
28+
};
29+
30+
export const ScreenSharing = Template.bind({});
31+
ScreenSharing.args = {
32+
meetingID: 'meeting2',
33+
personID: 'user3',
34+
};

0 commit comments

Comments
 (0)