bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/user-list/component.jsx

363 lines
9.8 KiB
React
Raw Normal View History

2016-06-29 03:52:03 +08:00
import React, { Component, PropTypes } from 'react';
import { withRouter } from 'react-router';
2016-06-28 21:10:20 +08:00
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import styles from './styles.scss';
2016-06-29 22:24:27 +08:00
import cx from 'classnames';
import { defineMessages, injectIntl } from 'react-intl';
2016-05-20 02:22:56 +08:00
import UserListItem from './user-list-item/component.jsx';
import ChatListItem from './chat-list-item/component.jsx';
2017-05-05 22:37:01 +08:00
import KEY_CODES from '/imports/utils/keyCodes';
2016-05-20 02:22:56 +08:00
2016-06-29 03:52:03 +08:00
const propTypes = {
openChats: PropTypes.array.isRequired,
users: PropTypes.array.isRequired,
};
const defaultProps = {
};
2016-06-28 21:10:20 +08:00
const listTransition = {
enter: styles.enter,
enterActive: styles.enterActive,
appear: styles.appear,
appearActive: styles.appearActive,
leave: styles.leave,
leaveActive: styles.leaveActive,
};
class UserList extends Component {
constructor(props) {
super(props);
this.state = {
2016-09-15 01:48:50 +08:00
compact: this.props.compact,
};
this.rovingIndex = this.rovingIndex.bind(this);
2017-05-11 02:10:27 +08:00
this.focusList = this.focusList.bind(this);
this.focusListItem = this.focusListItem.bind(this);
2017-05-06 03:11:28 +08:00
this.counter = -1;
}
2017-05-11 02:10:27 +08:00
focusList(activeElement, list) {
activeElement.tabIndex = -1;
this.counter = 0;
list.tabIndex = 0;
list.focus();
}
focusListItem(active, direction, element, count) {
2017-05-11 02:24:09 +08:00
function select() {
element.tabIndex = 0;
element.focus();
}
2017-05-11 02:10:27 +08:00
active.tabIndex = -1;
switch (direction) {
case 'down':
element.childNodes[this.counter].tabIndex = 0;
element.childNodes[this.counter].focus();
this.counter++;
break;
case 'up':
this.counter--;
element.childNodes[this.counter].tabIndex = 0;
element.childNodes[this.counter].focus();
break;
case 'upLoopUp':
2017-05-11 02:39:03 +08:00
case 'upLoopDown':
2017-05-11 02:10:27 +08:00
this.counter = count - 1;
2017-05-11 02:24:09 +08:00
select();
2017-05-11 02:10:27 +08:00
break;
2017-05-11 02:39:03 +08:00
case 'downLoopDown':
this.counter = -1;
select();
break;
case 'downLoopUp':
this.counter = 1;
select();
2017-05-11 02:10:27 +08:00
break;
}
}
2017-05-05 05:41:09 +08:00
rovingIndex(...Args) {
2017-05-13 02:11:42 +08:00
const { users, openChats } = this.props;
2017-05-05 22:37:01 +08:00
let active = document.activeElement;
2017-05-06 03:11:28 +08:00
let list;
let items;
let count;
2017-05-05 22:37:01 +08:00
2017-05-11 02:10:27 +08:00
switch (Args[1]) {
case 'users':
2017-05-11 22:11:17 +08:00
list = this._usersList;
items = this._userItems;
2017-05-11 02:10:27 +08:00
count = users.length;
break;
case 'messages':
2017-05-11 22:11:17 +08:00
list = this._msgsList;
items = this._msgItems;
2017-05-11 02:10:27 +08:00
count = openChats.length;
break;
2017-05-05 05:41:09 +08:00
}
2017-05-11 02:10:27 +08:00
if (Args[0].keyCode === KEY_CODES.ESCAPE
|| this.counter === -1
|| this.counter > count) {
this.focusList(active, list);
2017-05-06 03:11:28 +08:00
}
2017-05-05 22:37:01 +08:00
if (Args[0].keyCode === KEY_CODES.ENTER
|| Args[0].keyCode === KEY_CODES.ARROW_RIGHT
|| Args[0].keyCode === KEY_CODES.ARROW_LEFT) {
2017-05-06 03:11:28 +08:00
active.firstChild.click();
2017-05-05 22:37:01 +08:00
}
if (Args[0].keyCode === KEY_CODES.ARROW_DOWN) {
if (this.counter < count) {
2017-05-13 02:11:42 +08:00
this.focusListItem(active, 'down', items);
2017-05-11 02:10:27 +08:00
}else if (this.counter === count) {
2017-05-13 02:11:42 +08:00
this.focusListItem(active, 'downLoopDown', list);
2017-05-11 02:10:27 +08:00
}else if (this.counter === 0) {
2017-05-13 02:11:42 +08:00
this.focusListItem(active, 'downLoopUp', list);
}
}
2017-05-05 22:37:01 +08:00
if (Args[0].keyCode === KEY_CODES.ARROW_UP) {
2017-05-06 03:11:28 +08:00
if (this.counter < count && this.counter !== 0) {
2017-05-13 02:11:42 +08:00
this.focusListItem(active, 'up', items);
2017-05-11 02:10:27 +08:00
}else if (this.counter === 0) {
2017-05-13 02:11:42 +08:00
this.focusListItem(active, 'upLoopUp', list, count);
2017-05-11 02:10:27 +08:00
}else if (this.counter === count) {
2017-05-13 02:11:42 +08:00
this.focusListItem(active, 'upLoopDown', list, count);
}
2017-05-05 22:37:01 +08:00
}
}
componentDidMount() {
2017-05-05 05:41:09 +08:00
let _this = this;
if (!this.state.compact) {
2017-05-13 02:11:42 +08:00
this._msgsList.addEventListener('keypress', function (event) {
_this.rovingIndex.call(this, event, 'messages');
});
2017-05-13 02:11:42 +08:00
this._usersList.addEventListener('keypress', function (event) {
_this.rovingIndex.call(this, event, 'users');
2017-05-05 05:41:09 +08:00
});
}
}
2017-05-05 22:37:01 +08:00
componentWillUnmount() {
2017-05-13 02:11:42 +08:00
this._msgsList.removeEventListener('keypress', function (event) {}, false);
this._usersList.removeEventListener('keypress', function (event) {}, false);
2017-05-05 22:37:01 +08:00
}
2016-05-20 02:22:56 +08:00
render() {
return (
<div className={styles.userList}>
2016-05-26 03:29:22 +08:00
{this.renderHeader()}
{this.renderContent()}
</div>
);
}
renderHeader() {
const { intl } = this.props;
2016-05-26 03:29:22 +08:00
return (
<div className={styles.header}>
2016-09-15 01:48:50 +08:00
{
!this.state.compact ?
2017-05-13 01:45:41 +08:00
<div className={styles.headerTitle} role="banner">
{intl.formatMessage(intlMessages.participantsTitle)}
2017-05-13 01:45:41 +08:00
</div> : null
}
2016-05-26 03:29:22 +08:00
</div>
);
2016-05-26 03:29:22 +08:00
}
2016-05-20 02:22:56 +08:00
2016-05-26 03:29:22 +08:00
renderContent() {
return (
<div className={styles.content}>
{this.renderMessages()}
{this.renderParticipants()}
2016-05-20 02:22:56 +08:00
</div>
);
}
2016-05-26 03:29:22 +08:00
renderMessages() {
2016-06-29 03:52:03 +08:00
const {
openChats,
2016-06-30 22:45:19 +08:00
openChat,
intl,
2016-06-29 03:52:03 +08:00
} = this.props;
2016-05-26 03:29:22 +08:00
return (
<div className={styles.messages}>
2016-09-15 01:48:50 +08:00
{
!this.state.compact ?
2017-05-13 01:45:41 +08:00
<div className={styles.smallTitle} role="banner">
{intl.formatMessage(intlMessages.messagesTitle)}
2017-05-13 01:45:41 +08:00
</div> : <hr className={styles.separator}></hr>
}
2017-05-11 22:11:17 +08:00
<div
tabIndex={0}
className={styles.scrollableList}
ref={(r) => this._msgsList = r}>
2016-06-28 21:10:20 +08:00
<ReactCSSTransitionGroup
transitionName={listTransition}
transitionAppear={true}
transitionEnter={true}
transitionLeave={false}
transitionAppearTimeout={0}
transitionEnterTimeout={0}
transitionLeaveTimeout={0}
2017-05-13 01:45:41 +08:00
component="div"
2017-05-11 22:11:17 +08:00
className={cx(styles.chatsList, styles.scrollableList)}>
<div ref={(r) => this._msgItems = r}>
2016-06-29 03:52:03 +08:00
{openChats.map(chat => (
2016-06-28 21:10:20 +08:00
<ChatListItem
compact={this.state.compact}
2016-06-28 21:10:20 +08:00
key={chat.id}
2016-06-30 22:45:19 +08:00
openChat={openChat}
chat={chat}
tabIndex={-1} />
2016-06-28 21:10:20 +08:00
))}
2017-05-11 22:11:17 +08:00
</div>
2016-06-28 21:10:20 +08:00
</ReactCSSTransitionGroup>
</div>
2016-05-26 03:29:22 +08:00
</div>
);
2016-05-26 03:29:22 +08:00
}
renderParticipants() {
2016-06-29 03:52:03 +08:00
const {
users,
currentUser,
isBreakoutRoom,
intl,
makeCall,
meeting,
2016-06-29 03:52:03 +08:00
} = this.props;
2016-06-28 21:10:20 +08:00
2017-04-29 02:28:55 +08:00
const userActions = {
openChat: {
label: intl.formatMessage(intlMessages.ChatLabel),
handler: (router, user) => router.push(`/users/chat/${user.id}`),
icon: 'chat',
},
clearStatus: {
label: intl.formatMessage(intlMessages.ClearStatusLabel),
handler: user => makeCall('setEmojiStatus', user.id, 'none'),
2017-04-29 02:28:55 +08:00
icon: 'clear_status',
},
setPresenter: {
label: intl.formatMessage(intlMessages.MakePresenterLabel),
handler: user => makeCall('assignPresenter', user.id),
2017-04-29 02:28:55 +08:00
icon: 'presentation',
},
kick: {
label: intl.formatMessage(intlMessages.KickUserLabel),
handler: user => makeCall('kickUser', user.id),
2017-04-29 02:28:55 +08:00
icon: 'circle_close',
},
mute: {
label: intl.formatMessage(intlMessages.MuteUserAudioLabel),
handler: user => makeCall('muteUser', user.id),
2017-04-29 02:28:55 +08:00
icon: 'audio_off',
},
unmute: {
label: intl.formatMessage(intlMessages.UnmuteUserAudioLabel),
handler: user => makeCall('unmuteUser', user.id),
2017-04-29 02:28:55 +08:00
icon: 'audio_on',
},
};
2016-05-26 03:29:22 +08:00
return (
<div className={styles.participants}>
2016-09-15 01:48:50 +08:00
{
!this.state.compact ?
2017-05-13 01:45:41 +08:00
<div className={styles.smallTitle} role="banner">
{intl.formatMessage(intlMessages.usersTitle)}
2016-09-15 01:48:50 +08:00
&nbsp;({users.length})
2017-05-13 01:45:41 +08:00
</div> : <hr className={styles.separator}></hr>
}
2017-05-11 22:11:17 +08:00
<div
className={styles.scrollableList}
tabIndex={0}
ref={(r) => this._usersList = r}>
<ReactCSSTransitionGroup
transitionName={listTransition}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionAppearTimeout={0}
transitionEnterTimeout={0}
transitionLeaveTimeout={0}
2017-05-13 01:45:41 +08:00
component="div"
2017-05-11 22:11:17 +08:00
className={cx(styles.participantsList, styles.scrollableList)}>
<div ref={(r) => this._userItems = r}>
{
users.map(user => (
<UserListItem
compact={this.state.compact}
key={user.id}
isBreakoutRoom={isBreakoutRoom}
user={user}
currentUser={currentUser}
userActions={userActions}
/>))
}
</div>
</ReactCSSTransitionGroup>
</div>
2016-05-26 03:29:22 +08:00
</div>
);
2016-05-26 03:29:22 +08:00
}
2016-05-20 02:22:56 +08:00
}
const intlMessages = defineMessages({
usersTitle: {
id: 'app.userlist.usersTitle',
2017-04-06 06:49:05 +08:00
description: 'Title for the Header',
},
messagesTitle: {
id: 'app.userlist.messagesTitle',
2017-04-06 06:49:05 +08:00
description: 'Title for the messages list',
},
participantsTitle: {
id: 'app.userlist.participantsTitle',
2017-04-06 06:49:05 +08:00
description: 'Title for the Users list',
},
2017-04-29 02:28:55 +08:00
ChatLabel: {
id: 'app.userlist.menu.chat.label',
description: 'Save the changes and close the settings menu',
},
ClearStatusLabel: {
id: 'app.userlist.menu.clearStatus.label',
description: 'Clear the emoji status of this user',
},
MakePresenterLabel: {
id: 'app.userlist.menu.makePresenter.label',
description: 'Set this user to be the presenter in this meeting',
},
KickUserLabel: {
id: 'app.userlist.menu.kickUser.label',
description: 'Forcefully remove this user from the meeting',
},
MuteUserAudioLabel: {
id: 'app.userlist.menu.muteUserAudio.label',
description: 'Forcefully mute this user',
},
UnmuteUserAudioLabel: {
id: 'app.userlist.menu.unmuteUserAudio.label',
description: 'Forcefully unmute this user',
},
});
2016-06-29 03:52:03 +08:00
UserList.propTypes = propTypes;
export default withRouter(injectIntl(UserList));