Skip to content

Commit

Permalink
feat(WebexMember): split component into space members and meeting par…
Browse files Browse the repository at this point in the history
…ticipant
  • Loading branch information
thescripted committed Mar 30, 2022
1 parent be778a9 commit d1edde8
Show file tree
Hide file tree
Showing 11 changed files with 581 additions and 466 deletions.
5 changes: 3 additions & 2 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ export const parameters = {
[
'Webex Avatar',
'Webex Member Roster',
'Webex Member',
],
'Messaging',
[
'Webex Messaging',
'Webex Activity Stream',
'Webex Activity',
'Webex Member',
],
'Meetings',
[
Expand All @@ -58,7 +58,8 @@ export const parameters = {
'Webex In-Meeting',
'Webex Local Media',
'Webex Remote Media',
'Webex Meeting Control'
'Webex Meeting Control',
'Webex Meeting Participant',
],
],
},
Expand Down
89 changes: 89 additions & 0 deletions src/components/WebexMeetingParticipant/WebexMeetingParticipant.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import {DestinationType} from '@webex/component-adapter-interfaces';
import Spinner from '../generic/Spinner/Spinner';
import Icon from '../generic/Icon/Icon';
import {
useMembers,
useMe,
useOrganization,
usePerson,
} from '../hooks';
import WebexAvatar from '../WebexAvatar/WebexAvatar';
import webexComponentClasses from '../helpers';

/**
* Displays a webex meeting participant.
*
* @param {object} props Data passed to the component
* @param {string} props.className Custom CSS class to apply
* @param {string} props.meetingID ID of the meeting for which to get members
* @param {boolean} props.displayStatus Whether or not to display the user's status
* @param {string} props.personID ID of the member for which to display avatar
* @param {object} props.style Custom style to apply
* @returns {object} JSX of the component
*
*/
export default function WebexMeetingParticipant({
className,
meetingID,
displayStatus,
personID,
style,
}) {
const {displayName, orgID, emails} = usePerson(personID);
const me = useMe();
const members = useMembers(meetingID, DestinationType.MEETING);
const member = members
.find((itemMember) => itemMember.ID === personID);
const organization = useOrganization(orgID);

const isMuted = member?.muted;
const isSpeaking = member?.speaking;
const isExternal = orgID !== undefined && me.orgID !== undefined && me.orgID !== orgID;
const isSharing = member?.sharing;
const isInMeeting = member?.inMeeting;
const showMe = me.ID === personID;
const isHost = member?.host;
const isGuest = member?.guest;

const roles = [
showMe && 'You',
isHost && 'Host',
isSharing && 'Presenter',
].filter((role) => role);
const emailDomain = emails?.[0]?.split('@')[1] || <i>Unknown organization</i>;

const [cssClasses, sc] = webexComponentClasses('meeting-participant', className);

return (
<div className={cssClasses} style={style}>
<WebexAvatar personID={personID} displayStatus={displayStatus} className={sc('avatar')} />
<div className={sc('details')}>
<div className={sc('name')}>
{(displayName ?? <Spinner size={18} />) || <i>Name not available</i>}
{isGuest && <span className={sc('guest')}> (Guest)</span>}
</div>
{roles.length > 0 && <div className={sc('roles')}>{roles.join(', ')}</div>}
{isExternal && <div className={sc('organization')}>{organization.name || emailDomain}</div>}
</div>
{isInMeeting && isSharing && <Icon name="content-share" size={16} className={sc('sharing')} />}
{isInMeeting && isSpeaking && <Icon name="microphone" size={16} className={sc('speaking')} />}
{isInMeeting && isMuted && <Icon name="microphone-muted" size={16} className={sc('muted')} />}
</div>
);
}

WebexMeetingParticipant.propTypes = {
className: PropTypes.string,
meetingID: PropTypes.string.isRequired,
displayStatus: PropTypes.bool,
personID: PropTypes.string.isRequired,
style: PropTypes.shape(),
};

WebexMeetingParticipant.defaultProps = {
className: '',
displayStatus: false,
style: undefined,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
$C: #{$WEBEX_COMPONENTS_CLASS_PREFIX}-meeting-participant;

.#{$C} {
display: flex;
align-items: center;
color: var(--wxc-text-color);

.#{$C}__avatar {
height: 2rem;
width: 2rem;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}

.#{$C}__details {
flex: 1;
min-width: 0;
margin-left: 0.75rem;
}

.#{$C}__name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 0.063rem;

.#{$C}__guest {
color: var(--wxc-secondary-text-color);
}
}

.#{$C}__roles {
font-size: 0.875rem;
line-height: 1rem;
color: var(--wxc-secondary-text-color);
}

.#{$C}__organization {
font-size: 0.875rem;
line-height: 1rem;
color: var(--wxc-warning-color);
}

.#{$C}__sharing {
margin-right: 2.5rem;
}

.#{$C}__speaking {
color: var(--wxc-speaking-color);
}

.#{$C}__muted {
color: var(--wxc-muted-color);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import WebexMeetingParticipant from './WebexMeetingParticipant';

export default {
title: 'Meetings/Webex Meeting Participant',
component: WebexMeetingParticipant,
decorators: [(Story) => <div style={{width: '20rem'}}><Story /></div>],
};

const Template = (args) => <WebexMeetingParticipant {...args} />;

export const Muted = Template.bind({});
Muted.args = {
meetingID: 'meeting2',
personID: 'user2',
};

export const Host = Template.bind({});
Host.args = {
meetingID: 'meeting2',
personID: 'user4',
};

export const Guest = Template.bind({});
Guest.args = {
meetingID: 'meeting2',
personID: 'user6',
};

export const ScreenSharing = Template.bind({});
ScreenSharing.args = {
meetingID: 'meeting2',
personID: 'user3',
};
Loading

0 comments on commit d1edde8

Please sign in to comment.