Updated More Direct Channels modal and converted it to ReactBootstrap

Этот коммит содержится в:
hmhealey
2015-10-14 16:57:03 -04:00
родитель 6c4022337c
Коммит ae0d4dce7b
5 изменённых файлов: 345 добавлений и 136 удалений

Просмотреть файл

@@ -1,133 +1,273 @@
// 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.
var TeamStore = require('../stores/team_store.jsx'); const AsyncClient = require('../utils/async_client.jsx');
var Client = require('../utils/client.jsx'); const ChannelStore = require('../stores/channel_store.jsx');
var Constants = require('../utils/constants.jsx'); const Constants = require('../utils/constants.jsx');
var AsyncClient = require('../utils/async_client.jsx'); const Client = require('../utils/client.jsx');
var PreferenceStore = require('../stores/preference_store.jsx'); const Modal = ReactBootstrap.Modal;
var utils = require('../utils/utils.jsx'); const PreferenceStore = require('../stores/preference_store.jsx');
const TeamStore = require('../stores/team_store.jsx');
const UserStore = require('../stores/user_store.jsx');
const Utils = require('../utils/utils.jsx');
export default class MoreDirectChannels extends React.Component { export default class MoreDirectChannels extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {channels: [], loadingDMChannel: -1}; this.handleFilterChange = this.handleFilterChange.bind(this);
this.handleHide = this.handleHide.bind(this);
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
this.handleUserChange = this.handleUserChange.bind(this);
this.createRowForUser = this.createRowForUser.bind(this);
this.state = {
users: this.getUsersFromStore(),
filter: '',
loadingDMChannel: -1
};
}
getUsersFromStore() {
const currentId = UserStore.getCurrentId();
const profiles = UserStore.getProfiles();
const users = [];
for (const id in profiles) {
if (id !== currentId) {
users.push(profiles[id]);
}
}
users.sort((a, b) => a.username.localeCompare(b.username));
return users;
} }
componentDidMount() { componentDidMount() {
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => { UserStore.addChangeListener(this.handleUserChange);
var button = e.relatedTarget;
this.setState({channels: $(button).data('channels')}); // eslint-disable-line react/no-did-mount-set-state
});
} }
handleJoinDirectChannel(channel) { componentWillUnmount() {
const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, channel.teammate_id, 'true'); UserStore.addChangeListener(this.handleUserChange);
}
handleFilterChange() {
const filter = React.findDOMNode(this.refs.filter).value;
if (filter !== this.state.filter) {
this.setState({filter});
}
}
handleHide() {
if (this.props.onModalDismissed) {
this.props.onModalDismissed();
}
this.setState({filter: ''});
}
handleShowDirectChannel(teammate, e) {
if (this.state.loadingDMChannel !== -1) {
return;
}
e.preventDefault();
const channelName = Utils.getDirectChannelName(UserStore.getCurrentId(), teammate.id);
let channel = ChannelStore.getByName(channelName);
const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, teammate.id, 'true');
AsyncClient.savePreferences([preference]); AsyncClient.savePreferences([preference]);
if (channel) {
Utils.switchChannel(channel);
this.handleHide();
} else {
this.setState({loadingDMChannel: teammate.id});
channel = {
name: channelName,
last_post_at: 0,
total_msg_count: 0,
type: 'D',
display_name: teammate.username,
teammate_id: teammate.id,
status: UserStore.getStatus(teammate.id)
};
Client.createDirectChannel(
channel,
teammate.id,
(data) => {
this.setState({loadingDMChannel: -1});
AsyncClient.getChannel(data.id);
Utils.switchChannel(data);
this.handleHide();
},
() => {
this.setState({loadingDMChannel: -1});
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName;
}
);
}
}
handleUserChange() {
this.setState({users: this.getUsersFromStore()});
}
createRowForUser(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`}
className='nickname'
>
{separator + user.nickname}
</span>
);
}
let joinButton;
if (this.state.loadingDMChannel === user.id) {
joinButton = (
<img
className='channel-loading-gif'
src='/static/images/load.gif'
/>
);
} else {
joinButton = (
<button
type='button'
className='btn btn-primary btn-message'
onClick={this.handleShowDirectChannel.bind(this, user)}
>
{'Message'}
</button>
);
}
return (
<li
key={user.id}
className='direct-channel'
>
<div className='col-xs-1 image-div'>
<img
className='profile-image'
src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
/>
</div>
<div className='col-xs-9'>
<div className='username'>
{user.username}
</div>
<div>
{details}
</div>
</div>
<div className='col-xs-2 btn-div'>
{joinButton}
</div>
</li>
);
} }
render() { render() {
var directMessageItems = this.state.channels.map((channel, index) => { if (!this.props.show) {
var badge = ''; return null;
var titleClass = ''; }
var handleClick = null;
if (channel.fake) { let users = this.state.users;
// It's a direct message channel that doesn't exist yet so let's create it now if (this.state.filter !== '') {
var otherUserId = utils.getUserIdFromChannelName(channel); users = users.filter((user) => {
return user.username.indexOf(this.state.filter) !== -1 ||
user.first_name.indexOf(this.state.filter) !== -1 ||
user.last_name.indexOf(this.state.filter) !== -1 ||
user.nickname.indexOf(this.state.filter) !== -1;
});
}
if (this.state.loadingDMChannel === index) { const userEntries = users.map(this.createRowForUser);
badge = (
<img
className='channel-loading-gif pull-right'
src='/static/images/load.gif'
/>
);
}
if (this.state.loadingDMChannel === -1) { if (userEntries.length === 0) {
handleClick = (e) => { userEntries.push(<li key='no-users-found'>{'No users found :('}</li>);
e.preventDefault(); }
this.setState({loadingDMChannel: index});
this.handleJoinDirectChannel(channel);
Client.createDirectChannel(channel, otherUserId, let memberString = 'Member';
(data) => { if (users.length !== 1) {
$(React.findDOMNode(this.refs.modal)).modal('hide'); memberString += 's';
this.setState({loadingDMChannel: -1}); }
AsyncClient.getChannel(data.id);
utils.switchChannel(data);
},
() => {
this.setState({loadingDMChannel: -1});
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
}
);
};
}
} else {
if (channel.unread) {
badge = <span className='badge pull-right small'>{channel.unread}</span>;
titleClass = 'unread-title';
}
handleClick = (e) => { let count;
e.preventDefault(); if (users.length === this.state.users.length) {
this.handleJoinDirectChannel(channel); count = `${users.length} ${memberString}`;
utils.switchChannel(channel); } else {
$(React.findDOMNode(this.refs.modal)).modal('hide'); count = `${users.length} ${memberString} of ${this.state.users.length} Total`;
}; }
}
return (
<li key={channel.name}>
<a
className={'sidebar-channel ' + titleClass}
href='#'
onClick={handleClick}
>{badge}{channel.display_name}</a>
</li>
);
});
return ( return (
<div <Modal
className='modal fade' className='modal-direct-channels'
id='more_direct_channels' show={this.props.show}
ref='modal' bsSize='large'
tabIndex='-1' onHide={this.handleHide}
role='dialog'
aria-hidden='true'
> >
<div className='modal-dialog'> <Modal.Header closeButton={true}>
<div className='modal-content'> <Modal.Title>{'More Direct Messages'}</Modal.Title>
<div className='modal-header'> </Modal.Header>
<button <Modal.Body>
type='button' <div>
className='close' <input
data-dismiss='modal' ref='filter'
> className='form-control filter-textbox'
<span aria-hidden='true'>{'×'}</span> placeholder='Search members'
<span className='sr-only'>{'Close'}</span> onInput={this.handleFilterChange}
</button> style={{width: '200px', display: 'inline'}}
<h4 className='modal-title'>{'More Direct Messages'}</h4> />
</div> <span className='member-count pull-right'>{count}</span>
<div className='modal-body'>
<ul className='nav nav-pills nav-stacked'>
{directMessageItems}
</ul>
</div>
<div className='modal-footer'>
<button
type='button'
className='btn btn-default'
data-dismiss='modal'
>{'Close'}</button>
</div>
</div> </div>
</div> <ul className='user-list'>
</div> {userEntries}
</ul>
</Modal.Body>
<Modal.Footer>
<button
type='button'
className='btn btn-default'
onClick={this.handleHide}
>
{'Close'}
</button>
</Modal.Footer>
</Modal>
); );
} }
} }
MoreDirectChannels.propTypes = {
show: React.PropTypes.bool.isRequired,
onModalDismissed: React.PropTypes.func
};

Просмотреть файл

@@ -8,6 +8,7 @@ const Client = require('../utils/client.jsx');
const Constants = require('../utils/constants.jsx'); const Constants = require('../utils/constants.jsx');
const PreferenceStore = require('../stores/preference_store.jsx'); const PreferenceStore = require('../stores/preference_store.jsx');
const NewChannelFlow = require('./new_channel_flow.jsx'); const NewChannelFlow = require('./new_channel_flow.jsx');
const MoreDirectChannels = require('./more_direct_channels.jsx');
const SearchBox = require('./search_bar.jsx'); const SearchBox = require('./search_bar.jsx');
const SidebarHeader = require('./sidebar_header.jsx'); const SidebarHeader = require('./sidebar_header.jsx');
const SocketStore = require('../stores/socket_store.jsx'); const SocketStore = require('../stores/socket_store.jsx');
@@ -33,12 +34,19 @@ export default class Sidebar extends React.Component {
this.onResize = this.onResize.bind(this); this.onResize = this.onResize.bind(this);
this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this); this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this);
this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this); this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this);
this.showNewChannelModal = this.showNewChannelModal.bind(this);
this.hideNewChannelModal = this.hideNewChannelModal.bind(this);
this.showMoreDirectChannelsModal = this.showMoreDirectChannelsModal.bind(this);
this.hideMoreDirectChannelsModal = this.hideMoreDirectChannelsModal.bind(this);
this.createChannelElement = this.createChannelElement.bind(this); this.createChannelElement = this.createChannelElement.bind(this);
this.isLeaving = new Map(); this.isLeaving = new Map();
const state = this.getStateFromStores(); const state = this.getStateFromStores();
state.modal = ''; state.newChannelModalType = '';
state.showMoreDirectChannelsModal = false;
state.loadingDMChannel = -1; state.loadingDMChannel = -1;
this.state = state; this.state = state;
@@ -47,10 +55,11 @@ export default class Sidebar extends React.Component {
const members = ChannelStore.getAllMembers(); const members = ChannelStore.getAllMembers();
var teamMemberMap = UserStore.getActiveOnlyProfiles(); var teamMemberMap = UserStore.getActiveOnlyProfiles();
var currentId = ChannelStore.getCurrentId(); var currentId = ChannelStore.getCurrentId();
const currentUserId = UserStore.getCurrentId();
var teammates = []; var teammates = [];
for (var id in teamMemberMap) { for (var id in teamMemberMap) {
if (id === UserStore.getCurrentId()) { if (id === currentUserId) {
continue; continue;
} }
teammates.push(teamMemberMap[id]); teammates.push(teamMemberMap[id]);
@@ -58,22 +67,16 @@ export default class Sidebar extends React.Component {
const preferences = PreferenceStore.getPreferences(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW); const preferences = PreferenceStore.getPreferences(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
// Create lists of all read and unread direct channels
var visibleDirectChannels = []; var visibleDirectChannels = [];
var hiddenDirectChannels = []; var hiddenDirectChannelCount = 0;
for (var i = 0; i < teammates.length; i++) { for (var i = 0; i < teammates.length; i++) {
const teammate = teammates[i]; const teammate = teammates[i];
if (teammate.id === UserStore.getCurrentId()) { if (teammate.id === currentUserId) {
continue; continue;
} }
var channelName = ''; const channelName = Utils.getDirectChannelName(currentUserId, teammate.id);
if (teammate.id > UserStore.getCurrentId()) {
channelName = UserStore.getCurrentId() + '__' + teammate.id;
} else {
channelName = teammate.id + '__' + UserStore.getCurrentId();
}
let forceShow = false; let forceShow = false;
let channel = ChannelStore.getByName(channelName); let channel = ChannelStore.getByName(channelName);
@@ -106,19 +109,18 @@ export default class Sidebar extends React.Component {
visibleDirectChannels.push(channel); visibleDirectChannels.push(channel);
} else { } else {
hiddenDirectChannels.push(channel); hiddenDirectChannelCount += 1;
} }
} }
visibleDirectChannels.sort(this.sortChannelsByDisplayName); visibleDirectChannels.sort(this.sortChannelsByDisplayName);
hiddenDirectChannels.sort(this.sortChannelsByDisplayName);
return { return {
activeId: currentId, activeId: currentId,
channels: ChannelStore.getAll(), channels: ChannelStore.getAll(),
members, members,
visibleDirectChannels, visibleDirectChannels,
hiddenDirectChannels hiddenDirectChannelCount
}; };
} }
@@ -336,6 +338,20 @@ export default class Sidebar extends React.Component {
return a.display_name.localeCompare(b.display_name); return a.display_name.localeCompare(b.display_name);
} }
showNewChannelModal(type) {
this.setState({newChannelModalType: type});
}
hideNewChannelModal() {
this.setState({newChannelModalType: ''});
}
showMoreDirectChannelsModal() {
this.setState({showDirectChannelsModal: true});
}
hideMoreDirectChannelsModal() {
this.setState({showDirectChannelsModal: false});
}
createChannelElement(channel, index, arr, handleClose) { createChannelElement(channel, index, arr, handleClose) {
var members = this.state.members; var members = this.state.members;
var activeId = this.state.activeId; var activeId = this.state.activeId;
@@ -532,25 +548,21 @@ export default class Sidebar extends React.Component {
head.appendChild(link); head.appendChild(link);
var directMessageMore = null; var directMessageMore = null;
if (this.state.hiddenDirectChannels.length > 0) { if (this.state.hiddenDirectChannelCount > 0) {
directMessageMore = ( directMessageMore = (
<li key='more'> <li key='more'>
<a <a
key={`more${this.state.hiddenDirectChannels.length}`}
href='#' href='#'
data-toggle='modal' onClick={this.showMoreDirectChannelsModal}
className='nav-more'
data-target='#more_direct_channels'
data-channels={JSON.stringify(this.state.hiddenDirectChannels)}
> >
{'More (' + this.state.hiddenDirectChannels.length + ')'} {'More (' + this.state.hiddenDirectChannelCount + ')'}
</a> </a>
</li> </li>
); );
} }
let showChannelModal = false; let showChannelModal = false;
if (this.state.modal !== '') { if (this.state.newChannelModalType !== '') {
showChannelModal = true; showChannelModal = true;
} }
@@ -561,9 +573,14 @@ export default class Sidebar extends React.Component {
<div> <div>
<NewChannelFlow <NewChannelFlow
show={showChannelModal} show={showChannelModal}
channelType={this.state.modal} channelType={this.state.newChannelModalType}
onModalDismissed={() => this.setState({modal: ''})} onModalDismissed={this.hideNewChannelModal}
/> />
<MoreDirectChannels
show={this.state.showDirectChannelsModal}
onModalDismissed={this.hideMoreDirectChannelsModal}
/>
<SidebarHeader <SidebarHeader
teamDisplayName={this.props.teamDisplayName} teamDisplayName={this.props.teamDisplayName}
teamName={this.props.teamName} teamName={this.props.teamName}
@@ -599,7 +616,7 @@ export default class Sidebar extends React.Component {
<a <a
className='add-channel-btn' className='add-channel-btn'
href='#' href='#'
onClick={() => this.setState({modal: 'O'})} onClick={this.showNewChannelModal.bind(this, 'O')}
> >
{'+'} {'+'}
</a> </a>
@@ -632,7 +649,7 @@ export default class Sidebar extends React.Component {
<a <a
className='add-channel-btn' className='add-channel-btn'
href='#' href='#'
onClick={() => this.setState({modal: 'P'})} onClick={this.showNewChannelModal.bind(this, 'P')}
> >
{'+'} {'+'}
</a> </a>

Просмотреть файл

@@ -24,7 +24,6 @@ var TeamSettingsModal = require('../components/team_settings_modal.jsx');
var ChannelMembersModal = require('../components/channel_members.jsx'); var ChannelMembersModal = require('../components/channel_members.jsx');
var ChannelInviteModal = require('../components/channel_invite_modal.jsx'); var ChannelInviteModal = require('../components/channel_invite_modal.jsx');
var TeamMembersModal = require('../components/team_members.jsx'); var TeamMembersModal = require('../components/team_members.jsx');
var DirectChannelModal = require('../components/more_direct_channels.jsx');
var ErrorBar = require('../components/error_bar.jsx'); var ErrorBar = require('../components/error_bar.jsx');
var ErrorStore = require('../stores/error_store.jsx'); var ErrorStore = require('../stores/error_store.jsx');
var ChannelLoader = require('../components/channel_loader.jsx'); var ChannelLoader = require('../components/channel_loader.jsx');
@@ -155,11 +154,6 @@ function setupChannelPage(props) {
document.getElementById('more_channels_modal') document.getElementById('more_channels_modal')
); );
React.render(
<DirectChannelModal />,
document.getElementById('direct_channel_modal')
);
React.render( React.render(
<PostListContainer />, <PostListContainer />,
document.getElementById('post-list') document.getElementById('post-list')

Просмотреть файл

@@ -911,6 +911,18 @@ export function isBrowserEdge() {
return window.naviagtor && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1; return window.naviagtor && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1;
} }
export function getDirectChannelName(id, otherId) {
let handle;
if (otherId > id) {
handle = id + '__' + otherId;
} else {
handle = otherId + '__' + id;
}
return handle;
}
// Used to get the id of the other user from a DM channel // Used to get the id of the other user from a DM channel
export function getUserIdFromChannelName(channel) { export function getUserIdFromChannelName(channel) {
var ids = channel.name.split('__'); var ids = channel.name.split('__');

Просмотреть файл

@@ -329,3 +329,49 @@
} }
} }
} }
.modal-direct-channels {
.user-list {
list-style-type: none;
margin: 15px 0px 0px;
max-height: 600px;
padding: 0px;
overflow: auto;
li {
border-bottom: 1px solid #ddd;
height: 60px;
padding: 10px 0px;
.image-div {
padding: 0px;
.profile-image {
width: 40px;
height: 40px;
@include border-radius(20px);
}
}
.username {
font-weight: bold;
}
.nickname {
color: #888;
}
.btn-div {
padding: 0px;
.btn-message {
position: relative;
top: 5px;
}
}
&:last-child {
border-bottom: 0px;
}
}
}
}