PLT-3077 Add group messaging (#5489)
* Implement server changes for group messaging * Majority of client-side implementation * Some server updates * Added new React multiselect component * Fix style issues * Add custom renderer for options * Fix model test * Update ENTER functionality for multiselect control * Remove buttons from multiselect UI control * Updating group messaging UI (#5524) * Move filter controls up a component level * Scroll with arrow keys * Updating mobile layout for multiselect (#5534) * Fix race condition when backspacing quickly * Hidden or new GMs show up for regular messages * Add overriding of number remaining text * Add UI filtering for team if config setting set * Add icon to channel switcher and class prop to status icon * Minor updates per feedback * Improving group messaging UI (#5563) * UX changes per feedback * Update email for group messages * UI fixes for group messaging (#5587) * Fix missing localization string * Add maximum users message when adding members to GM * Fix input clearing on Android * Updating group messaging UI (#5603) * Updating UI for group messaging (#5604)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
8c5cee9521
Коммит
3a91d4e5e4
@@ -15,6 +15,7 @@ import ChannelStore from 'stores/channel_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import ModalStore from 'stores/modal_store.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
@@ -22,7 +23,7 @@ import * as ChannelUtils from 'utils/channel_utils.jsx';
|
||||
import * as ChannelActions from 'actions/channel_actions.jsx';
|
||||
|
||||
import {trackEvent} from 'actions/diagnostics_actions.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
import {ActionTypes, Constants} from 'utils/constants.jsx';
|
||||
|
||||
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
|
||||
|
||||
@@ -49,6 +50,8 @@ export default class Sidebar extends React.Component {
|
||||
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onModalChange = this.onModalChange.bind(this);
|
||||
this.onInChannelChange = this.onInChannelChange.bind(this);
|
||||
this.onScroll = this.onScroll.bind(this);
|
||||
this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this);
|
||||
this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this);
|
||||
@@ -77,6 +80,7 @@ export default class Sidebar extends React.Component {
|
||||
state.showDirectChannelsModal = false;
|
||||
state.showMoreChannelsModal = false;
|
||||
state.loadingDMChannel = -1;
|
||||
state.inChannelChange = false;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@@ -96,7 +100,7 @@ export default class Sidebar extends React.Component {
|
||||
Object.keys(unreadCounts).forEach((chId) => {
|
||||
const channel = ChannelStore.get(chId);
|
||||
|
||||
if (channel && (channel.type === 'D' || channel.team_id === this.state.currentTeam.id)) {
|
||||
if (channel && (channel.type === Constants.DM_CHANNEL || channel.type === Constants.GM_CHANNEL || channel.team_id === this.state.currentTeam.id)) {
|
||||
msgs += unreadCounts[chId].msgs;
|
||||
mentions += unreadCounts[chId].mentions;
|
||||
}
|
||||
@@ -128,13 +132,19 @@ export default class Sidebar extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
onInChannelChange() {
|
||||
this.setState({inChannelChange: !this.state.inChannelChange});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
UserStore.addChangeListener(this.onChange);
|
||||
UserStore.addInTeamChangeListener(this.onChange);
|
||||
UserStore.addInChannelChangeListener(this.onInChannelChange);
|
||||
UserStore.addStatusesChangeListener(this.onChange);
|
||||
TeamStore.addChangeListener(this.onChange);
|
||||
PreferenceStore.addChangeListener(this.onChange);
|
||||
ModalStore.addModalListener(ActionTypes.TOGGLE_DM_MODAL, this.onModalChange);
|
||||
|
||||
this.updateTitle();
|
||||
this.updateUnreadIndicators();
|
||||
@@ -179,13 +189,19 @@ export default class Sidebar extends React.Component {
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeInTeamChangeListener(this.onChange);
|
||||
UserStore.removeInChannelChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onChange);
|
||||
TeamStore.removeChangeListener(this.onChange);
|
||||
PreferenceStore.removeChangeListener(this.onChange);
|
||||
ModalStore.removeModalListener(ActionTypes.TOGGLE_DM_MODAL, this.onModalChange);
|
||||
document.removeEventListener('keydown', this.navigateChannelShortcut);
|
||||
document.removeEventListener('keydown', this.navigateUnreadChannelShortcut);
|
||||
}
|
||||
|
||||
onModalChange(value, args) {
|
||||
this.showMoreDirectChannelsModal(args.startingUsers);
|
||||
}
|
||||
|
||||
onChange() {
|
||||
if (this.state.currentTeam.id !== TeamStore.getCurrentId()) {
|
||||
ChannelStore.clear();
|
||||
@@ -203,11 +219,13 @@ export default class Sidebar extends React.Component {
|
||||
}
|
||||
|
||||
let currentChannelName = channel.display_name;
|
||||
if (channel.type === 'D') {
|
||||
if (channel.type === Constants.DM_CHANNEL) {
|
||||
const teammate = Utils.getDirectTeammate(channel.id);
|
||||
if (teammate != null) {
|
||||
currentChannelName = teammate.username;
|
||||
}
|
||||
} else if (channel.type === Constants.GM_CHANNEL) {
|
||||
currentChannelName = ChannelUtils.buildGroupChannelName(channel.id);
|
||||
}
|
||||
|
||||
const unread = this.getTotalUnreadCount();
|
||||
@@ -331,7 +349,7 @@ export default class Sidebar extends React.Component {
|
||||
}
|
||||
|
||||
getDisplayedChannels() {
|
||||
return this.state.favoriteChannels.concat(this.state.publicChannels).concat(this.state.privateChannels).concat(this.state.directChannels).concat(this.state.directNonTeamChannels);
|
||||
return this.state.favoriteChannels.concat(this.state.publicChannels).concat(this.state.privateChannels).concat(this.state.directAndGroupChannels).concat(this.state.directNonTeamChannels);
|
||||
}
|
||||
|
||||
handleLeaveDirectChannel(e, channel) {
|
||||
@@ -340,9 +358,19 @@ export default class Sidebar extends React.Component {
|
||||
if (!this.isLeaving.get(channel.id)) {
|
||||
this.isLeaving.set(channel.id, true);
|
||||
|
||||
let id;
|
||||
let category;
|
||||
if (channel.type === Constants.DM_CHANNEL) {
|
||||
id = channel.teammate_id;
|
||||
category = Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW;
|
||||
} else {
|
||||
id = channel.id;
|
||||
category = Constants.Preferences.CATEGORY_GROUP_CHANNEL_SHOW;
|
||||
}
|
||||
|
||||
AsyncClient.savePreference(
|
||||
Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
channel.teammate_id,
|
||||
category,
|
||||
id,
|
||||
'false',
|
||||
() => {
|
||||
this.isLeaving.set(channel.id, false);
|
||||
@@ -382,13 +410,13 @@ export default class Sidebar extends React.Component {
|
||||
this.setState({newChannelModalType: ''});
|
||||
}
|
||||
|
||||
showMoreDirectChannelsModal() {
|
||||
showMoreDirectChannelsModal(startingUsers) {
|
||||
trackEvent('ui', 'ui_channels_more_direct');
|
||||
this.setState({showDirectChannelsModal: true});
|
||||
this.setState({showDirectChannelsModal: true, startingUsers});
|
||||
}
|
||||
|
||||
hideMoreDirectChannelsModal() {
|
||||
this.setState({showDirectChannelsModal: false});
|
||||
this.setState({showDirectChannelsModal: false, startingUsers: null});
|
||||
}
|
||||
|
||||
openLeftSidebar() {
|
||||
@@ -509,11 +537,16 @@ export default class Sidebar extends React.Component {
|
||||
rowClass += ' has-badge';
|
||||
}
|
||||
|
||||
let displayName = channel.display_name;
|
||||
|
||||
var icon = null;
|
||||
if (channel.type === Constants.OPEN_CHANNEL) {
|
||||
icon = <div className='status'><i className='fa fa-globe'/></div>;
|
||||
} else if (channel.type === Constants.PRIVATE_CHANNEL) {
|
||||
icon = <div className='status'><i className='fa fa-lock'/></div>;
|
||||
} else if (channel.type === Constants.GM_CHANNEL) {
|
||||
displayName = ChannelUtils.buildGroupChannelName(channel.id);
|
||||
icon = <div className='status status--group'>{UserStore.getProfileListInChannel(channel.id, true).length}</div>;
|
||||
} else {
|
||||
// set up status icon for direct message channels (status is null for other channel types)
|
||||
icon = (
|
||||
@@ -576,7 +609,7 @@ export default class Sidebar extends React.Component {
|
||||
onClick={this.trackChannelSelectedEvent}
|
||||
>
|
||||
{icon}
|
||||
{channel.display_name}
|
||||
{displayName}
|
||||
{badge}
|
||||
{closeButton}
|
||||
</Link>
|
||||
@@ -615,27 +648,10 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
const privateChannelItems = this.state.privateChannels.map(this.createChannelElement);
|
||||
|
||||
const directMessageItems = this.state.directChannels.map((channel, index, arr) => {
|
||||
const directMessageItems = this.state.directAndGroupChannels.map((channel, index, arr) => {
|
||||
return this.createChannelElement(channel, index, arr, this.handleLeaveDirectChannel);
|
||||
});
|
||||
|
||||
const directMessageNonTeamItems = this.state.directNonTeamChannels.map((channel, index, arr) => {
|
||||
return this.createChannelElement(channel, index, arr, this.handleLeaveDirectChannel);
|
||||
});
|
||||
|
||||
let directDivider;
|
||||
if (directMessageNonTeamItems.length !== 0) {
|
||||
directDivider =
|
||||
(<div className='sidebar__divider'>
|
||||
<div className='sidebar__divider__text'>
|
||||
<FormattedMessage
|
||||
id='sidebar.otherMembers'
|
||||
defaultMessage='Outside this team'
|
||||
/>
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
||||
// update the favicon to show if there are any notifications
|
||||
if (this.lastBadgesActive !== this.badgesActive) {
|
||||
var link = document.createElement('link');
|
||||
@@ -659,7 +675,7 @@ export default class Sidebar extends React.Component {
|
||||
<li key='more'>
|
||||
<a
|
||||
href='#'
|
||||
onClick={this.showMoreDirectChannelsModal}
|
||||
onClick={() => this.showMoreDirectChannelsModal()}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='sidebar.moreElips'
|
||||
@@ -753,6 +769,7 @@ export default class Sidebar extends React.Component {
|
||||
moreDirectChannelsModal = (
|
||||
<MoreDirectChannels
|
||||
onModalDismissed={this.hideMoreDirectChannelsModal}
|
||||
startingUsers={this.state.startingUsers}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -866,8 +883,6 @@ export default class Sidebar extends React.Component {
|
||||
</h4>
|
||||
</li>
|
||||
{directMessageItems}
|
||||
{directDivider}
|
||||
{directMessageNonTeamItems}
|
||||
{directMessageMore}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Ссылка в новой задаче
Block a user