PLT-6023: Add Users to Team in WebApp. (#5956)
* PLT-6198: Use added to channel system message on default channels. Use a different sytem message when a user was added to a default channel by someone else than when they joined themselves. * PLT-6023: Add Users to Team in WebApp. * Fix string text. * Handle added_to_team websocket message. * Fix unread flag on new channel.
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
77a76487a8
Коммит
1fa3f2351c
@@ -114,6 +114,31 @@ export function addUserToTeamFromInvite(data, hash, inviteId, success, error) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addUsersToTeam(teamId, userIds, success, error) {
|
||||||
|
Client.addUsersToTeam(
|
||||||
|
teamId,
|
||||||
|
userIds,
|
||||||
|
(teamMembers) => {
|
||||||
|
teamMembers.forEach((member) => {
|
||||||
|
TeamStore.removeMemberNotInTeam(teamId, member.user_id);
|
||||||
|
UserStore.removeProfileNotInTeam(teamId, member.user_id);
|
||||||
|
});
|
||||||
|
UserStore.emitNotInTeamChange();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
success(teamMembers);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
AsyncClient.dispatchError(err, 'addUsersToTeam');
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getInviteInfo(inviteId, success, error) {
|
export function getInviteInfo(inviteId, success, error) {
|
||||||
Client.getInviteInfo(
|
Client.getInviteInfo(
|
||||||
inviteId,
|
inviteId,
|
||||||
|
|||||||
@@ -492,6 +492,28 @@ export function searchUsers(term, teamId = TeamStore.getCurrentId(), options = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function searchUsersNotInTeam(term, teamId = TeamStore.getCurrentId(), options = {}, success, error) {
|
||||||
|
Client.searchUsersNotInTeam(
|
||||||
|
term,
|
||||||
|
teamId,
|
||||||
|
options,
|
||||||
|
(data) => {
|
||||||
|
loadStatusesForProfilesList(data);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
success(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
AsyncClient.dispatchError(err, 'searchUsersNotInTeam');
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function autocompleteUsersInChannel(username, channelId, success, error) {
|
export function autocompleteUsersInChannel(username, channelId, success, error) {
|
||||||
Client.autocompleteUsersInChannel(
|
Client.autocompleteUsersInChannel(
|
||||||
username,
|
username,
|
||||||
|
|||||||
@@ -153,6 +153,10 @@ function handleEvent(msg) {
|
|||||||
handleUpdateTeamEvent(msg);
|
handleUpdateTeamEvent(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SocketEvents.ADDED_TO_TEAM:
|
||||||
|
handleTeamAddedEvent(msg);
|
||||||
|
break;
|
||||||
|
|
||||||
case SocketEvents.USER_ADDED:
|
case SocketEvents.USER_ADDED:
|
||||||
handleUserAddedEvent(msg);
|
handleUserAddedEvent(msg);
|
||||||
break;
|
break;
|
||||||
@@ -241,6 +245,27 @@ function handlePostDeleteEvent(msg) {
|
|||||||
GlobalActions.emitPostDeletedEvent(post);
|
GlobalActions.emitPostDeletedEvent(post);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTeamAddedEvent(msg) {
|
||||||
|
Client.getTeam(msg.data.team_id, (team) => {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_TEAM,
|
||||||
|
team
|
||||||
|
});
|
||||||
|
|
||||||
|
Client.getMyTeamMembers((data) => {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_MY_TEAM_MEMBERS,
|
||||||
|
team_members: data
|
||||||
|
});
|
||||||
|
AsyncClient.getMyTeamsUnread();
|
||||||
|
}, (err) => {
|
||||||
|
AsyncClient.dispatchError(err, 'getMyTeamMembers');
|
||||||
|
});
|
||||||
|
}, (err) => {
|
||||||
|
AsyncClient.dispatchError(err, 'getTeam');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleLeaveTeamEvent(msg) {
|
function handleLeaveTeamEvent(msg) {
|
||||||
if (UserStore.getCurrentId() === msg.data.user_id) {
|
if (UserStore.getCurrentId() === msg.data.user_id) {
|
||||||
TeamStore.removeMyTeamMember(msg.data.team_id);
|
TeamStore.removeMyTeamMember(msg.data.team_id);
|
||||||
|
|||||||
@@ -489,6 +489,15 @@ export default class Client {
|
|||||||
|
|
||||||
// Team Routes Section
|
// Team Routes Section
|
||||||
|
|
||||||
|
getTeam(teamId, success, error) {
|
||||||
|
request.
|
||||||
|
get(`${this.getTeamsRoute()}/${teamId}/me`).
|
||||||
|
set(this.defaultHeaders).
|
||||||
|
type('application/json').
|
||||||
|
accept('application/json').
|
||||||
|
end(this.handleResponse.bind(this, 'getTeam', success, error));
|
||||||
|
}
|
||||||
|
|
||||||
findTeamByName(teamName, success, error) {
|
findTeamByName(teamName, success, error) {
|
||||||
request.
|
request.
|
||||||
post(`${this.getTeamsRoute()}/find_team_by_name`).
|
post(`${this.getTeamsRoute()}/find_team_by_name`).
|
||||||
@@ -681,6 +690,30 @@ export default class Client {
|
|||||||
this.trackEvent('api', 'api_teams_invite_members');
|
this.trackEvent('api', 'api_teams_invite_members');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addUsersToTeam(teamId, userIds, success, error) {
|
||||||
|
let nonEmptyTeamId = teamId;
|
||||||
|
if (nonEmptyTeamId === '') {
|
||||||
|
nonEmptyTeamId = this.getTeamId();
|
||||||
|
}
|
||||||
|
|
||||||
|
const teamMembers = userIds.map((userId) => {
|
||||||
|
return {
|
||||||
|
team_id: nonEmptyTeamId,
|
||||||
|
user_id: userId
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
request.
|
||||||
|
post(`${this.url}/api/v4/teams/${nonEmptyTeamId}/members/batch`).
|
||||||
|
set(this.defaultHeaders).
|
||||||
|
type('application/json').
|
||||||
|
accept('application/json').
|
||||||
|
send(teamMembers).
|
||||||
|
end(this.handleResponse.bind(this, 'addUsersToTeam', success, error));
|
||||||
|
|
||||||
|
this.trackEvent('api', 'api_teams_batch_add_members', {team_id: nonEmptyTeamId, count: teamMembers.length});
|
||||||
|
}
|
||||||
|
|
||||||
removeUserFromTeam(teamId, userId, success, error) {
|
removeUserFromTeam(teamId, userId, success, error) {
|
||||||
let nonEmptyTeamId = teamId;
|
let nonEmptyTeamId = teamId;
|
||||||
if (nonEmptyTeamId === '') {
|
if (nonEmptyTeamId === '') {
|
||||||
@@ -1124,6 +1157,29 @@ export default class Client {
|
|||||||
this.trackEvent('api', 'api_profiles_get_in_team', {team_id: teamId});
|
this.trackEvent('api', 'api_profiles_get_in_team', {team_id: teamId});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getProfilesNotInTeam(teamId, offset, limit, success, error) {
|
||||||
|
// Super hacky, but this option only exists in api v4
|
||||||
|
function wrappedSuccess(data, res) {
|
||||||
|
// Convert the profile list provided by api v4 to a map to match similar v3 calls
|
||||||
|
const profiles = {};
|
||||||
|
|
||||||
|
for (const profile of data) {
|
||||||
|
profiles[profile.id] = profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
success(profiles, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.
|
||||||
|
get(`${this.url}/api/v4/users?not_in_team=${this.getTeamId()}&page=${offset}&per_page=${limit}`).
|
||||||
|
set(this.defaultHeaders).
|
||||||
|
type('application/json').
|
||||||
|
accept('application/json').
|
||||||
|
end(this.handleResponse.bind(this, 'getProfilesNotInTeam', wrappedSuccess, error));
|
||||||
|
|
||||||
|
this.trackEvent('api', 'api_profiles_get_not_in_team', {team_id: teamId});
|
||||||
|
}
|
||||||
|
|
||||||
getProfilesInChannel(channelId, offset, limit, success, error) {
|
getProfilesInChannel(channelId, offset, limit, success, error) {
|
||||||
request.
|
request.
|
||||||
get(`${this.getChannelNeededRoute(channelId)}/users/${offset}/${limit}`).
|
get(`${this.getChannelNeededRoute(channelId)}/users/${offset}/${limit}`).
|
||||||
@@ -1191,6 +1247,19 @@ export default class Client {
|
|||||||
end(this.handleResponse.bind(this, 'searchUsers', success, error));
|
end(this.handleResponse.bind(this, 'searchUsers', success, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
searchUsersNotInTeam(term, teamId, options, success, error) {
|
||||||
|
// Note that this is calling an APIv4 Endpoint since no APIv3 equivalent exists.
|
||||||
|
request.
|
||||||
|
post(`${this.url}/api/v4/users/search`).
|
||||||
|
set(this.defaultHeaders).
|
||||||
|
type('application/json').
|
||||||
|
accept('application/json').
|
||||||
|
send({term, not_in_team_id: teamId, ...options}).
|
||||||
|
end(this.handleResponse.bind(this, 'searchUsersNotInTeam', success, error));
|
||||||
|
|
||||||
|
this.trackEvent('api', 'api_search_users_not_in_team', {team_id: teamId});
|
||||||
|
}
|
||||||
|
|
||||||
autocompleteUsersInChannel(term, channelId, success, error) {
|
autocompleteUsersInChannel(term, channelId, success, error) {
|
||||||
request.
|
request.
|
||||||
get(`${this.getChannelNeededRoute(channelId)}/users/autocomplete?term=${encodeURIComponent(term)}`).
|
get(`${this.getChannelNeededRoute(channelId)}/users/autocomplete?term=${encodeURIComponent(term)}`).
|
||||||
|
|||||||
270
webapp/components/add_users_to_team.jsx
Обычный файл
270
webapp/components/add_users_to_team.jsx
Обычный файл
@@ -0,0 +1,270 @@
|
|||||||
|
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import MultiSelect from 'components/multiselect/multiselect.jsx';
|
||||||
|
import ProfilePicture from 'components/profile_picture.jsx';
|
||||||
|
|
||||||
|
import {addUsersToTeam} from 'actions/team_actions.jsx';
|
||||||
|
import {searchUsersNotInTeam} from 'actions/user_actions.jsx';
|
||||||
|
|
||||||
|
import UserStore from 'stores/user_store.jsx';
|
||||||
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
|
|
||||||
|
import * as AsyncClient from 'utils/async_client.jsx';
|
||||||
|
import Constants from 'utils/constants.jsx';
|
||||||
|
import {displayUsernameForUser} from 'utils/utils.jsx';
|
||||||
|
import Client from 'client/web_client.jsx';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {Modal} from 'react-bootstrap';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
import {browserHistory} from 'react-router/es6';
|
||||||
|
|
||||||
|
const USERS_PER_PAGE = 50;
|
||||||
|
const MAX_SELECTABLE_VALUES = 20;
|
||||||
|
|
||||||
|
export default class AddUsersToTeam extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleHide = this.handleHide.bind(this);
|
||||||
|
this.handleExit = this.handleExit.bind(this);
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
this.handleDelete = this.handleDelete.bind(this);
|
||||||
|
this.onChange = this.onChange.bind(this);
|
||||||
|
this.search = this.search.bind(this);
|
||||||
|
this.addValue = this.addValue.bind(this);
|
||||||
|
|
||||||
|
this.searchTimeoutId = 0;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
users: null,
|
||||||
|
values: [],
|
||||||
|
show: true,
|
||||||
|
search: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
UserStore.addChangeListener(this.onChange);
|
||||||
|
UserStore.addNotInTeamChangeListener(this.onChange);
|
||||||
|
UserStore.addStatusesChangeListener(this.onChange);
|
||||||
|
|
||||||
|
AsyncClient.getProfilesNotInTeam(TeamStore.getCurrentId(), 0, USERS_PER_PAGE * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
UserStore.removeChangeListener(this.onChange);
|
||||||
|
UserStore.removeNotInTeamChangeListener(this.onChange);
|
||||||
|
UserStore.removeStatusesChangeListener(this.onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHide() {
|
||||||
|
this.setState({show: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleExit() {
|
||||||
|
if (this.exitToChannel) {
|
||||||
|
browserHistory.push(this.exitToChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.onModalDismissed) {
|
||||||
|
this.props.onModalDismissed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit(e) {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
const userIds = this.state.values.map((v) => v.id);
|
||||||
|
if (userIds.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addUsersToTeam(TeamStore.getCurrentId(), userIds);
|
||||||
|
|
||||||
|
this.handleHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
addValue(value) {
|
||||||
|
const values = Object.assign([], this.state.values);
|
||||||
|
if (values.indexOf(value) === -1) {
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({values});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(force) {
|
||||||
|
if (this.state.search && !force) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = Object.assign([], UserStore.getProfileListNotInTeam(TeamStore.getCurrentId(), true));
|
||||||
|
|
||||||
|
for (let i = 0; i < users.length; i++) {
|
||||||
|
const user = Object.assign({}, users[i]);
|
||||||
|
user.value = user.id;
|
||||||
|
user.label = '@' + user.username;
|
||||||
|
users[i] = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
users
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePageChange(page, prevPage) {
|
||||||
|
if (page > prevPage) {
|
||||||
|
AsyncClient.getProfilesNotInTeam((page + 1) * USERS_PER_PAGE, USERS_PER_PAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
search(term) {
|
||||||
|
clearTimeout(this.searchTimeoutId);
|
||||||
|
|
||||||
|
if (term === '') {
|
||||||
|
this.onChange(true);
|
||||||
|
this.setState({search: false});
|
||||||
|
this.searchTimeoutId = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const teamId = TeamStore.getCurrentId();
|
||||||
|
|
||||||
|
const searchTimeoutId = setTimeout(
|
||||||
|
() => {
|
||||||
|
searchUsersNotInTeam(
|
||||||
|
term,
|
||||||
|
teamId,
|
||||||
|
{},
|
||||||
|
(users) => {
|
||||||
|
if (searchTimeoutId !== this.searchTimeoutId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let indexToDelete = -1;
|
||||||
|
for (let i = 0; i < users.length; i++) {
|
||||||
|
if (users[i].id === UserStore.getCurrentId()) {
|
||||||
|
indexToDelete = i;
|
||||||
|
}
|
||||||
|
users[i].value = users[i].id;
|
||||||
|
users[i].label = '@' + users[i].username;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indexToDelete !== -1) {
|
||||||
|
users.splice(indexToDelete, 1);
|
||||||
|
}
|
||||||
|
this.setState({search: true, users});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Constants.SEARCH_TIMEOUT_MILLISECONDS
|
||||||
|
);
|
||||||
|
|
||||||
|
this.searchTimeoutId = searchTimeoutId;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDelete(values) {
|
||||||
|
this.setState({values});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOption(option, isSelected, onAdd) {
|
||||||
|
var rowSelected = '';
|
||||||
|
if (isSelected) {
|
||||||
|
rowSelected = 'more-modal__row--selected';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={option.id}
|
||||||
|
ref={isSelected ? 'selected' : option.id}
|
||||||
|
className={'more-modal__row clickable ' + rowSelected}
|
||||||
|
onClick={() => onAdd(option)}
|
||||||
|
>
|
||||||
|
<ProfilePicture
|
||||||
|
src={`${Client.getUsersRoute()}/${option.id}/image?time=${option.last_picture_update}`}
|
||||||
|
width='32'
|
||||||
|
height='32'
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className='more-modal__details'
|
||||||
|
>
|
||||||
|
<div className='more-modal__name'>
|
||||||
|
{displayUsernameForUser(option)}
|
||||||
|
</div>
|
||||||
|
<div className='more-modal__description'>
|
||||||
|
{option.email}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='more-modal__actions'>
|
||||||
|
<div className='more-modal__actions--round'>
|
||||||
|
<i className='fa fa-plus'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderValue(user) {
|
||||||
|
return user.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const numRemainingText = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='multiselect.numPeopleRemaining'
|
||||||
|
defaultMessage='You can add {num, number} more {num, plural, =0 {people} one {person} other {people}}. '
|
||||||
|
values={{
|
||||||
|
num: MAX_SELECTABLE_VALUES - this.state.values.length
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
dialogClassName={'more-modal more-direct-channels'}
|
||||||
|
show={this.state.show}
|
||||||
|
onHide={this.handleHide}
|
||||||
|
onExited={this.handleExit}
|
||||||
|
>
|
||||||
|
<Modal.Header closeButton={true}>
|
||||||
|
<Modal.Title>
|
||||||
|
<FormattedMessage
|
||||||
|
id='add_users_to_team.title'
|
||||||
|
defaultMessage='Add New Members To {teamName} Team'
|
||||||
|
values={{
|
||||||
|
teamName: (
|
||||||
|
<strong>{TeamStore.getCurrent().display_name}</strong>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<MultiSelect
|
||||||
|
key='addUsersToTeamKey'
|
||||||
|
options={this.state.users}
|
||||||
|
optionRenderer={this.renderOption}
|
||||||
|
values={this.state.values}
|
||||||
|
valueRenderer={this.renderValue}
|
||||||
|
perPage={USERS_PER_PAGE}
|
||||||
|
handlePageChange={this.handlePageChange}
|
||||||
|
handleInput={this.search}
|
||||||
|
handleDelete={this.handleDelete}
|
||||||
|
handleAdd={this.addValue}
|
||||||
|
handleSubmit={this.handleSubmit}
|
||||||
|
maxValues={MAX_SELECTABLE_VALUES}
|
||||||
|
numRemainingText={numRemainingText}
|
||||||
|
/>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddUsersToTeam.propTypes = {
|
||||||
|
onModalDismissed: React.PropTypes.func
|
||||||
|
};
|
||||||
@@ -14,6 +14,7 @@ import AboutBuildModal from './about_build_modal.jsx';
|
|||||||
import SidebarHeaderDropdownButton from './sidebar_header_dropdown_button.jsx';
|
import SidebarHeaderDropdownButton from './sidebar_header_dropdown_button.jsx';
|
||||||
import TeamMembersModal from './team_members_modal.jsx';
|
import TeamMembersModal from './team_members_modal.jsx';
|
||||||
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
|
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
|
||||||
|
import AddUsersToTeam from './add_users_to_team.jsx';
|
||||||
|
|
||||||
import {Constants, WebrtcActionTypes} from 'utils/constants.jsx';
|
import {Constants, WebrtcActionTypes} from 'utils/constants.jsx';
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
this.handleAboutModal = this.handleAboutModal.bind(this);
|
this.handleAboutModal = this.handleAboutModal.bind(this);
|
||||||
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
|
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
|
||||||
this.toggleAccountSettingsModal = this.toggleAccountSettingsModal.bind(this);
|
this.toggleAccountSettingsModal = this.toggleAccountSettingsModal.bind(this);
|
||||||
|
this.showAddUsersToTeamModal = this.showAddUsersToTeamModal.bind(this);
|
||||||
|
this.hideAddUsersToTeamModal = this.hideAddUsersToTeamModal.bind(this);
|
||||||
this.showInviteMemberModal = this.showInviteMemberModal.bind(this);
|
this.showInviteMemberModal = this.showInviteMemberModal.bind(this);
|
||||||
this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
|
this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
|
||||||
this.showTeamMembersModal = this.showTeamMembersModal.bind(this);
|
this.showTeamMembersModal = this.showTeamMembersModal.bind(this);
|
||||||
@@ -61,7 +64,8 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
showAboutModal: false,
|
showAboutModal: false,
|
||||||
showDropdown: false,
|
showDropdown: false,
|
||||||
showTeamMembersModal: false,
|
showTeamMembersModal: false,
|
||||||
showUserSettingsModal: false
|
showUserSettingsModal: false,
|
||||||
|
showAddUsersToTeamModal: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +106,21 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showAddUsersToTeamModal(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
showAddUsersToTeamModal: true,
|
||||||
|
showDropdown: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hideAddUsersToTeamModal() {
|
||||||
|
this.setState({
|
||||||
|
showAddUsersToTeamModal: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
showInviteMemberModal(e) {
|
showInviteMemberModal(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -181,6 +200,7 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
const currentUser = this.props.currentUser;
|
const currentUser = this.props.currentUser;
|
||||||
let teamLink = '';
|
let teamLink = '';
|
||||||
let inviteLink = '';
|
let inviteLink = '';
|
||||||
|
let addMemberToTeam = '';
|
||||||
let manageLink = '';
|
let manageLink = '';
|
||||||
let sysAdminLink = '';
|
let sysAdminLink = '';
|
||||||
let isAdmin = false;
|
let isAdmin = false;
|
||||||
@@ -204,7 +224,22 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='navbar_dropdown.inviteMember'
|
id='navbar_dropdown.inviteMember'
|
||||||
defaultMessage='Invite New Member'
|
defaultMessage='Send Email Invite'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
addMemberToTeam = (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
id='addUsersToTeam'
|
||||||
|
href='#'
|
||||||
|
onClick={this.showAddUsersToTeamModal}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='navbar_dropdown.addMemberToTeam'
|
||||||
|
defaultMessage='Add Members to Team'
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -230,9 +265,11 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
if (config.RestrictTeamInvite === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
|
if (config.RestrictTeamInvite === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
|
||||||
teamLink = null;
|
teamLink = null;
|
||||||
inviteLink = null;
|
inviteLink = null;
|
||||||
|
addMemberToTeam = null;
|
||||||
} else if (config.RestrictTeamInvite === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
|
} else if (config.RestrictTeamInvite === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
|
||||||
teamLink = null;
|
teamLink = null;
|
||||||
inviteLink = null;
|
inviteLink = null;
|
||||||
|
addMemberToTeam = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,10 +453,8 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let nativeAppDivider = null;
|
|
||||||
let nativeAppLink = null;
|
let nativeAppLink = null;
|
||||||
if (global.window.mm_config.AppDownloadLink && !UserAgent.isMobileApp()) {
|
if (global.window.mm_config.AppDownloadLink && !UserAgent.isMobileApp()) {
|
||||||
nativeAppDivider = <li className='divider'/>;
|
|
||||||
nativeAppLink = (
|
nativeAppLink = (
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Link
|
||||||
@@ -447,6 +482,92 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let addUsersToTeamModal;
|
||||||
|
if (this.state.showAddUsersToTeamModal) {
|
||||||
|
addUsersToTeamModal = (
|
||||||
|
<AddUsersToTeam
|
||||||
|
onModalDismissed={this.hideAddUsersToTeamModal}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountSettings = (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
id='accountSettings'
|
||||||
|
href='#'
|
||||||
|
onClick={this.toggleAccountSettingsModal}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='navbar_dropdown.accountSettings'
|
||||||
|
defaultMessage='Account Settings'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
const about = (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href='#'
|
||||||
|
onClick={this.handleAboutModal}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='navbar_dropdown.about'
|
||||||
|
defaultMessage='About Mattermost'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
const logout = (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
id='logout'
|
||||||
|
href='#'
|
||||||
|
onClick={() => GlobalActions.emitUserLoggedOutEvent()}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='navbar_dropdown.logout'
|
||||||
|
defaultMessage='Logout'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
const customEmoji = this.renderCustomEmojiLink();
|
||||||
|
|
||||||
|
// Dividers.
|
||||||
|
let inviteDivider = null;
|
||||||
|
if (inviteLink || teamLink || addMemberToTeam) {
|
||||||
|
inviteDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let teamDivider = null;
|
||||||
|
if (teamSettings || manageLink || teams) {
|
||||||
|
teamDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let backstageDivider = null;
|
||||||
|
if (integrationsLink || customEmoji) {
|
||||||
|
backstageDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sysAdminDivider = null;
|
||||||
|
if (sysAdminLink) {
|
||||||
|
sysAdminDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let helpDivider = null;
|
||||||
|
if (helpLink || reportLink || nativeAppLink || about) {
|
||||||
|
helpDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let logoutDivider = null;
|
||||||
|
if (logout) {
|
||||||
|
logoutDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
id='sidebar-header-dropdown'
|
id='sidebar-header-dropdown'
|
||||||
@@ -460,56 +581,27 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
onClick={this.toggleDropdown}
|
onClick={this.toggleDropdown}
|
||||||
/>
|
/>
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
<li>
|
{accountSettings}
|
||||||
<a
|
{inviteDivider}
|
||||||
id='accountSettings'
|
|
||||||
href='#'
|
|
||||||
onClick={this.toggleAccountSettingsModal}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='navbar_dropdown.accountSettings'
|
|
||||||
defaultMessage='Account Settings'
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{inviteLink}
|
{inviteLink}
|
||||||
{teamLink}
|
{teamLink}
|
||||||
<li>
|
{addMemberToTeam}
|
||||||
<a
|
{teamDivider}
|
||||||
id='logout'
|
|
||||||
href='#'
|
|
||||||
onClick={() => GlobalActions.emitUserLoggedOutEvent()}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='navbar_dropdown.logout'
|
|
||||||
defaultMessage='Logout'
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li className='divider'/>
|
|
||||||
{integrationsLink}
|
|
||||||
{this.renderCustomEmojiLink()}
|
|
||||||
<li className='divider'/>
|
|
||||||
{teamSettings}
|
{teamSettings}
|
||||||
{manageLink}
|
{manageLink}
|
||||||
{sysAdminLink}
|
|
||||||
{teams}
|
{teams}
|
||||||
<li className='divider'/>
|
{backstageDivider}
|
||||||
|
{integrationsLink}
|
||||||
|
{customEmoji}
|
||||||
|
{sysAdminDivider}
|
||||||
|
{sysAdminLink}
|
||||||
|
{helpDivider}
|
||||||
{helpLink}
|
{helpLink}
|
||||||
{reportLink}
|
{reportLink}
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
href='#'
|
|
||||||
onClick={this.handleAboutModal}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='navbar_dropdown.about'
|
|
||||||
defaultMessage='About Mattermost'
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{nativeAppDivider}
|
|
||||||
{nativeAppLink}
|
{nativeAppLink}
|
||||||
|
{about}
|
||||||
|
{logoutDivider}
|
||||||
|
{logout}
|
||||||
<UserSettingsModal
|
<UserSettingsModal
|
||||||
show={this.state.showUserSettingsModal}
|
show={this.state.showUserSettingsModal}
|
||||||
onModalDismissed={() => this.setState({showUserSettingsModal: false})}
|
onModalDismissed={() => this.setState({showUserSettingsModal: false})}
|
||||||
@@ -519,6 +611,7 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
show={this.state.showAboutModal}
|
show={this.state.showAboutModal}
|
||||||
onModalDismissed={this.aboutModalDismissed}
|
onModalDismissed={this.aboutModalDismissed}
|
||||||
/>
|
/>
|
||||||
|
{addUsersToTeamModal}
|
||||||
</Dropdown.Menu>
|
</Dropdown.Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import TeamMembersModal from './team_members_modal.jsx';
|
|||||||
import ToggleModalButton from './toggle_modal_button.jsx';
|
import ToggleModalButton from './toggle_modal_button.jsx';
|
||||||
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
|
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
|
||||||
import AboutBuildModal from './about_build_modal.jsx';
|
import AboutBuildModal from './about_build_modal.jsx';
|
||||||
|
import AddUsersToTeam from './add_users_to_team.jsx';
|
||||||
|
|
||||||
import UserStore from 'stores/user_store.jsx';
|
import UserStore from 'stores/user_store.jsx';
|
||||||
import TeamStore from 'stores/team_store.jsx';
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
@@ -36,6 +37,8 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
this.handleClick = this.handleClick.bind(this);
|
this.handleClick = this.handleClick.bind(this);
|
||||||
this.handleAboutModal = this.handleAboutModal.bind(this);
|
this.handleAboutModal = this.handleAboutModal.bind(this);
|
||||||
|
this.showAddUsersToTeamModal = this.showAddUsersToTeamModal.bind(this);
|
||||||
|
this.hideAddUsersToTeamModal = this.hideAddUsersToTeamModal.bind(this);
|
||||||
this.searchMentions = this.searchMentions.bind(this);
|
this.searchMentions = this.searchMentions.bind(this);
|
||||||
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
|
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
|
||||||
this.getFlagged = this.getFlagged.bind(this);
|
this.getFlagged = this.getFlagged.bind(this);
|
||||||
@@ -43,6 +46,7 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
const state = this.getStateFromStores();
|
const state = this.getStateFromStores();
|
||||||
state.showUserSettingsModal = false;
|
state.showUserSettingsModal = false;
|
||||||
state.showAboutModal = false;
|
state.showAboutModal = false;
|
||||||
|
state.showAddUsersToTeamModal = false;
|
||||||
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
@@ -62,6 +66,21 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
this.setState({showAboutModal: false});
|
this.setState({showAboutModal: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showAddUsersToTeamModal(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
showAddUsersToTeamModal: true,
|
||||||
|
showDropdown: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hideAddUsersToTeamModal() {
|
||||||
|
this.setState({
|
||||||
|
showAddUsersToTeamModal: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getFlagged(e) {
|
getFlagged(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
getFlaggedPosts();
|
getFlaggedPosts();
|
||||||
@@ -145,6 +164,7 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
const currentUser = UserStore.getCurrentUser();
|
const currentUser = UserStore.getCurrentUser();
|
||||||
let teamLink;
|
let teamLink;
|
||||||
let inviteLink;
|
let inviteLink;
|
||||||
|
let addUserToTeamLink;
|
||||||
let teamSettingsLink;
|
let teamSettingsLink;
|
||||||
let manageLink;
|
let manageLink;
|
||||||
let consoleLink;
|
let consoleLink;
|
||||||
@@ -165,7 +185,23 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
<i className='icon fa fa-user-plus'/>
|
<i className='icon fa fa-user-plus'/>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='sidebar_right_menu.inviteNew'
|
id='sidebar_right_menu.inviteNew'
|
||||||
defaultMessage='Invite New Member'
|
defaultMessage='Send Email Invite'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
addUserToTeamLink = (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
id='addUsersToTeam'
|
||||||
|
href='#'
|
||||||
|
onClick={this.showAddUsersToTeamModal}
|
||||||
|
>
|
||||||
|
<i className='icon fa fa-user-plus'/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='sidebar_right_menu.addMemberToTeam'
|
||||||
|
defaultMessage='Add Members to Team'
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -192,9 +228,11 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
|
if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
|
||||||
teamLink = null;
|
teamLink = null;
|
||||||
inviteLink = null;
|
inviteLink = null;
|
||||||
|
addUserToTeamLink = null;
|
||||||
} else if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
|
} else if (global.window.mm_config.RestrictTeamInvite === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
|
||||||
teamLink = null;
|
teamLink = null;
|
||||||
inviteLink = null;
|
inviteLink = null;
|
||||||
|
addUserToTeamLink = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +398,25 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let addUsersToTeamModal;
|
||||||
|
if (this.state.showAddUsersToTeamModal) {
|
||||||
|
addUsersToTeamModal = (
|
||||||
|
<AddUsersToTeam
|
||||||
|
onModalDismissed={this.hideAddUsersToTeamModal}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let teamDivider = null;
|
||||||
|
if (teamSettingsLink || manageLink || joinAnotherTeamLink) {
|
||||||
|
teamDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let consoleDivider = null;
|
||||||
|
if (consoleLink) {
|
||||||
|
consoleDivider = <li className='divider'/>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='sidebar--menu'
|
className='sidebar--menu'
|
||||||
@@ -414,16 +471,20 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li className='divider'/>
|
||||||
{inviteLink}
|
{inviteLink}
|
||||||
{teamLink}
|
{teamLink}
|
||||||
{joinAnotherTeamLink}
|
{addUserToTeamLink}
|
||||||
<li className='divider'/>
|
{teamDivider}
|
||||||
{teamSettingsLink}
|
{teamSettingsLink}
|
||||||
{manageLink}
|
{manageLink}
|
||||||
|
{joinAnotherTeamLink}
|
||||||
|
{consoleDivider}
|
||||||
{consoleLink}
|
{consoleLink}
|
||||||
<li className='divider'/>
|
<li className='divider'/>
|
||||||
{helpLink}
|
{helpLink}
|
||||||
{reportLink}
|
{reportLink}
|
||||||
|
{nativeAppLink}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href='#'
|
href='#'
|
||||||
@@ -437,8 +498,6 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className='divider'/>
|
<li className='divider'/>
|
||||||
{nativeAppLink}
|
|
||||||
<li className='divider'/>
|
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href='#'
|
href='#'
|
||||||
@@ -461,6 +520,7 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
show={this.state.showAboutModal}
|
show={this.state.showAboutModal}
|
||||||
onModalDismissed={this.aboutModalDismissed}
|
onModalDismissed={this.aboutModalDismissed}
|
||||||
/>
|
/>
|
||||||
|
{addUsersToTeamModal}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,7 @@
|
|||||||
"add_outgoing_webhook.triggerWordsTriggerWhen.help": "Choose when to trigger the outgoing webhook; if the first word of a message matches a Trigger Word exactly, or if it starts with a Trigger Word.",
|
"add_outgoing_webhook.triggerWordsTriggerWhen.help": "Choose when to trigger the outgoing webhook; if the first word of a message matches a Trigger Word exactly, or if it starts with a Trigger Word.",
|
||||||
"add_outgoing_webhook.triggerWordsTriggerWhenFullWord": "First word matches a trigger word exactly",
|
"add_outgoing_webhook.triggerWordsTriggerWhenFullWord": "First word matches a trigger word exactly",
|
||||||
"add_outgoing_webhook.triggerWordsTriggerWhenStartsWith": "First word starts with a trigger word",
|
"add_outgoing_webhook.triggerWordsTriggerWhenStartsWith": "First word starts with a trigger word",
|
||||||
|
"add_users_to_team.title": "Add New Members To {teamName} Team",
|
||||||
"admin.advance.cluster": "High Availability (Beta)",
|
"admin.advance.cluster": "High Availability (Beta)",
|
||||||
"admin.advance.metrics": "Performance Monitoring",
|
"admin.advance.metrics": "Performance Monitoring",
|
||||||
"admin.audits.reload": "Reload User Activity Logs",
|
"admin.audits.reload": "Reload User Activity Logs",
|
||||||
@@ -1759,12 +1760,13 @@
|
|||||||
"navbar.viewPinnedPosts": "View Pinned Posts",
|
"navbar.viewPinnedPosts": "View Pinned Posts",
|
||||||
"navbar_dropdown.about": "About Mattermost",
|
"navbar_dropdown.about": "About Mattermost",
|
||||||
"navbar_dropdown.accountSettings": "Account Settings",
|
"navbar_dropdown.accountSettings": "Account Settings",
|
||||||
|
"navbar_dropdown.addMemberToTeam": "Add Members to Team",
|
||||||
"navbar_dropdown.console": "System Console",
|
"navbar_dropdown.console": "System Console",
|
||||||
"navbar_dropdown.create": "Create a New Team",
|
"navbar_dropdown.create": "Create a New Team",
|
||||||
"navbar_dropdown.emoji": "Custom Emoji",
|
"navbar_dropdown.emoji": "Custom Emoji",
|
||||||
"navbar_dropdown.help": "Help",
|
"navbar_dropdown.help": "Help",
|
||||||
"navbar_dropdown.integrations": "Integrations",
|
"navbar_dropdown.integrations": "Integrations",
|
||||||
"navbar_dropdown.inviteMember": "Invite New Member",
|
"navbar_dropdown.inviteMember": "Send Email Invite",
|
||||||
"navbar_dropdown.join": "Join Another Team",
|
"navbar_dropdown.join": "Join Another Team",
|
||||||
"navbar_dropdown.leave": "Leave Team",
|
"navbar_dropdown.leave": "Leave Team",
|
||||||
"navbar_dropdown.logout": "Logout",
|
"navbar_dropdown.logout": "Logout",
|
||||||
@@ -1917,10 +1919,11 @@
|
|||||||
"sidebar.unreadBelow": "Unread post(s) below",
|
"sidebar.unreadBelow": "Unread post(s) below",
|
||||||
"sidebar_header.tutorial": "<h4>Main Menu</h4><p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p><p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>",
|
"sidebar_header.tutorial": "<h4>Main Menu</h4><p>The <strong>Main Menu</strong> is where you can <strong>Invite New Members</strong>, access your <strong>Account Settings</strong> and set your <strong>Theme Color</strong>.</p><p>Team administrators can also access their <strong>Team Settings</strong> from this menu.</p><p>System administrators will find a <strong>System Console</strong> option to administrate the entire system.</p>",
|
||||||
"sidebar_right_menu.accountSettings": "Account Settings",
|
"sidebar_right_menu.accountSettings": "Account Settings",
|
||||||
|
"sidebar_right_menu.addMemberToTeam": "Add Members to Team",
|
||||||
"sidebar_right_menu.console": "System Console",
|
"sidebar_right_menu.console": "System Console",
|
||||||
"sidebar_right_menu.flagged": "Flagged Posts",
|
"sidebar_right_menu.flagged": "Flagged Posts",
|
||||||
"sidebar_right_menu.help": "Help",
|
"sidebar_right_menu.help": "Help",
|
||||||
"sidebar_right_menu.inviteNew": "Invite New Member",
|
"sidebar_right_menu.inviteNew": "Send Email Invite",
|
||||||
"sidebar_right_menu.logout": "Logout",
|
"sidebar_right_menu.logout": "Logout",
|
||||||
"sidebar_right_menu.manageMembers": "Manage Members",
|
"sidebar_right_menu.manageMembers": "Manage Members",
|
||||||
"sidebar_right_menu.nativeApps": "Download Apps",
|
"sidebar_right_menu.nativeApps": "Download Apps",
|
||||||
|
|||||||
@@ -252,6 +252,12 @@ class TeamStoreClass extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeMemberNotInTeam(teamId = this.getCurrentId(), userId) {
|
||||||
|
if (this.members_not_in_team[teamId]) {
|
||||||
|
Reflect.deleteProperty(this.members_not_in_team[teamId], userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getMembersInTeam(teamId = this.getCurrentId()) {
|
getMembersInTeam(teamId = this.getCurrentId()) {
|
||||||
return Object.assign({}, this.members_in_team[teamId]) || {};
|
return Object.assign({}, this.members_in_team[teamId]) || {};
|
||||||
}
|
}
|
||||||
@@ -365,6 +371,10 @@ TeamStore.dispatchToken = AppDispatcher.register((payload) => {
|
|||||||
TeamStore.saveMyTeam(action.team);
|
TeamStore.saveMyTeam(action.team);
|
||||||
TeamStore.emitChange();
|
TeamStore.emitChange();
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.RECEIVED_TEAM:
|
||||||
|
TeamStore.saveTeam(action.team);
|
||||||
|
TeamStore.emitChange();
|
||||||
|
break;
|
||||||
case ActionTypes.CREATED_TEAM:
|
case ActionTypes.CREATED_TEAM:
|
||||||
TeamStore.saveTeam(action.team);
|
TeamStore.saveTeam(action.team);
|
||||||
TeamStore.appendMyTeamMember(action.member);
|
TeamStore.appendMyTeamMember(action.member);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const UserStatuses = Constants.UserStatuses;
|
|||||||
|
|
||||||
const CHANGE_EVENT_NOT_IN_CHANNEL = 'change_not_in_channel';
|
const CHANGE_EVENT_NOT_IN_CHANNEL = 'change_not_in_channel';
|
||||||
const CHANGE_EVENT_IN_CHANNEL = 'change_in_channel';
|
const CHANGE_EVENT_IN_CHANNEL = 'change_in_channel';
|
||||||
|
const CHANGE_EVENT_NOT_IN_TEAM = 'change_not_in_team';
|
||||||
const CHANGE_EVENT_IN_TEAM = 'change_in_team';
|
const CHANGE_EVENT_IN_TEAM = 'change_in_team';
|
||||||
const CHANGE_EVENT_WITHOUT_TEAM = 'change_without_team';
|
const CHANGE_EVENT_WITHOUT_TEAM = 'change_without_team';
|
||||||
const CHANGE_EVENT = 'change';
|
const CHANGE_EVENT = 'change';
|
||||||
@@ -36,6 +37,11 @@ class UserStoreClass extends EventEmitter {
|
|||||||
this.paging_offset = 0;
|
this.paging_offset = 0;
|
||||||
this.paging_count = 0;
|
this.paging_count = 0;
|
||||||
|
|
||||||
|
// Lists of sorted IDs for users in a team
|
||||||
|
this.profiles_not_in_team = {};
|
||||||
|
this.not_in_team_offset = 0;
|
||||||
|
this.not_in_team_count = 0;
|
||||||
|
|
||||||
// Lists of sorted IDs for users in a team
|
// Lists of sorted IDs for users in a team
|
||||||
this.profiles_in_team = {};
|
this.profiles_in_team = {};
|
||||||
this.in_team_offset = 0;
|
this.in_team_offset = 0;
|
||||||
@@ -85,6 +91,18 @@ class UserStoreClass extends EventEmitter {
|
|||||||
this.removeListener(CHANGE_EVENT_IN_TEAM, callback);
|
this.removeListener(CHANGE_EVENT_IN_TEAM, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitNotInTeamChange() {
|
||||||
|
this.emit(CHANGE_EVENT_NOT_IN_TEAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
addNotInTeamChangeListener(callback) {
|
||||||
|
this.on(CHANGE_EVENT_NOT_IN_TEAM, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeNotInTeamChangeListener(callback) {
|
||||||
|
this.removeListener(CHANGE_EVENT_NOT_IN_TEAM, callback);
|
||||||
|
}
|
||||||
|
|
||||||
emitInChannelChange() {
|
emitInChannelChange() {
|
||||||
this.emit(CHANGE_EVENT_IN_CHANNEL);
|
this.emit(CHANGE_EVENT_IN_CHANNEL);
|
||||||
}
|
}
|
||||||
@@ -373,6 +391,75 @@ class UserStoreClass extends EventEmitter {
|
|||||||
userIds.splice(index, 1);
|
userIds.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not In Team Profiles
|
||||||
|
|
||||||
|
saveProfilesNotInTeam(teamId, profiles) {
|
||||||
|
const oldProfileList = this.profiles_not_in_team[teamId] || [];
|
||||||
|
const oldProfileMap = {};
|
||||||
|
for (let i = 0; i < oldProfileList.length; i++) {
|
||||||
|
oldProfileMap[oldProfileList[i]] = this.getProfile(oldProfileList[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newProfileMap = Object.assign({}, oldProfileMap, profiles);
|
||||||
|
const newProfileList = Object.keys(newProfileMap);
|
||||||
|
|
||||||
|
newProfileList.sort((a, b) => {
|
||||||
|
const aProfile = newProfileMap[a];
|
||||||
|
const bProfile = newProfileMap[b];
|
||||||
|
|
||||||
|
if (aProfile.username < bProfile.username) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (aProfile.username > bProfile.username) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.profiles_not_in_team[teamId] = newProfileList;
|
||||||
|
this.saveProfiles(profiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeProfileNotInTeam(teamId, userId) {
|
||||||
|
const userIds = this.profiles_not_in_team[teamId];
|
||||||
|
if (!userIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = userIds.indexOf(userId);
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
userIds.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getProfileListNotInTeam(teamId = TeamStore.getCurrentId(), skipCurrent = false, skipInactive = false) {
|
||||||
|
const userIds = this.profiles_not_in_team[teamId] || [];
|
||||||
|
const profiles = [];
|
||||||
|
const currentId = this.getCurrentId();
|
||||||
|
|
||||||
|
for (let i = 0; i < userIds.length; i++) {
|
||||||
|
const profile = this.getProfile(userIds[i]);
|
||||||
|
|
||||||
|
if (!profile) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipCurrent && profile.id === currentId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipInactive && profile.delete_at > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles.push(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return profiles;
|
||||||
|
}
|
||||||
|
|
||||||
// Channel-Wide Profiles
|
// Channel-Wide Profiles
|
||||||
|
|
||||||
saveProfilesInChannel(channelId = ChannelStore.getCurrentId(), profiles) {
|
saveProfilesInChannel(channelId = ChannelStore.getCurrentId(), profiles) {
|
||||||
@@ -646,6 +733,19 @@ class UserStoreClass extends EventEmitter {
|
|||||||
return this.in_team_count;
|
return this.in_team_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNotInTeamPage(offset, count) {
|
||||||
|
this.not_in_team_offset = offset + count;
|
||||||
|
this.not_in_team_count = this.not_in_team_count + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotInTeamPagingOffset() {
|
||||||
|
return this.not_in_team_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotInTeamPagingCount() {
|
||||||
|
return this.not_in_team_count;
|
||||||
|
}
|
||||||
|
|
||||||
setInChannelPage(channelId, offset, count) {
|
setInChannelPage(channelId, offset, count) {
|
||||||
this.in_channel_offset[channelId] = offset + count;
|
this.in_channel_offset[channelId] = offset + count;
|
||||||
this.in_channel_count[channelId] = this.dm_paging_count + count;
|
this.in_channel_count[channelId] = this.dm_paging_count + count;
|
||||||
@@ -694,6 +794,13 @@ UserStore.dispatchToken = AppDispatcher.register((payload) => {
|
|||||||
}
|
}
|
||||||
UserStore.emitInTeamChange();
|
UserStore.emitInTeamChange();
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.RECEIVED_PROFILES_NOT_IN_TEAM:
|
||||||
|
UserStore.saveProfilesNotInTeam(action.team_id, action.profiles);
|
||||||
|
if (action.offset != null && action.count != null) {
|
||||||
|
UserStore.setNotInTeamPage(action.offset, action.count);
|
||||||
|
}
|
||||||
|
UserStore.emitNotInTeamChange();
|
||||||
|
break;
|
||||||
case ActionTypes.RECEIVED_PROFILES_IN_CHANNEL:
|
case ActionTypes.RECEIVED_PROFILES_IN_CHANNEL:
|
||||||
UserStore.saveProfilesInChannel(action.channel_id, action.profiles);
|
UserStore.saveProfilesInChannel(action.channel_id, action.profiles);
|
||||||
if (action.offset != null && action.count != null) {
|
if (action.offset != null && action.count != null) {
|
||||||
|
|||||||
@@ -403,6 +403,36 @@ export function getProfilesInTeam(teamId = TeamStore.getCurrentId(), offset = Us
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getProfilesNotInTeam(teamId = TeamStore.getCurrentId(), offset = UserStore.getInTeamPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
|
||||||
|
const callName = `getProfilesNotInTeam${teamId}${offset}${limit}`;
|
||||||
|
|
||||||
|
if (isCallInProgress(callName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callTracker[callName] = utils.getTimestamp();
|
||||||
|
Client.getProfilesNotInTeam(
|
||||||
|
teamId,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
(data) => {
|
||||||
|
callTracker[callName] = 0;
|
||||||
|
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_PROFILES_NOT_IN_TEAM,
|
||||||
|
profiles: data,
|
||||||
|
team_id: teamId,
|
||||||
|
offset,
|
||||||
|
count: Object.keys(data).length
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
callTracker[callName] = 0;
|
||||||
|
dispatchError(err, 'getProfilesNotInTeam');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getProfilesInChannel(channelId = ChannelStore.getCurrentId(), offset = UserStore.getInChannelPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
|
export function getProfilesInChannel(channelId = ChannelStore.getCurrentId(), offset = UserStore.getInChannelPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
|
||||||
const callName = `getProfilesInChannel${channelId}${offset}${limit}`;
|
const callName = `getProfilesInChannel${channelId}${offset}${limit}`;
|
||||||
|
|
||||||
@@ -830,34 +860,6 @@ export function getTeamMember(teamId, userId) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMyTeamMembers() {
|
|
||||||
const callName = 'getMyTeamMembers';
|
|
||||||
if (isCallInProgress(callName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callTracker[callName] = utils.getTimestamp();
|
|
||||||
Client.getMyTeamMembers(
|
|
||||||
(data) => {
|
|
||||||
callTracker[callName] = 0;
|
|
||||||
|
|
||||||
const members = {};
|
|
||||||
for (const member of data) {
|
|
||||||
members[member.team_id] = member;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppDispatcher.handleServerAction({
|
|
||||||
type: ActionTypes.RECEIVED_MY_TEAM_MEMBERS_UNREAD,
|
|
||||||
team_members: members
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
callTracker[callName] = 0;
|
|
||||||
dispatchError(err, 'getMyTeamMembers');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMyTeamsUnread(teamId) {
|
export function getMyTeamsUnread(teamId) {
|
||||||
const members = TeamStore.getMyTeamMembers();
|
const members = TeamStore.getMyTeamMembers();
|
||||||
if (members.length > 1) {
|
if (members.length > 1) {
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ export const ActionTypes = keyMirror({
|
|||||||
|
|
||||||
RECEIVED_PROFILES: null,
|
RECEIVED_PROFILES: null,
|
||||||
RECEIVED_PROFILES_IN_TEAM: null,
|
RECEIVED_PROFILES_IN_TEAM: null,
|
||||||
|
RECEIVED_PROFILES_NOT_IN_TEAM: null,
|
||||||
RECEIVED_PROFILE: null,
|
RECEIVED_PROFILE: null,
|
||||||
RECEIVED_PROFILES_IN_CHANNEL: null,
|
RECEIVED_PROFILES_IN_CHANNEL: null,
|
||||||
RECEIVED_PROFILES_NOT_IN_CHANNEL: null,
|
RECEIVED_PROFILES_NOT_IN_CHANNEL: null,
|
||||||
@@ -137,6 +138,7 @@ export const ActionTypes = keyMirror({
|
|||||||
|
|
||||||
RECEIVED_MSG: null,
|
RECEIVED_MSG: null,
|
||||||
|
|
||||||
|
RECEIVED_TEAM: null,
|
||||||
RECEIVED_MY_TEAM: null,
|
RECEIVED_MY_TEAM: null,
|
||||||
CREATED_TEAM: null,
|
CREATED_TEAM: null,
|
||||||
UPDATE_TEAM: null,
|
UPDATE_TEAM: null,
|
||||||
@@ -219,6 +221,7 @@ export const SocketEvents = {
|
|||||||
CHANNEL_VIEWED: 'channel_viewed',
|
CHANNEL_VIEWED: 'channel_viewed',
|
||||||
DIRECT_ADDED: 'direct_added',
|
DIRECT_ADDED: 'direct_added',
|
||||||
NEW_USER: 'new_user',
|
NEW_USER: 'new_user',
|
||||||
|
ADDED_TO_TEAM: 'added_to_team',
|
||||||
LEAVE_TEAM: 'leave_team',
|
LEAVE_TEAM: 'leave_team',
|
||||||
UPDATE_TEAM: 'update_team',
|
UPDATE_TEAM: 'update_team',
|
||||||
USER_ADDED: 'user_added',
|
USER_ADDED: 'user_added',
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user