Merged contents of MoreDirectChannelsModal and ChannelMembersModal into UserList
Этот коммит содержится в:
коммит произвёл
Harrison Healey
родитель
af2a64b6bd
Коммит
f7b04f0f27
@@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import FilteredUserList from './filtered_user_list.jsx';
|
||||||
import LoadingScreen from './loading_screen.jsx';
|
import LoadingScreen from './loading_screen.jsx';
|
||||||
import MemberList from './member_list.jsx';
|
|
||||||
import ChannelInviteModal from './channel_invite_modal.jsx';
|
import ChannelInviteModal from './channel_invite_modal.jsx';
|
||||||
|
|
||||||
import UserStore from '../stores/user_store.jsx';
|
import UserStore from '../stores/user_store.jsx';
|
||||||
@@ -24,6 +24,8 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
this.handleRemove = this.handleRemove.bind(this);
|
this.handleRemove = this.handleRemove.bind(this);
|
||||||
|
|
||||||
|
this.createRemoveMemberButton = this.createRemoveMemberButton.bind(this);
|
||||||
|
|
||||||
// the rest of the state gets populated when the modal is shown
|
// the rest of the state gets populated when the modal is shown
|
||||||
this.state = {
|
this.state = {
|
||||||
showInviteModal: false
|
showInviteModal: false
|
||||||
@@ -51,24 +53,10 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = UserStore.getActiveOnlyProfiles();
|
// clone the member list since we mutate it later on
|
||||||
const memberList = extraInfo.members;
|
const memberList = extraInfo.members.map((member) => {
|
||||||
|
return Object.assign({}, member);
|
||||||
const nonmemberList = [];
|
});
|
||||||
for (const id in users) {
|
|
||||||
if (users.hasOwnProperty(id)) {
|
|
||||||
let found = false;
|
|
||||||
for (let i = 0; i < memberList.length; i++) {
|
|
||||||
if (memberList[i].id === id) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
nonmemberList.push(users[id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareByUsername(a, b) {
|
function compareByUsername(a, b) {
|
||||||
if (a.username < b.username) {
|
if (a.username < b.username) {
|
||||||
@@ -81,15 +69,14 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
memberList.sort(compareByUsername);
|
memberList.sort(compareByUsername);
|
||||||
nonmemberList.sort(compareByUsername);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nonmemberList,
|
|
||||||
memberList,
|
memberList,
|
||||||
loading: false
|
loading: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
onShow() {
|
onShow() {
|
||||||
|
// TODO ugh
|
||||||
if ($(window).width() > 768) {
|
if ($(window).width() > 768) {
|
||||||
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
|
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
|
||||||
}
|
}
|
||||||
@@ -116,41 +103,25 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleRemove(userId) {
|
handleRemove(user) {
|
||||||
// Make sure the user is a member of the channel
|
const userId = user.id;
|
||||||
const memberList = this.state.memberList;
|
|
||||||
let found = false;
|
|
||||||
for (let i = 0; i < memberList.length; i++) {
|
|
||||||
if (memberList[i].id === userId) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {};
|
const data = {};
|
||||||
data.user_id = userId;
|
data.user_id = userId;
|
||||||
|
|
||||||
Client.removeChannelMember(ChannelStore.getCurrentId(), data,
|
Client.removeChannelMember(
|
||||||
|
ChannelStore.getCurrentId(),
|
||||||
|
data,
|
||||||
() => {
|
() => {
|
||||||
let oldMember;
|
const memberList = this.state.memberList.slice();
|
||||||
for (let i = 0; i < memberList.length; i++) {
|
for (let i = 0; i < memberList.length; i++) {
|
||||||
if (userId === memberList[i].id) {
|
if (userId === memberList[i].id) {
|
||||||
oldMember = memberList[i];
|
|
||||||
memberList.splice(i, 1);
|
memberList.splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonmemberList = this.state.nonmemberList;
|
this.setState({memberList});
|
||||||
if (oldMember) {
|
|
||||||
nonmemberList.push(oldMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({memberList, nonmemberList});
|
|
||||||
AsyncClient.getChannelExtraInfo();
|
AsyncClient.getChannelExtraInfo();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
@@ -158,30 +129,39 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
createRemoveMemberButton({user}) {
|
||||||
|
if (user.id === UserStore.getCurrentId()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='btn btn-primary btn-message'
|
||||||
|
onClick={this.handleRemove.bind(this, user)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='member_item.removeMember'
|
||||||
|
defaultMessage='Remove Member'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
var maxHeight = 1000;
|
var maxHeight = 1000;
|
||||||
if (Utils.windowHeight() <= 1200) {
|
if (Utils.windowHeight() <= 1200) {
|
||||||
maxHeight = Utils.windowHeight() - 300;
|
maxHeight = Utils.windowHeight() - 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentMember = ChannelStore.getCurrentMember();
|
|
||||||
let isAdmin = false;
|
|
||||||
if (currentMember) {
|
|
||||||
isAdmin = Utils.isAdmin(currentMember.roles) || Utils.isAdmin(UserStore.getCurrentUser().roles);
|
|
||||||
}
|
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (this.state.loading) {
|
if (this.state.loading) {
|
||||||
content = (<LoadingScreen/>);
|
content = (<LoadingScreen/>);
|
||||||
} else {
|
} else {
|
||||||
content = (
|
content = (
|
||||||
<div className='team-member-list'>
|
<FilteredUserList
|
||||||
<MemberList
|
users={this.state.memberList}
|
||||||
memberList={this.state.memberList}
|
actions={[this.createRemoveMemberButton]}
|
||||||
isAdmin={isAdmin}
|
/>
|
||||||
handleRemove={this.handleRemove}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
129
web/react/components/filtered_user_list.jsx
Обычный файл
129
web/react/components/filtered_user_list.jsx
Обычный файл
@@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import UserList from './user_list.jsx';
|
||||||
|
|
||||||
|
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||||
|
|
||||||
|
const holders = defineMessages({
|
||||||
|
member: {
|
||||||
|
id: 'more_direct_channels.member',
|
||||||
|
defaultMessage: 'Member'
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
id: 'more_direct_channels.search',
|
||||||
|
defaultMessage: 'Search members'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class FilteredUserList extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleFilterChange = this.handleFilterChange.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
filter: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevState.filter !== this.state.filter) {
|
||||||
|
$(ReactDOM.findDOMNode(this.refs.userList)).scrollTop(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFilterChange(e) {
|
||||||
|
this.setState({
|
||||||
|
filter: e.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {formatMessage} = this.props.intl;
|
||||||
|
|
||||||
|
let users = this.props.users;
|
||||||
|
|
||||||
|
if (this.state.filter) {
|
||||||
|
const filter = this.state.filter.toLowerCase();
|
||||||
|
|
||||||
|
users = users.filter((user) => {
|
||||||
|
return user.username.toLowerCase().indexOf(filter) !== -1 ||
|
||||||
|
(user.first_name && user.first_name.toLowerCase().indexOf(filter) !== -1) ||
|
||||||
|
(user.last_name && user.last_name.toLowerCase().indexOf(filter) !== -1) ||
|
||||||
|
(user.nickname && user.nickname.toLowerCase().indexOf(filter) !== -1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let memberString = formatMessage(holders.member);
|
||||||
|
if (users.length !== 1) {
|
||||||
|
memberString += 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
let count;
|
||||||
|
if (users.length === this.props.users.length) {
|
||||||
|
count = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='more_direct_channels.count'
|
||||||
|
defaultMessage='{count} {member}'
|
||||||
|
values={{
|
||||||
|
count: users.length,
|
||||||
|
member: memberString
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
count = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='more_direct_channels.countTotal'
|
||||||
|
defaultMessage='{count} {member} of {total} Total'
|
||||||
|
values={{
|
||||||
|
count: users.length,
|
||||||
|
member: memberString,
|
||||||
|
total: this.props.users.length
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='filter-row'>
|
||||||
|
<div className='col-sm-6'>
|
||||||
|
<input
|
||||||
|
ref='filter'
|
||||||
|
className='form-control filter-textbox'
|
||||||
|
placeholder={formatMessage(holders.search)}
|
||||||
|
onInput={this.handleFilterChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='col-sm-6'>
|
||||||
|
<span className='member-count'>{count}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref='userList'
|
||||||
|
className='user-list'
|
||||||
|
>
|
||||||
|
<UserList
|
||||||
|
users={users}
|
||||||
|
actions={this.props.actions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteredUserList.defaultProps = {
|
||||||
|
users: [],
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
FilteredUserList.propTypes = {
|
||||||
|
intl: intlShape.isRequired,
|
||||||
|
users: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
|
actions: React.PropTypes.arrayOf(React.PropTypes.func)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default injectIntl(FilteredUserList);
|
||||||
@@ -2,36 +2,24 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
const Modal = ReactBootstrap.Modal;
|
const Modal = ReactBootstrap.Modal;
|
||||||
|
import FilteredUserList from './filtered_user_list.jsx';
|
||||||
import UserStore from '../stores/user_store.jsx';
|
import UserStore from '../stores/user_store.jsx';
|
||||||
import * as Utils from '../utils/utils.jsx';
|
import * as Utils from '../utils/utils.jsx';
|
||||||
|
|
||||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
import {FormattedMessage} from 'mm-intl';
|
||||||
|
|
||||||
const holders = defineMessages({
|
export default class MoreDirectChannels extends React.Component {
|
||||||
member: {
|
|
||||||
id: 'more_direct_channels.member',
|
|
||||||
defaultMessage: 'Member'
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
id: 'more_direct_channels.search',
|
|
||||||
defaultMessage: 'Search members'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
class MoreDirectChannels extends React.Component {
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.handleFilterChange = this.handleFilterChange.bind(this);
|
|
||||||
this.handleHide = this.handleHide.bind(this);
|
this.handleHide = this.handleHide.bind(this);
|
||||||
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
||||||
this.handleUserChange = this.handleUserChange.bind(this);
|
this.handleUserChange = this.handleUserChange.bind(this);
|
||||||
|
|
||||||
this.createRowForUser = this.createRowForUser.bind(this);
|
this.createJoinDirectChannelButton = this.createJoinDirectChannelButton.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
users: this.getUsersFromStore(),
|
users: this.getUsersFromStore(),
|
||||||
filter: '',
|
|
||||||
loadingDMChannel: -1
|
loadingDMChannel: -1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -67,32 +55,20 @@ class MoreDirectChannels extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
if (Utils.isMobile()) {
|
// TODO ugh
|
||||||
$(ReactDOM.findDOMNode(this.refs.userList)).css('max-height', $(window).height() - 250);
|
/*if (Utils.isMobile()) {
|
||||||
|
$(ReactDOM.findDOMNode(this.refs.modal)).css('max-height', $(window).height() - 250);
|
||||||
} else {
|
} else {
|
||||||
$(ReactDOM.findDOMNode(this.refs.userList)).perfectScrollbar();
|
console.log(ReactDOM.findDOMNode(this.refs.modal));
|
||||||
$(ReactDOM.findDOMNode(this.refs.userList)).css('max-height', $(window).height() - 300);
|
console.log($(ReactDOM.findDOMNode(this.refs.modal)));
|
||||||
}
|
$(ReactDOM.findDOMNode(this.refs.modal)).css('max-height', $(window).height() - 300);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
handleFilterChange() {
|
|
||||||
const filter = ReactDOM.findDOMNode(this.refs.filter).value;
|
|
||||||
|
|
||||||
if ($(window).width() > 768) {
|
|
||||||
$(ReactDOM.findDOMNode(this.refs.userList)).scrollTop(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter !== this.state.filter) {
|
|
||||||
this.setState({filter});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHide() {
|
handleHide() {
|
||||||
if (this.props.onModalDismissed) {
|
if (this.props.onModalDismissed) {
|
||||||
this.props.onModalDismissed();
|
this.props.onModalDismissed();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({filter: ''});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowDirectChannel(teammate, e) {
|
handleShowDirectChannel(teammate, e) {
|
||||||
@@ -120,145 +96,34 @@ class MoreDirectChannels extends React.Component {
|
|||||||
this.setState({users: this.getUsersFromStore()});
|
this.setState({users: this.getUsersFromStore()});
|
||||||
}
|
}
|
||||||
|
|
||||||
createRowForUser(user) {
|
createJoinDirectChannelButton({user}) {
|
||||||
const details = [];
|
|
||||||
|
|
||||||
const fullName = Utils.getFullName(user);
|
|
||||||
if (fullName) {
|
|
||||||
details.push(
|
|
||||||
<span
|
|
||||||
key={`${user.id}__full-name`}
|
|
||||||
className='full-name'
|
|
||||||
>
|
|
||||||
{fullName}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.nickname) {
|
|
||||||
const separator = fullName ? ' - ' : '';
|
|
||||||
details.push(
|
|
||||||
<span
|
|
||||||
key={`${user.nickname}__nickname`}
|
|
||||||
>
|
|
||||||
{separator + user.nickname}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let joinButton;
|
|
||||||
if (this.state.loadingDMChannel === user.id) {
|
if (this.state.loadingDMChannel === user.id) {
|
||||||
joinButton = (
|
return (
|
||||||
<img
|
<img
|
||||||
className='channel-loading-gif'
|
className='channel-loading-gif'
|
||||||
src='/static/images/load.gif'
|
src='/static/images/load.gif'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
joinButton = (
|
|
||||||
<button
|
|
||||||
type='button'
|
|
||||||
className='btn btn-primary btn-message'
|
|
||||||
onClick={this.handleShowDirectChannel.bind(this, user)}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='more_direct_channels.message'
|
|
||||||
defaultMessage='Message'
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={'direct-channel-row-user' + user.id}>
|
<button
|
||||||
<td
|
type='button'
|
||||||
key={user.id}
|
className='btn btn-primary btn-message'
|
||||||
className='direct-channel'
|
onClick={this.handleShowDirectChannel.bind(this, user)}
|
||||||
>
|
>
|
||||||
<img
|
<FormattedMessage
|
||||||
className='profile-img pull-left'
|
id='more_direct_channels.message'
|
||||||
width='38'
|
defaultMessage='Message'
|
||||||
height='38'
|
/>
|
||||||
src={`/api/v1/users/${user.id}/image?time=${user.update_at}&${Utils.getSessionIndex()}`}
|
</button>
|
||||||
/>
|
|
||||||
<div className='more-name'>
|
|
||||||
{user.username}
|
|
||||||
</div>
|
|
||||||
<div className='more-description'>
|
|
||||||
{details}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className='td--action lg'>
|
|
||||||
{joinButton}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {formatMessage} = this.props.intl;
|
|
||||||
if (!this.props.show) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let users = this.state.users;
|
|
||||||
if (this.state.filter) {
|
|
||||||
const filter = this.state.filter.toLowerCase();
|
|
||||||
|
|
||||||
users = users.filter((user) => {
|
|
||||||
return user.username.toLowerCase().indexOf(filter) !== -1 ||
|
|
||||||
user.first_name.toLowerCase().indexOf(filter) !== -1 ||
|
|
||||||
user.last_name.toLowerCase().indexOf(filter) !== -1 ||
|
|
||||||
user.nickname.toLowerCase().indexOf(filter) !== -1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const userEntries = users.map(this.createRowForUser);
|
|
||||||
|
|
||||||
if (userEntries.length === 0) {
|
|
||||||
userEntries.push(
|
|
||||||
<tr key='no-users-found'><td>
|
|
||||||
<FormattedMessage
|
|
||||||
id='more_direct_channels.notFound'
|
|
||||||
defaultMessage='No users found :('
|
|
||||||
/>
|
|
||||||
</td></tr>);
|
|
||||||
}
|
|
||||||
|
|
||||||
let memberString = formatMessage(holders.member);
|
|
||||||
if (users.length !== 1) {
|
|
||||||
memberString += 's';
|
|
||||||
}
|
|
||||||
|
|
||||||
let count;
|
|
||||||
if (users.length === this.state.users.length) {
|
|
||||||
count = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='more_direct_channels.count'
|
|
||||||
defaultMessage='{count} {member}'
|
|
||||||
values={{
|
|
||||||
count: users.length,
|
|
||||||
member: memberString
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
count = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='more_direct_channels.countTotal'
|
|
||||||
defaultMessage='{count} {member} of {total} Total'
|
|
||||||
values={{
|
|
||||||
count: users.length,
|
|
||||||
member: memberString,
|
|
||||||
total: this.state.users.length
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
dialogClassName='more-modal'
|
dialogClassName='more-modal more-direct-channels'
|
||||||
show={this.props.show}
|
show={this.props.show}
|
||||||
onHide={this.handleHide}
|
onHide={this.handleHide}
|
||||||
>
|
>
|
||||||
@@ -270,30 +135,11 @@ class MoreDirectChannels extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</Modal.Title>
|
</Modal.Title>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Body ref='modalBody'>
|
<Modal.Body>
|
||||||
<div className='filter-row'>
|
<FilteredUserList
|
||||||
<div className='col-sm-6'>
|
users={this.state.users}
|
||||||
<input
|
actions={[this.createJoinDirectChannelButton]}
|
||||||
ref='filter'
|
/>
|
||||||
className='form-control filter-textbox'
|
|
||||||
placeholder={formatMessage(holders.search)}
|
|
||||||
onInput={this.handleFilterChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='col-sm-6'>
|
|
||||||
<span className='member-count'>{count}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
ref='userList'
|
|
||||||
className='user-list'
|
|
||||||
>
|
|
||||||
<table className='more-table table'>
|
|
||||||
<tbody>
|
|
||||||
{userEntries}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<button
|
<button
|
||||||
@@ -313,9 +159,6 @@ class MoreDirectChannels extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoreDirectChannels.propTypes = {
|
MoreDirectChannels.propTypes = {
|
||||||
intl: intlShape.isRequired,
|
|
||||||
show: React.PropTypes.bool.isRequired,
|
show: React.PropTypes.bool.isRequired,
|
||||||
onModalDismissed: React.PropTypes.func
|
onModalDismissed: React.PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectIntl(MoreDirectChannels);
|
|
||||||
|
|||||||
53
web/react/components/user_list.jsx
Обычный файл
53
web/react/components/user_list.jsx
Обычный файл
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import {FormattedMessage} from 'mm-intl';
|
||||||
|
import UserListRow from './user_list_row.jsx';
|
||||||
|
|
||||||
|
export default class UserList extends React.Component {
|
||||||
|
render() {
|
||||||
|
const users = this.props.users;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
if (users.length > 0) {
|
||||||
|
content = users.map((user) => {
|
||||||
|
return (
|
||||||
|
<UserListRow
|
||||||
|
key={user.id}
|
||||||
|
user={user}
|
||||||
|
actions={this.props.actions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
content = (
|
||||||
|
<tr key='no-users-found'>
|
||||||
|
<td>
|
||||||
|
<FormattedMessage
|
||||||
|
id='more_direct_channels.notFound'
|
||||||
|
defaultMessage='No users found :('
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table className='more-table table'>
|
||||||
|
<tbody>
|
||||||
|
{content}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserList.defaultProps = {
|
||||||
|
users: [],
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
UserList.propTypes = {
|
||||||
|
users: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||||
|
actions: React.PropTypes.arrayOf(React.PropTypes.func)
|
||||||
|
};
|
||||||
79
web/react/components/user_list_row.jsx
Обычный файл
79
web/react/components/user_list_row.jsx
Обычный файл
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import * as Utils from '../utils/utils.jsx';
|
||||||
|
|
||||||
|
export default function UserListRow({user, actions}) {
|
||||||
|
const details = [];
|
||||||
|
|
||||||
|
const fullName = Utils.getFullName(user);
|
||||||
|
if (fullName) {
|
||||||
|
details.push(
|
||||||
|
<span
|
||||||
|
key={`${user.id}__full-name`}
|
||||||
|
className='full-name'
|
||||||
|
>
|
||||||
|
{fullName}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.nickname) {
|
||||||
|
const separator = fullName ? ' - ' : '';
|
||||||
|
details.push(
|
||||||
|
<span
|
||||||
|
key={`${user.nickname}__nickname`}
|
||||||
|
>
|
||||||
|
{separator + user.nickname}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttons = actions.map((Action, index) => {
|
||||||
|
return (
|
||||||
|
<Action
|
||||||
|
key={index.toString()}
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
key={user.id}
|
||||||
|
className='direct-channel'
|
||||||
|
style={{display: 'flex'}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className='profile-img'
|
||||||
|
src={`/api/v1/users/${user.id}/image?time=${user.update_at}&${Utils.getSessionIndex()}`}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className='user-list-item__details'
|
||||||
|
>
|
||||||
|
<div className='more-name'>
|
||||||
|
{user.username}
|
||||||
|
</div>
|
||||||
|
<div className='more-description'>
|
||||||
|
{details}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='user-list-item__actions'
|
||||||
|
>
|
||||||
|
{buttons}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserListRow.defaultProps = {
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
UserListRow.propTypes = {
|
||||||
|
user: React.PropTypes.object.isRequired,
|
||||||
|
actions: React.PropTypes.arrayOf(React.PropTypes.func)
|
||||||
|
};
|
||||||
@@ -434,3 +434,25 @@
|
|||||||
max-height: 150px;
|
max-height: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.profile-img {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-list-item__details {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-list-item__actions {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user