mirror of https://github.com/jitsi/jitsi-meet
- Remove references to the model ContactList. - Replace ContactListView with an empty element for attaching the React Component ContactListPanel, which has the same features as the old ContactListView. - Create new selector for getting non-fake participants for ContactListPanel's props. - Create a ParticipantCounter component to place in the contact list button. Previously ContactListView updated that but now it's a react component hooked into the participant state. - Remove pub/sub that was used only by ContactListView.pull/1965/head jitsi-meet_2431
parent
ed53f54628
commit
31729d7949
@ -1,19 +0,0 @@ |
||||
/** |
||||
* Class representing Contact model |
||||
* @class Contact |
||||
*/ |
||||
export default class Contact { |
||||
constructor(opts) { |
||||
let { |
||||
id, |
||||
avatar, |
||||
name, |
||||
isLocal |
||||
} = opts; |
||||
|
||||
this.id = id; |
||||
this.avatar = avatar || ''; |
||||
this.name = name || ''; |
||||
this.isLocal = isLocal || false; |
||||
} |
||||
} |
@ -1,93 +0,0 @@ |
||||
/* global APP */ |
||||
|
||||
import UIEvents from '../../../../service/UI/UIEvents'; |
||||
import ContactListView from './ContactListView'; |
||||
import Contact from './Contact'; |
||||
|
||||
/** |
||||
* Model for the Contact list. |
||||
* |
||||
* @class ContactList |
||||
*/ |
||||
class ContactList { |
||||
constructor(conference) { |
||||
this.conference = conference; |
||||
this.contacts = []; |
||||
this.roomLocked = false; |
||||
//setup ContactList Model into ContactList View
|
||||
ContactListView.setup(this); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if the current conference is locked. |
||||
* |
||||
* @returns {Boolean} |
||||
*/ |
||||
isLocked() { |
||||
return APP.store.getState()['features/base/conference'].locked; |
||||
} |
||||
|
||||
/** |
||||
* Adding new participant. |
||||
* |
||||
* @param id |
||||
* @param isLocal |
||||
*/ |
||||
addContact(id, isLocal) { |
||||
const exists = this.contacts.some(el => el.id === id); |
||||
|
||||
if (!exists) { |
||||
let newContact = new Contact({ id, isLocal }); |
||||
this.contacts.push(newContact); |
||||
APP.UI.emitEvent(UIEvents.CONTACT_ADDED, { id, isLocal }); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Removing participant. |
||||
* |
||||
* @param id |
||||
* @returns {Array|*} |
||||
*/ |
||||
removeContact(id) { |
||||
this.contacts = this.contacts.filter((el) => el.id !== id); |
||||
APP.UI.emitEvent(UIEvents.CONTACT_REMOVED, { id }); |
||||
return this.contacts; |
||||
} |
||||
|
||||
/** |
||||
* Changing the display name. |
||||
* |
||||
* @param id |
||||
* @param name |
||||
*/ |
||||
onDisplayNameChange (id, name) { |
||||
if(!name) |
||||
return; |
||||
if (id === 'localVideoContainer') { |
||||
id = APP.conference.getMyUserId(); |
||||
} |
||||
|
||||
let contacts = this.contacts.filter((el) => el.id === id); |
||||
contacts.forEach((el) => { |
||||
el.name = name; |
||||
}); |
||||
APP.UI.emitEvent(UIEvents.DISPLAY_NAME_CHANGED, { id, name }); |
||||
} |
||||
|
||||
/** |
||||
* Changing the avatar. |
||||
* |
||||
* @param id |
||||
* @param avatar |
||||
*/ |
||||
changeUserAvatar (id, avatar) { |
||||
let contacts = this.contacts.filter((el) => el.id === id); |
||||
contacts.forEach((el) => { |
||||
el.avatar = avatar; |
||||
}); |
||||
APP.UI.emitEvent(UIEvents.USER_AVATAR_CHANGED, { id, avatar }); |
||||
} |
||||
} |
||||
|
||||
export default ContactList; |
@ -0,0 +1,100 @@ |
||||
/* global APP */ |
||||
|
||||
import React, { Component } from 'react'; |
||||
|
||||
import UIEvents from '../../../../service/UI/UIEvents'; |
||||
|
||||
import { Avatar } from '../../base/participants'; |
||||
|
||||
/** |
||||
* Implements a React {@code Component} for showing a participant's avatar and |
||||
* name and emits an event when it has been clicked. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class ContactListItem extends Component { |
||||
/** |
||||
* Default values for {@code ContactListItem} component's properties. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* The link to the participant's avatar image. |
||||
*/ |
||||
avatarURI: React.PropTypes.string, |
||||
|
||||
/** |
||||
* An id attribute to set on the root of {@code ContactListItem}. Used |
||||
* by the torture tests. |
||||
*/ |
||||
id: React.PropTypes.string, |
||||
|
||||
/** |
||||
* The participant's display name. |
||||
*/ |
||||
name: React.PropTypes.string |
||||
}; |
||||
|
||||
/** |
||||
* Initializes new {@code ContactListItem} instance. |
||||
* |
||||
* @param {Object} props - The read-only properties with which the new |
||||
* instance is to be initialized. |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
this._onClick = this._onClick.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<li |
||||
className = 'clickable contact-list-item' |
||||
id = { this.props.id } |
||||
onClick = { this._onClick }> |
||||
{ this.props.avatarURI ? this._renderAvatar() : null } |
||||
<p className = 'contact-list-item-name'> |
||||
{ this.props.name } |
||||
</p> |
||||
</li> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Emits an event notifying the contact list item for the passed in |
||||
* participant ID has been clicked. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onClick() { |
||||
// FIXME move this call to a pinning action, which is what's happening
|
||||
// on the listener end, when the listener is properly hooked into redux.
|
||||
APP.UI.emitEvent(UIEvents.CONTACT_CLICKED, this.props.id); |
||||
} |
||||
|
||||
/** |
||||
* Renders the React Element for displaying the participant's avatar image. |
||||
* |
||||
* @private |
||||
* @returns {ReactElement} |
||||
*/ |
||||
_renderAvatar() { |
||||
return ( |
||||
<Avatar |
||||
className = 'icon-avatar avatar' |
||||
uri = { this.props.avatarURI } /> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default ContactListItem; |
@ -0,0 +1,182 @@ |
||||
import Button from '@atlaskit/button'; |
||||
import React, { Component } from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
import Avatar from '../../../../modules/UI/avatar/Avatar'; |
||||
|
||||
import { translate } from '../../base/i18n'; |
||||
import { getParticipants } from '../../base/participants'; |
||||
import { openInviteDialog } from '../../invite'; |
||||
|
||||
import ContactListItem from './ContactListItem'; |
||||
|
||||
const { PropTypes } = React; |
||||
|
||||
declare var interfaceConfig: Object; |
||||
|
||||
/** |
||||
* React component for showing a list of current conference participants, the |
||||
* current conference lock state, and a button to open the invite dialog. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class ContactListPanel extends Component { |
||||
/** |
||||
* Default values for {@code ContactListPanel} component's properties. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* Whether or not the conference is currently locked with a password. |
||||
*/ |
||||
_locked: PropTypes.bool, |
||||
|
||||
/** |
||||
* The participants to show in the contact list. |
||||
*/ |
||||
_participants: PropTypes.array, |
||||
|
||||
/** |
||||
* Invoked to open an invite dialog. |
||||
*/ |
||||
dispatch: PropTypes.func, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Initializes a new {@code ContactListPanel} instance. |
||||
* |
||||
* @param {Object} props - The read-only properties with which the new |
||||
* instance is to be initialized. |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
this._onOpenInviteDialog = this._onOpenInviteDialog.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
*/ |
||||
render() { |
||||
const { _locked, _participants, t } = this.props; |
||||
|
||||
return ( |
||||
<div className = 'contact-list-panel'> |
||||
<div className = 'title'> |
||||
{ t('contactlist', { pcount: _participants.length }) } |
||||
</div> |
||||
<div className = 'sideToolbarBlock first'> |
||||
<Button |
||||
appearance = 'primary' |
||||
className = 'contact-list-panel-invite-button' |
||||
id = 'addParticipantsBtn' |
||||
onClick = { this._onOpenInviteDialog } |
||||
type = 'button'> |
||||
{ t('addParticipants') } |
||||
</Button> |
||||
<div> |
||||
{ _locked |
||||
? this._renderLockedMessage() |
||||
: this._renderUnlockedMessage() } |
||||
</div> |
||||
</div> |
||||
<ul id = 'contacts'> |
||||
{ this._renderContacts() } |
||||
</ul> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Dispatches an action to open an invite dialog. |
||||
* |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onOpenInviteDialog() { |
||||
this.props.dispatch(openInviteDialog()); |
||||
} |
||||
|
||||
/** |
||||
* Renders React Elements for displaying information about each participant |
||||
* in the contact list. |
||||
* |
||||
* @private |
||||
* @returns {ReactElement[]} |
||||
*/ |
||||
_renderContacts() { |
||||
return this.props._participants.map(({ avatarId, id, name }) => |
||||
( // eslint-disable-line no-extra-parens
|
||||
<ContactListItem |
||||
avatarURI = { interfaceConfig.SHOW_CONTACTLIST_AVATARS |
||||
? Avatar.getAvatarUrl(avatarId) : null } |
||||
id = { id } |
||||
key = { id } |
||||
name = { name } /> |
||||
)); |
||||
} |
||||
|
||||
/** |
||||
* Renders a React Element for informing the conference is currently locked. |
||||
* |
||||
* @private |
||||
* @returns {ReactElement} |
||||
*/ |
||||
_renderLockedMessage() { |
||||
return ( |
||||
<p |
||||
className = 'form-control__hint form-control_full-width' |
||||
id = 'contactListroomLocked'> |
||||
<span className = 'icon-security-locked' /> |
||||
<span>{ this.props.t('roomLocked') }</span> |
||||
</p> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Renders a React Element for informing the conference is currently not |
||||
* locked. |
||||
* |
||||
* @private |
||||
* @returns {ReactElement} |
||||
*/ |
||||
_renderUnlockedMessage() { |
||||
return ( |
||||
<p |
||||
className = 'form-control__hint form-control_full-width' |
||||
id = 'contactListroomUnlocked'> |
||||
<span className = 'icon-security' /> |
||||
<span>{ this.props.t('roomUnlocked') }</span> |
||||
</p> |
||||
); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps (parts of) the Redux state to the associated {@code ContactListPanel}'s |
||||
* props. |
||||
* |
||||
* @param {Object} state - The Redux state. |
||||
* @private |
||||
* @returns {{ |
||||
* _locked: boolean, |
||||
* _participants: Array |
||||
* }} |
||||
*/ |
||||
function _mapStateToProps(state) { |
||||
return { |
||||
_locked: state['features/base/conference'].locked, |
||||
_participants: getParticipants(state) |
||||
}; |
||||
} |
||||
|
||||
export default translate(connect(_mapStateToProps)(ContactListPanel)); |
@ -0,0 +1,58 @@ |
||||
import React, { Component } from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
|
||||
import { getParticipantCount } from '../../base/participants'; |
||||
|
||||
/** |
||||
* React component for showing a badge with the current count of conference |
||||
* participants. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class ParticipantCounter extends Component { |
||||
/** |
||||
* {@code ParticipantCounter} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* The number of participants in the conference. |
||||
*/ |
||||
_count: React.PropTypes.number |
||||
}; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<span className = 'badge-round'> |
||||
<span id = 'numberOfParticipants'> |
||||
{ this.props._count } |
||||
</span> |
||||
</span> |
||||
); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Maps (parts of) the Redux state to the associated |
||||
* {@code ParticipantCounter}'s props. |
||||
* |
||||
* @param {Object} state - The Redux state. |
||||
* @private |
||||
* @returns {{ |
||||
* _count: number |
||||
* }} |
||||
*/ |
||||
function _mapStateToProps(state) { |
||||
return { |
||||
_count: getParticipantCount(state) |
||||
}; |
||||
} |
||||
|
||||
export default connect(_mapStateToProps)(ParticipantCounter); |
@ -0,0 +1,2 @@ |
||||
export { default as ContactListPanel } from './ContactListPanel'; |
||||
export { default as ParticipantCounter } from './ParticipantCounter'; |
@ -0,0 +1 @@ |
||||
export * from './components'; |
Loading…
Reference in new issue