Merging performance branch into master (#4268)
* improve performance on sendNotifications * Fix SQL queries * Remove get direct profiles, not needed anymore * Add raw data to error details if AppError fails to decode * men * Fix decode (#4052) * Fixing json decode * Adding unit test * Initial work for client scaling (#4051) * Begin adding paging to profiles API * Added more paging functionality * Finish hooking up admin console user lists * Add API for searching users and add searching to all user lists * Add lazy loading of profiles * Revert config.json * Fix unit tests and some style issues * Add GetProfilesFromList to Go driver and fix web unit test * Update etag for GetProfiles * Updating ui for filters and pagination (#4044) * Updating UI for pagination * Adjusting margins for filter row * Adjusting margin for specific modals * Adding relative padding to system console * Adjusting responsive view * Update client user tests * Minor fixes for direct messages modal (#4056) * Remove some unneeded initial load calls (#4057) * UX updates to user lists, added smart counts and bug fixes (#4059) * Improved getExplicitMentions and unit tests (#4064) * Refactor getting posts to lazy load profiles correctly (#4062) * Comment out SetActiveChannel test (#4066) * Profiler cpu, block, and memory profiler. (#4081) * Fix TestSetActiveChannel unit test (#4071) * Fixing build failure caused by dependancies updating (#4076) * Adding profiler * Fix admin_team_member_dropdown eslint errors * Bumping session cache size (#4077) * Bumping session cache size * Bumping status cache * Refactor how the client handles channel members to be large team friendly (#4106) * Refactor how the client handles channel members to be large team friendly * Change Id to ChannelId in ChannelStats model * Updated getChannelMember and getProfilesByIds routes to match proposal * Performance improvements (#4100) * Performance improvements * Fixing re-connect issue * Fixing error message * Some other minor perf tweaks * Some other minor perf tweaks * Fixing config file * Fixing buffer size * Fixing web socket send message * adding some error logging * fix getMe to be user required * Fix websocket event for new user * Fixing shutting down * Reverting web socket changes * Fixing logging lvl * Adding caching to GetMember * Adding some logging * Fixing caching * Fixing caching invalidate * Fixing direct message caching * Fixing caching * Fixing caching * Remove GetDirectProfiles from initial load * Adding logging and fixing websocket client * Adding back caching from bad merge. * Explicitly close go driver requests (#4162) * Refactored how the client handles team members to be more large team friendly (#4159) * Refactor getProfilesForDirectMessageList API into getAllProfiles API * Refactored how the client handles team members to be more large team friendly * Fix js error when receiving a notification * Fix JS error caused by current user being overwritten with sanitized version (#4165) * Adding error message to status failure (#4167) * Fix a few bugs caused by client scaling refactoring (#4170) * When there is no read replica, don't open a second set of connections to the master database (#4173) * Adding connection tacking to stats (#4174) * Reduce DB writes for statuses and other status related changes (#4175) * Fix bug preventing opening of DM channels from more modal (#4181) * Fixing socket timing error (#4183) * Fixing ping/pong handler * Fixing socket timing error * Commenting out status broadcasting * Removing user status changes * Removing user status changes * Removing user status changes * Removing user status changes * Adding DoPreComputeJson() * Performance improvements (#4194) * * Fix System Console Analytics queries * Add db.SetConnMaxLifetime to 15 minutes * Add "net/http/pprof" for profiling * Add FreeOSMemory() to manually release memory on reload config * Add flag to enable http profiler * Fix memory leak (#4197) * Fix memory leak * removed unneeded nil assignment * Fixing go routine leak (#4208) * Merge fixes * Merge fix * Refactored statuses to be queried by the client rather than broadcast by the server (#4212) * Refactored server code to reduce status broadcasts and to allow getting statuses by IDs * Refactor client code to periodically fetch statuses * Add store unit test for getting statuses by ids * Fix status unit test * Add getStatusesByIds REST API and move the client over to use that instead of the WebSocket * Adding multiple threads to websocket hub (#4230) * Adding multiple threads to websocket hub * Fixing unit tests * Fixing so websocket connections from the same user end up in the same… (#4240) * Fixing so websocket connections from the same user end up in the same list * Removing old comment * Refactor user autocomplete to query the server (#4239) * Add API for autocompleting users * Converted at mention autocomplete to query server * Converted user search autocomplete to query server * Switch autocomplete API naming to use term instead of username * Split autocomplete API into two, one for channels and for teams * Fix copy/paste error * Some final client scaling fixes (#4246) * Add lazy loading of profiles to integration pages * Add lazy loading of profiles to emoji page * Fix JS error when receiving post in select team menu and also clean up channel store
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
0512bd26ee
Коммит
365b8b465e
@@ -1,18 +1,26 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
|
||||
import {loadProfilesAndTeamMembersForDMSidebar} from 'actions/user_actions.jsx';
|
||||
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as UserAgent from 'utils/user_agent.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import {Preferences, ActionTypes} from 'utils/constants.jsx';
|
||||
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
|
||||
export function goToChannel(channel) {
|
||||
if (channel.fake) {
|
||||
Utils.openDirectChannelToUser(
|
||||
openDirectChannelToUser(
|
||||
UserStore.getProfileByUsername(channel.display_name),
|
||||
() => {
|
||||
browserHistory.push(TeamStore.getCurrentTeamRelativeUrl() + '/channels/' + channel.name);
|
||||
@@ -53,3 +61,124 @@ export function setChannelAsRead(channelIdParam) {
|
||||
ChannelStore.emitLastViewed(Number.MAX_VALUE, false);
|
||||
}
|
||||
}
|
||||
|
||||
export function addUserToChannel(channelId, userId, success, error) {
|
||||
Client.addChannelMember(
|
||||
channelId,
|
||||
userId,
|
||||
(data) => {
|
||||
UserStore.removeProfileNotInChannel(channelId, userId);
|
||||
const profile = UserStore.getProfile(userId);
|
||||
if (profile) {
|
||||
UserStore.saveProfileInChannel(channelId, profile);
|
||||
UserStore.emitInChannelChange();
|
||||
}
|
||||
UserStore.emitNotInChannelChange();
|
||||
|
||||
if (success) {
|
||||
success(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'addChannelMember');
|
||||
|
||||
if (error) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function removeUserFromChannel(channelId, userId, success, error) {
|
||||
Client.removeChannelMember(
|
||||
channelId,
|
||||
userId,
|
||||
(data) => {
|
||||
UserStore.removeProfileInChannel(channelId, userId);
|
||||
const profile = UserStore.getProfile(userId);
|
||||
if (profile) {
|
||||
UserStore.saveProfileNotInChannel(channelId, profile);
|
||||
UserStore.emitNotInChannelChange();
|
||||
}
|
||||
UserStore.emitInChannelChange();
|
||||
|
||||
if (success) {
|
||||
success(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'removeChannelMember');
|
||||
|
||||
if (error) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function openDirectChannelToUser(user, success, error) {
|
||||
const channelName = Utils.getDirectChannelName(UserStore.getCurrentId(), user.id);
|
||||
let channel = ChannelStore.getByName(channelName);
|
||||
|
||||
if (channel) {
|
||||
PreferenceStore.setPreference(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, user.id, 'true');
|
||||
loadProfilesAndTeamMembersForDMSidebar();
|
||||
|
||||
AsyncClient.savePreference(
|
||||
Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
user.id,
|
||||
'true'
|
||||
);
|
||||
|
||||
if (success) {
|
||||
success(channel, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
channel = {
|
||||
name: channelName,
|
||||
last_post_at: 0,
|
||||
total_msg_count: 0,
|
||||
type: 'D',
|
||||
display_name: user.username,
|
||||
teammate_id: user.id,
|
||||
status: UserStore.getStatus(user.id)
|
||||
};
|
||||
|
||||
Client.createDirectChannel(
|
||||
user.id,
|
||||
(data) => {
|
||||
Client.getChannel(
|
||||
data.id,
|
||||
(data2) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_CHANNEL,
|
||||
channel: data2.channel,
|
||||
member: data2.member
|
||||
});
|
||||
|
||||
PreferenceStore.setPreference(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, user.id, 'true');
|
||||
loadProfilesAndTeamMembersForDMSidebar();
|
||||
|
||||
AsyncClient.savePreference(
|
||||
Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
user.id,
|
||||
'true'
|
||||
);
|
||||
|
||||
if (success) {
|
||||
success(data2.channel, false);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
() => {
|
||||
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/' + channelName);
|
||||
if (error) {
|
||||
error();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
46
webapp/actions/emoji_actions.jsx
Обычный файл
46
webapp/actions/emoji_actions.jsx
Обычный файл
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
|
||||
import {ActionTypes} from 'utils/constants.jsx';
|
||||
|
||||
export function loadEmoji(getProfiles = true) {
|
||||
Client.listEmoji(
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_CUSTOM_EMOJIS,
|
||||
emojis: data
|
||||
});
|
||||
|
||||
if (getProfiles) {
|
||||
loadProfilesForEmoji(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'listEmoji');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadProfilesForEmoji(emojiList) {
|
||||
const profilesToLoad = {};
|
||||
for (let i = 0; i < emojiList.length; i++) {
|
||||
const emoji = emojiList[i];
|
||||
if (!UserStore.hasProfile(emoji.creator_id)) {
|
||||
profilesToLoad[emoji.creator_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = Object.keys(profilesToLoad);
|
||||
if (list.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncClient.getProfilesByIds(list);
|
||||
}
|
||||
@@ -12,7 +12,8 @@ import TeamStore from 'stores/team_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import SearchStore from 'stores/search_store.jsx';
|
||||
|
||||
import {handleNewPost} from 'actions/post_actions.jsx';
|
||||
import {handleNewPost, loadPosts, loadPostsBefore, loadPostsAfter} from 'actions/post_actions.jsx';
|
||||
import {loadProfilesAndTeamMembersForDMSidebar} from 'actions/user_actions.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
@@ -43,9 +44,9 @@ export function emitChannelClickEvent(channel) {
|
||||
function switchToChannel(chan) {
|
||||
AsyncClient.getChannels(true);
|
||||
AsyncClient.getMoreChannels(true);
|
||||
AsyncClient.getChannelExtraInfo(chan.id);
|
||||
AsyncClient.getChannelStats(chan.id);
|
||||
AsyncClient.updateLastViewedAt(chan.id);
|
||||
AsyncClient.getPosts(chan.id);
|
||||
loadPosts(chan.id);
|
||||
trackPage();
|
||||
|
||||
AppDispatcher.handleViewAction({
|
||||
@@ -108,7 +109,7 @@ export function emitInitialLoad(callback) {
|
||||
|
||||
if (data.team_members) {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_TEAM_MEMBERS,
|
||||
type: ActionTypes.RECEIVED_MY_TEAM_MEMBERS,
|
||||
team_members: data.team_members
|
||||
});
|
||||
}
|
||||
@@ -143,9 +144,9 @@ export function doFocusPost(channelId, postId, data) {
|
||||
});
|
||||
AsyncClient.getChannels(true);
|
||||
AsyncClient.getMoreChannels(true);
|
||||
AsyncClient.getChannelExtraInfo(channelId);
|
||||
AsyncClient.getPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true);
|
||||
AsyncClient.getPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true);
|
||||
AsyncClient.getChannelStats(channelId);
|
||||
loadPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true);
|
||||
loadPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true);
|
||||
}
|
||||
|
||||
export function emitPostFocusEvent(postId, onSuccess) {
|
||||
@@ -246,14 +247,14 @@ export function emitLoadMorePostsFocusedTopEvent() {
|
||||
export function loadMorePostsTop(id, isFocusPost) {
|
||||
const earliestPostId = PostStore.getEarliestPost(id).id;
|
||||
if (PostStore.requestVisibilityIncrease(id, Constants.POST_CHUNK_SIZE)) {
|
||||
AsyncClient.getPostsBefore(earliestPostId, 0, Constants.POST_CHUNK_SIZE, isFocusPost);
|
||||
loadPostsBefore(earliestPostId, 0, Constants.POST_CHUNK_SIZE, isFocusPost);
|
||||
}
|
||||
}
|
||||
|
||||
export function emitLoadMorePostsFocusedBottomEvent() {
|
||||
const id = PostStore.getFocusedPostId();
|
||||
const latestPostId = PostStore.getLatestPost(id).id;
|
||||
AsyncClient.getPostsAfter(latestPostId, 0, Constants.POST_CHUNK_SIZE, Boolean(id));
|
||||
loadPostsAfter(latestPostId, 0, Constants.POST_CHUNK_SIZE, Boolean(id));
|
||||
}
|
||||
|
||||
export function emitUserPostedEvent(post) {
|
||||
@@ -362,7 +363,7 @@ export function emitClearSuggestions(suggestionId) {
|
||||
|
||||
export function emitPreferenceChangedEvent(preference) {
|
||||
if (preference.category === Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW) {
|
||||
AsyncClient.getDirectProfiles();
|
||||
loadProfilesAndTeamMembersForDMSidebar();
|
||||
}
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
@@ -437,7 +438,7 @@ export function loadDefaultLocale() {
|
||||
export function viewLoggedIn() {
|
||||
AsyncClient.getChannels();
|
||||
AsyncClient.getMoreChannels();
|
||||
AsyncClient.getChannelExtraInfo();
|
||||
AsyncClient.getChannelStats();
|
||||
|
||||
// Clear pending posts (shouldn't have pending posts if we are loading)
|
||||
PostStore.clearPendingPosts();
|
||||
|
||||
114
webapp/actions/integration_actions.jsx
Обычный файл
114
webapp/actions/integration_actions.jsx
Обычный файл
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
|
||||
import {ActionTypes} from 'utils/constants.jsx';
|
||||
|
||||
export function loadIncomingHooks() {
|
||||
Client.listIncomingHooks(
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_INCOMING_WEBHOOKS,
|
||||
teamId: TeamStore.getCurrentId(),
|
||||
incomingWebhooks: data
|
||||
});
|
||||
|
||||
loadProfilesForIncomingHooks(data);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'listIncomingHooks');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadProfilesForIncomingHooks(hooks) {
|
||||
const profilesToLoad = {};
|
||||
for (let i = 0; i < hooks.length; i++) {
|
||||
const hook = hooks[i];
|
||||
if (!UserStore.hasProfile(hook.user_id)) {
|
||||
profilesToLoad[hook.user_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = Object.keys(profilesToLoad);
|
||||
if (list.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncClient.getProfilesByIds(list);
|
||||
}
|
||||
|
||||
export function loadOutgoingHooks() {
|
||||
Client.listOutgoingHooks(
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_OUTGOING_WEBHOOKS,
|
||||
teamId: TeamStore.getCurrentId(),
|
||||
outgoingWebhooks: data
|
||||
});
|
||||
|
||||
loadProfilesForOutgoingHooks(data);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'listOutgoingHooks');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadProfilesForOutgoingHooks(hooks) {
|
||||
const profilesToLoad = {};
|
||||
for (let i = 0; i < hooks.length; i++) {
|
||||
const hook = hooks[i];
|
||||
if (!UserStore.hasProfile(hook.creator_id)) {
|
||||
profilesToLoad[hook.creator_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = Object.keys(profilesToLoad);
|
||||
if (list.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncClient.getProfilesByIds(list);
|
||||
}
|
||||
|
||||
export function loadTeamCommands() {
|
||||
Client.listTeamCommands(
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_COMMANDS,
|
||||
teamId: Client.teamId,
|
||||
commands: data
|
||||
});
|
||||
|
||||
loadProfilesForCommands(data);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'loadTeamCommands');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadProfilesForCommands(commands) {
|
||||
const profilesToLoad = {};
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
const command = commands[i];
|
||||
if (!UserStore.hasProfile(command.creator_id)) {
|
||||
profilesToLoad[command.creator_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = Object.keys(profilesToLoad);
|
||||
if (list.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncClient.getProfilesByIds(list);
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import PostStore from 'stores/post_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {loadStatusesForChannel} from 'actions/status_actions.jsx';
|
||||
|
||||
import * as PostUtils from 'utils/post_utils.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
@@ -52,6 +54,8 @@ export function handleNewPost(post, msg) {
|
||||
post,
|
||||
websocketMessageProps
|
||||
});
|
||||
|
||||
loadProfilesForPosts(data.posts);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getPost');
|
||||
@@ -115,7 +119,7 @@ export function setUnreadPost(channelId, postId) {
|
||||
member.last_viewed_at = lastViewed;
|
||||
member.msg_count = channel.total_msg_count - unreadPosts;
|
||||
member.mention_count = 0;
|
||||
ChannelStore.setChannelMember(member);
|
||||
ChannelStore.storeMyChannelMember(member);
|
||||
ChannelStore.setUnreadCount(channelId);
|
||||
AsyncClient.setLastViewedAt(lastViewed, channelId);
|
||||
}
|
||||
@@ -153,9 +157,156 @@ export function getFlaggedPosts() {
|
||||
results: data,
|
||||
is_flagged_posts: true
|
||||
});
|
||||
|
||||
loadProfilesForPosts(data.posts);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getFlaggedPosts');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function loadPosts(channelId = ChannelStore.getCurrentId()) {
|
||||
const postList = PostStore.getAllPosts(channelId);
|
||||
const latestPostTime = PostStore.getLatestPostFromPageTime(channelId);
|
||||
|
||||
if (!postList || Object.keys(postList).length === 0 || postList.order.length < Constants.POST_CHUNK_SIZE || latestPostTime === 0) {
|
||||
loadPostsPage(channelId, Constants.POST_CHUNK_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
Client.getPosts(
|
||||
channelId,
|
||||
latestPostTime,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: true,
|
||||
numRequested: 0,
|
||||
post_list: data
|
||||
});
|
||||
|
||||
loadProfilesForPosts(data.posts);
|
||||
loadStatusesForChannel(channelId);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'loadPosts');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function loadPostsPage(channelId = ChannelStore.getCurrentId(), max = Constants.POST_CHUNK_SIZE) {
|
||||
const postList = PostStore.getAllPosts(channelId);
|
||||
|
||||
// if we already have more than POST_CHUNK_SIZE posts,
|
||||
// let's get the amount we have but rounded up to next multiple of POST_CHUNK_SIZE,
|
||||
// with a max
|
||||
let numPosts = Math.min(max, Constants.POST_CHUNK_SIZE);
|
||||
if (postList && postList.order.length > 0) {
|
||||
numPosts = Math.min(max, Constants.POST_CHUNK_SIZE * Math.ceil(postList.order.length / Constants.POST_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
Client.getPostsPage(
|
||||
channelId,
|
||||
0,
|
||||
numPosts,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: true,
|
||||
numRequested: numPosts,
|
||||
checkLatest: true,
|
||||
post_list: data
|
||||
});
|
||||
|
||||
loadProfilesForPosts(data.posts);
|
||||
loadStatusesForChannel(channelId);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'loadPostsPage');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function loadPostsBefore(postId, offset, numPost, isPost) {
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Client.getPostsBefore(
|
||||
channelId,
|
||||
postId,
|
||||
offset,
|
||||
numPost,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: true,
|
||||
numRequested: numPost,
|
||||
post_list: data,
|
||||
isPost
|
||||
});
|
||||
|
||||
loadProfilesForPosts(data.posts);
|
||||
loadStatusesForChannel(channelId);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'loadPostsBefore');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function loadPostsAfter(postId, offset, numPost, isPost) {
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Client.getPostsAfter(
|
||||
channelId,
|
||||
postId,
|
||||
offset,
|
||||
numPost,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: false,
|
||||
numRequested: numPost,
|
||||
post_list: data,
|
||||
isPost
|
||||
});
|
||||
|
||||
loadProfilesForPosts(data.posts);
|
||||
loadStatusesForChannel(channelId);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'loadPostsAfter');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function loadProfilesForPosts(posts) {
|
||||
const profilesToLoad = {};
|
||||
for (const pid in posts) {
|
||||
if (!posts.hasOwnProperty(pid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const post = posts[pid];
|
||||
if (!UserStore.hasProfile(post.user_id)) {
|
||||
profilesToLoad[post.user_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = Object.keys(profilesToLoad);
|
||||
if (list.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncClient.getProfilesByIds(list);
|
||||
}
|
||||
|
||||
133
webapp/actions/status_actions.jsx
Обычный файл
133
webapp/actions/status_actions.jsx
Обычный файл
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
|
||||
import Client from 'client/web_client.jsx';
|
||||
|
||||
import {ActionTypes, Preferences, Constants} from 'utils/constants.jsx';
|
||||
|
||||
export function loadStatusesForChannel(channelId = ChannelStore.getCurrentId()) {
|
||||
const postList = PostStore.getVisiblePosts(channelId);
|
||||
if (!postList || !postList.posts) {
|
||||
return;
|
||||
}
|
||||
|
||||
const statusesToLoad = {};
|
||||
for (const pid in postList.posts) {
|
||||
if (!postList.posts.hasOwnProperty(pid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const post = postList.posts[pid];
|
||||
statusesToLoad[post.user_id] = true;
|
||||
}
|
||||
|
||||
loadStatusesByIds(Object.keys(statusesToLoad));
|
||||
}
|
||||
|
||||
export function loadStatusesForDMSidebar() {
|
||||
const dmPrefs = PreferenceStore.getCategory(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
|
||||
const statusesToLoad = [];
|
||||
|
||||
for (const [key, value] of dmPrefs) {
|
||||
if (value === 'true') {
|
||||
statusesToLoad.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
loadStatusesByIds(statusesToLoad);
|
||||
}
|
||||
|
||||
export function loadStatusesForChannelAndSidebar() {
|
||||
const statusesToLoad = {};
|
||||
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
const postList = PostStore.getVisiblePosts(channelId);
|
||||
if (postList && postList.posts) {
|
||||
for (const pid in postList.posts) {
|
||||
if (!postList.posts.hasOwnProperty(pid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const post = postList.posts[pid];
|
||||
statusesToLoad[post.user_id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const dmPrefs = PreferenceStore.getCategory(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
|
||||
|
||||
for (const [key, value] of dmPrefs) {
|
||||
if (value === 'true') {
|
||||
statusesToLoad[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
loadStatusesByIds(Object.keys(statusesToLoad));
|
||||
}
|
||||
|
||||
export function loadStatusesForProfilesList(users) {
|
||||
if (users == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const statusesToLoad = [];
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
statusesToLoad.push(users[i].id);
|
||||
}
|
||||
|
||||
loadStatusesByIds(statusesToLoad);
|
||||
}
|
||||
|
||||
export function loadStatusesForProfilesMap(users) {
|
||||
if (users == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const statusesToLoad = [];
|
||||
for (const userId in users) {
|
||||
if (!users.hasOwnProperty(userId)) {
|
||||
return;
|
||||
}
|
||||
statusesToLoad.push(userId);
|
||||
}
|
||||
|
||||
loadStatusesByIds(statusesToLoad);
|
||||
}
|
||||
|
||||
export function loadStatusesByIds(userIds) {
|
||||
if (userIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Client.getStatusesByIds(
|
||||
userIds,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_STATUSES,
|
||||
statuses: data
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let intervalId = '';
|
||||
|
||||
export function startPeriodicStatusUpdates() {
|
||||
clearInterval(intervalId);
|
||||
|
||||
intervalId = setInterval(
|
||||
() => {
|
||||
loadStatusesForChannelAndSidebar();
|
||||
},
|
||||
Constants.STATUS_INTERVAL
|
||||
);
|
||||
}
|
||||
|
||||
export function stopPeriodicStatusUpdates() {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
@@ -19,8 +20,6 @@ export function checkIfTeamExists(teamName, onSuccess, onError) {
|
||||
export function createTeam(team, onSuccess, onError) {
|
||||
Client.createTeam(team,
|
||||
(rteam) => {
|
||||
AsyncClient.getDirectProfiles();
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.CREATED_TEAM,
|
||||
team: rteam,
|
||||
@@ -36,3 +35,25 @@ export function createTeam(team, onSuccess, onError) {
|
||||
onError
|
||||
);
|
||||
}
|
||||
|
||||
export function removeUserFromTeam(teamId, userId, success, error) {
|
||||
Client.removeUserFromTeam(
|
||||
teamId,
|
||||
userId,
|
||||
() => {
|
||||
TeamStore.removeMemberInTeam(teamId, userId);
|
||||
AsyncClient.getUser(userId);
|
||||
|
||||
if (success) {
|
||||
success();
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'removeUserFromTeam');
|
||||
|
||||
if (error) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
|
||||
import {loadStatusesForProfilesList, loadStatusesForProfilesMap} from 'actions/status_actions.jsx';
|
||||
|
||||
import {getDirectChannelName} from 'utils/utils.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
|
||||
import {ActionTypes, Preferences} from 'utils/constants.jsx';
|
||||
|
||||
@@ -29,9 +34,179 @@ export function switchFromLdapToEmail(email, password, ldapPassword, onSuccess,
|
||||
);
|
||||
}
|
||||
|
||||
export function getMoreDmList() {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfilesForDirectMessageList();
|
||||
export function loadProfilesAndTeamMembers(offset, limit, teamId = TeamStore.getCurrentId(), success, error) {
|
||||
Client.getProfilesInTeam(
|
||||
teamId,
|
||||
offset,
|
||||
limit,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES_IN_TEAM,
|
||||
profiles: data,
|
||||
team_id: teamId,
|
||||
offset,
|
||||
count: Object.keys(data).length
|
||||
});
|
||||
|
||||
loadTeamMembersForProfilesMap(data, teamId, success, error);
|
||||
loadStatusesForProfilesMap(data);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getProfilesInTeam');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function loadTeamMembersForProfilesMap(profiles, teamId = TeamStore.getCurrentId(), success, error) {
|
||||
const membersToLoad = {};
|
||||
for (const pid in profiles) {
|
||||
if (!profiles.hasOwnProperty(pid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TeamStore.hasActiveMemberInTeam(teamId, pid)) {
|
||||
membersToLoad[pid] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = Object.keys(membersToLoad);
|
||||
if (list.length === 0) {
|
||||
if (success) {
|
||||
success({});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
loadTeamMembersForProfiles(list, teamId, success, error);
|
||||
}
|
||||
|
||||
export function loadTeamMembersForProfilesList(profiles, teamId = TeamStore.getCurrentId(), success, error) {
|
||||
const membersToLoad = {};
|
||||
for (let i = 0; i < profiles.length; i++) {
|
||||
const pid = profiles[i].id;
|
||||
|
||||
if (!TeamStore.hasActiveMemberInTeam(teamId, pid)) {
|
||||
membersToLoad[pid] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const list = Object.keys(membersToLoad);
|
||||
if (list.length === 0) {
|
||||
if (success) {
|
||||
success({});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
loadTeamMembersForProfiles(list, teamId, success, error);
|
||||
}
|
||||
|
||||
function loadTeamMembersForProfiles(userIds, teamId, success, error) {
|
||||
Client.getTeamMembersByIds(
|
||||
teamId,
|
||||
userIds,
|
||||
(data) => {
|
||||
const memberMap = {};
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
memberMap[data[i].user_id] = data[i];
|
||||
}
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_MEMBERS_IN_TEAM,
|
||||
team_id: teamId,
|
||||
team_members: memberMap
|
||||
});
|
||||
|
||||
if (success) {
|
||||
success(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getTeamMembersByIds');
|
||||
|
||||
if (error) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function populateDMChannelsWithProfiles(userIds) {
|
||||
const currentUserId = UserStore.getCurrentId();
|
||||
|
||||
for (let i = 0; i < userIds.length; i++) {
|
||||
const channelName = getDirectChannelName(currentUserId, userIds[i]);
|
||||
const channel = ChannelStore.getByName(channelName);
|
||||
if (channel) {
|
||||
UserStore.saveUserIdInChannel(channel.id, userIds[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function loadProfilesAndTeamMembersForDMSidebar() {
|
||||
const dmPrefs = PreferenceStore.getCategory(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
|
||||
const teamId = TeamStore.getCurrentId();
|
||||
const profilesToLoad = [];
|
||||
const membersToLoad = [];
|
||||
|
||||
for (const [key, value] of dmPrefs) {
|
||||
if (value === 'true') {
|
||||
if (!UserStore.hasProfile(key)) {
|
||||
profilesToLoad.push(key);
|
||||
}
|
||||
membersToLoad.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (profilesToLoad.length > 0) {
|
||||
Client.getProfilesByIds(
|
||||
profilesToLoad,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES,
|
||||
profiles: data
|
||||
});
|
||||
|
||||
// Use membersToLoad so we get all the DM profiles even if they were already loaded
|
||||
populateDMChannelsWithProfiles(membersToLoad);
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getProfilesByIds');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
populateDMChannelsWithProfiles(membersToLoad);
|
||||
}
|
||||
|
||||
if (membersToLoad.length > 0) {
|
||||
Client.getTeamMembersByIds(
|
||||
teamId,
|
||||
membersToLoad,
|
||||
(data) => {
|
||||
const memberMap = {};
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
memberMap[data[i].user_id] = data[i];
|
||||
}
|
||||
|
||||
const nonMembersMap = {};
|
||||
for (let i = 0; i < membersToLoad.length; i++) {
|
||||
if (!memberMap[membersToLoad[i]]) {
|
||||
nonMembersMap[membersToLoad[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_MEMBERS_IN_TEAM,
|
||||
team_id: teamId,
|
||||
team_members: memberMap,
|
||||
non_team_members: nonMembersMap
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getTeamMembersByIds');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function saveTheme(teamId, theme, onSuccess, onError) {
|
||||
@@ -82,3 +257,62 @@ function onThemeSaved(teamId, theme, onSuccess) {
|
||||
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
export function searchUsers(term, teamId = TeamStore.getCurrentId(), options = {}, success, error) {
|
||||
Client.searchUsers(
|
||||
term,
|
||||
teamId,
|
||||
options,
|
||||
(data) => {
|
||||
loadStatusesForProfilesList(data);
|
||||
|
||||
if (success) {
|
||||
success(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'searchUsers');
|
||||
|
||||
if (error) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function autocompleteUsersInChannel(username, channelId, success, error) {
|
||||
Client.autocompleteUsersInChannel(
|
||||
username,
|
||||
channelId,
|
||||
(data) => {
|
||||
if (success) {
|
||||
success(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'autocompleteUsersInChannel');
|
||||
|
||||
if (error) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function autocompleteUsersInTeam(username, success, error) {
|
||||
Client.autocompleteUsersInTeam(
|
||||
username,
|
||||
(data) => {
|
||||
if (success) {
|
||||
success(data);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'autocompleteUsersInTeam');
|
||||
|
||||
if (error) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
@@ -20,10 +18,11 @@ import * as Utils from 'utils/utils.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import * as UserActions from 'actions/user_actions.jsx';
|
||||
import {handleNewPost} from 'actions/post_actions.jsx';
|
||||
import {handleNewPost, loadPosts} from 'actions/post_actions.jsx';
|
||||
import {loadProfilesAndTeamMembersForDMSidebar} from 'actions/user_actions.jsx';
|
||||
import * as StatusActions from 'actions/status_actions.jsx';
|
||||
|
||||
import {Constants, SocketEvents, ActionTypes} from 'utils/constants.jsx';
|
||||
import {Constants, SocketEvents, UserStatuses} from 'utils/constants.jsx';
|
||||
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
|
||||
@@ -53,6 +52,7 @@ export function initialize() {
|
||||
connUrl += Client.getUsersRoute() + '/websocket';
|
||||
|
||||
WebSocketClient.setEventCallback(handleEvent);
|
||||
WebSocketClient.setFirstConnectCallback(handleFirstConnect);
|
||||
WebSocketClient.setReconnectCallback(handleReconnect);
|
||||
WebSocketClient.setCloseCallback(handleClose);
|
||||
WebSocketClient.initialize(connUrl);
|
||||
@@ -64,22 +64,19 @@ export function close() {
|
||||
}
|
||||
|
||||
export function getStatuses() {
|
||||
WebSocketClient.getStatuses(
|
||||
(resp) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_STATUSES,
|
||||
statuses: resp.data
|
||||
});
|
||||
}
|
||||
);
|
||||
StatusActions.loadStatusesForChannelAndSidebar();
|
||||
}
|
||||
|
||||
function handleFirstConnect() {
|
||||
getStatuses();
|
||||
ErrorStore.clearLastError();
|
||||
ErrorStore.emitChange();
|
||||
}
|
||||
|
||||
function handleReconnect() {
|
||||
if (Client.teamId) {
|
||||
AsyncClient.getChannels();
|
||||
AsyncClient.getPosts(ChannelStore.getCurrentId());
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
loadPosts(ChannelStore.getCurrentId());
|
||||
}
|
||||
|
||||
getStatuses();
|
||||
@@ -112,7 +109,7 @@ function handleEvent(msg) {
|
||||
break;
|
||||
|
||||
case SocketEvents.NEW_USER:
|
||||
handleNewUserEvent();
|
||||
handleNewUserEvent(msg);
|
||||
break;
|
||||
|
||||
case SocketEvents.LEAVE_TEAM:
|
||||
@@ -170,6 +167,10 @@ function handleEvent(msg) {
|
||||
function handleNewPostEvent(msg) {
|
||||
const post = JSON.parse(msg.data.post);
|
||||
handleNewPost(post, msg);
|
||||
|
||||
if (UserStore.getStatus(post.user_id) !== UserStatuses.ONLINE) {
|
||||
StatusActions.loadStatusesByIds([post.user_id]);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePostEditEvent(msg) {
|
||||
@@ -196,36 +197,33 @@ function handlePostDeleteEvent(msg) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleNewUserEvent() {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
AsyncClient.getDirectProfiles();
|
||||
AsyncClient.getChannelExtraInfo();
|
||||
function handleNewUserEvent(msg) {
|
||||
AsyncClient.getUser(msg.user_id);
|
||||
AsyncClient.getChannelStats();
|
||||
loadProfilesAndTeamMembersForDMSidebar();
|
||||
}
|
||||
|
||||
function handleLeaveTeamEvent(msg) {
|
||||
if (UserStore.getCurrentId() === msg.data.user_id) {
|
||||
TeamStore.removeTeamMember(msg.broadcast.team_id);
|
||||
TeamStore.removeMyTeamMember(msg.broadcast.team_id);
|
||||
|
||||
// if the are on the team begin removed redirect them to the root
|
||||
// if they are on the team being removed redirect them to the root
|
||||
if (TeamStore.getCurrentId() === msg.broadcast.team_id) {
|
||||
TeamStore.setCurrentId('');
|
||||
Client.setTeamId('');
|
||||
browserHistory.push('/');
|
||||
}
|
||||
} else if (TeamStore.getCurrentId() === msg.broadcast.team_id) {
|
||||
UserActions.getMoreDmList();
|
||||
}
|
||||
}
|
||||
|
||||
function handleDirectAddedEvent(msg) {
|
||||
AsyncClient.getChannel(msg.broadcast.channel_id);
|
||||
AsyncClient.getDirectProfiles();
|
||||
loadProfilesAndTeamMembersForDMSidebar();
|
||||
}
|
||||
|
||||
function handleUserAddedEvent(msg) {
|
||||
if (ChannelStore.getCurrentId() === msg.broadcast.channel_id) {
|
||||
AsyncClient.getChannelExtraInfo();
|
||||
AsyncClient.getChannelStats();
|
||||
}
|
||||
|
||||
if (TeamStore.getCurrentId() === msg.data.team_id && UserStore.getCurrentId() === msg.data.user_id) {
|
||||
@@ -248,7 +246,7 @@ function handleUserRemovedEvent(msg) {
|
||||
$('#removed_from_channel').modal('show');
|
||||
}
|
||||
} else if (ChannelStore.getCurrentId() === msg.broadcast.channel_id) {
|
||||
AsyncClient.getChannelExtraInfo();
|
||||
AsyncClient.getChannelStats();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +285,10 @@ function handlePreferenceChangedEvent(msg) {
|
||||
|
||||
function handleUserTypingEvent(msg) {
|
||||
GlobalActions.emitRemoteUserTypingEvent(msg.broadcast.channel_id, msg.data.user_id, msg.data.parent_id);
|
||||
|
||||
if (UserStore.getStatus(msg.data.user_id) !== UserStatuses.ONLINE) {
|
||||
StatusActions.loadStatusesByIds([msg.data.user_id]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleStatusChangedEvent(msg) {
|
||||
@@ -301,4 +303,4 @@ function handleHelloEvent(msg) {
|
||||
function handleWebrtc(msg) {
|
||||
const data = msg.data;
|
||||
return WebrtcActions.handle(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,11 +73,7 @@ export default class Client {
|
||||
return `${this.url}${this.urlVersion}/teams`;
|
||||
}
|
||||
|
||||
getTeamNeededRoute() {
|
||||
return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}`;
|
||||
}
|
||||
|
||||
getTeamNeededManualRoute(teamId) {
|
||||
getTeamNeededRoute(teamId = this.getTeamId()) {
|
||||
return `${this.url}${this.urlVersion}/teams/${teamId}`;
|
||||
}
|
||||
|
||||
@@ -565,15 +561,43 @@ export default class Client {
|
||||
end(this.handleResponse.bind(this, 'getMyTeam', success, error));
|
||||
}
|
||||
|
||||
getTeamMembers(teamId, success, error) {
|
||||
getTeamMembers(teamId, offset, limit, success, error) {
|
||||
request.
|
||||
get(`${this.getTeamsRoute()}/members/${teamId}`).
|
||||
get(`${this.getTeamNeededRoute(teamId)}/members/${offset}/${limit}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getTeamMembers', success, error));
|
||||
}
|
||||
|
||||
getTeamMember(teamId, userId, success, error) {
|
||||
request.
|
||||
get(`${this.getTeamNeededRoute(teamId)}/members/${userId}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getTeamMember', success, error));
|
||||
}
|
||||
|
||||
getTeamMembersByIds(teamId, userIds, success, error) {
|
||||
request.
|
||||
post(`${this.getTeamNeededRoute(teamId)}/members/ids`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
send(userIds).
|
||||
end(this.handleResponse.bind(this, 'getTeamMembersByIds', success, error));
|
||||
}
|
||||
|
||||
getTeamStats(teamId, success, error) {
|
||||
request.
|
||||
get(`${this.getTeamNeededRoute(teamId)}/stats`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getTeamStats', success, error));
|
||||
}
|
||||
|
||||
inviteMembers(data, success, error) {
|
||||
request.
|
||||
post(`${this.getTeamNeededRoute()}/invite_members`).
|
||||
@@ -740,7 +764,7 @@ export default class Client {
|
||||
};
|
||||
|
||||
request.
|
||||
post(`${this.getTeamNeededManualRoute(teamId)}/update_member_roles`).
|
||||
post(`${this.getTeamNeededRoute(teamId)}/update_member_roles`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
@@ -1003,40 +1027,78 @@ export default class Client {
|
||||
end(this.handleResponse.bind(this, 'getRecentlyActiveUsers', success, error));
|
||||
}
|
||||
|
||||
getDirectProfiles(success, error) {
|
||||
getProfiles(offset, limit, success, error) {
|
||||
request.
|
||||
get(`${this.getUsersRoute()}/direct_profiles`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getDirectProfiles', success, error));
|
||||
}
|
||||
|
||||
getProfiles(success, error) {
|
||||
request.
|
||||
get(`${this.getUsersRoute()}/profiles/${this.getTeamId()}`).
|
||||
get(`${this.getUsersRoute()}/${offset}/${limit}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getProfiles', success, error));
|
||||
}
|
||||
|
||||
getProfilesForTeam(teamId, success, error) {
|
||||
getProfilesInTeam(teamId, offset, limit, success, error) {
|
||||
request.
|
||||
get(`${this.getUsersRoute()}/profiles/${teamId}`).
|
||||
get(`${this.getTeamNeededRoute(teamId)}/users/${offset}/${limit}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getProfilesForTeam', success, error));
|
||||
end(this.handleResponse.bind(this, 'getProfilesInTeam', success, error));
|
||||
}
|
||||
|
||||
getProfilesForDirectMessageList(success, error) {
|
||||
getProfilesInChannel(channelId, offset, limit, success, error) {
|
||||
request.
|
||||
get(`${this.getUsersRoute()}/profiles_for_dm_list/${this.getTeamId()}`).
|
||||
get(`${this.getChannelNeededRoute(channelId)}/users/${offset}/${limit}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getProfilesForDirectMessageList', success, error));
|
||||
end(this.handleResponse.bind(this, 'getProfilesInChannel', success, error));
|
||||
}
|
||||
|
||||
getProfilesNotInChannel(channelId, offset, limit, success, error) {
|
||||
request.
|
||||
get(`${this.getChannelNeededRoute(channelId)}/users/not_in_channel/${offset}/${limit}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getProfilesNotInChannel', success, error));
|
||||
}
|
||||
|
||||
getProfilesByIds(userIds, success, error) {
|
||||
request.
|
||||
post(`${this.getUsersRoute()}/ids`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
send(userIds).
|
||||
end(this.handleResponse.bind(this, 'getProfilesByIds', success, error));
|
||||
}
|
||||
|
||||
searchUsers(term, teamId, options, success, error) {
|
||||
request.
|
||||
post(`${this.getUsersRoute()}/search`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
send({term, team_id: teamId, ...options}).
|
||||
end(this.handleResponse.bind(this, 'searchUsers', success, error));
|
||||
}
|
||||
|
||||
autocompleteUsersInChannel(term, channelId, success, error) {
|
||||
request.
|
||||
get(`${this.getChannelNeededRoute(channelId)}/users/autocomplete?term=${encodeURIComponent(term)}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'autocompleteUsers', success, error));
|
||||
}
|
||||
|
||||
autocompleteUsersInTeam(term, success, error) {
|
||||
request.
|
||||
get(`${this.getTeamNeededRoute()}/users/autocomplete?term=${encodeURIComponent(term)}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'autocompleteUsers', success, error));
|
||||
}
|
||||
|
||||
getStatuses(success, error) {
|
||||
@@ -1048,6 +1110,16 @@ export default class Client {
|
||||
end(this.handleResponse.bind(this, 'getStatuses', success, error));
|
||||
}
|
||||
|
||||
getStatusesByIds(userIds, success, error) {
|
||||
request.
|
||||
post(`${this.getUsersRoute()}/status/ids`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
send(userIds).
|
||||
end(this.handleResponse.bind(this, 'getStatuses', success, error));
|
||||
}
|
||||
|
||||
setActiveChannel(id, success, error) {
|
||||
request.
|
||||
post(`${this.getUsersRoute()}/status/set_active_channel`).
|
||||
@@ -1285,18 +1357,22 @@ export default class Client {
|
||||
end(this.handleResponse.bind(this, 'getChannelCounts', success, error));
|
||||
}
|
||||
|
||||
getChannelExtraInfo(channelId, memberLimit, success, error) {
|
||||
var url = `${this.getChannelNeededRoute(channelId)}/extra_info`;
|
||||
if (memberLimit) {
|
||||
url += '/' + memberLimit;
|
||||
}
|
||||
|
||||
getChannelStats(channelId, success, error) {
|
||||
request.
|
||||
get(url).
|
||||
get(`${this.getChannelNeededRoute(channelId)}/stats`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getChannelExtraInfo', success, error));
|
||||
end(this.handleResponse.bind(this, 'getChannelStats', success, error));
|
||||
}
|
||||
|
||||
getChannelMember(channelId, userId, success, error) {
|
||||
request.
|
||||
get(`${this.getChannelNeededRoute(channelId)}/members/${userId}`).
|
||||
set(this.defaultHeaders).
|
||||
type('application/json').
|
||||
accept('application/json').
|
||||
end(this.handleResponse.bind(this, 'getChannelMember', success, error));
|
||||
}
|
||||
|
||||
addChannelMember(channelId, userId, success, error) {
|
||||
|
||||
@@ -12,6 +12,7 @@ export default class WebSocketClient {
|
||||
this.connectFailCount = 0;
|
||||
this.eventCallback = null;
|
||||
this.responseCallbacks = {};
|
||||
this.firstConnectCallback = null;
|
||||
this.reconnectCallback = null;
|
||||
this.errorCallback = null;
|
||||
this.closeCallback = null;
|
||||
@@ -29,12 +30,13 @@ export default class WebSocketClient {
|
||||
this.conn = new WebSocket(connectionUrl);
|
||||
|
||||
this.conn.onopen = () => {
|
||||
if (this.reconnectCallback) {
|
||||
this.reconnectCallback();
|
||||
}
|
||||
|
||||
if (this.connectFailCount > 0) {
|
||||
console.log('websocket re-established connection'); //eslint-disable-line no-console
|
||||
if (this.reconnectCallback) {
|
||||
this.reconnectCallback();
|
||||
}
|
||||
} else if (this.firstConnectCallback) {
|
||||
this.firstConnectCallback();
|
||||
}
|
||||
|
||||
this.connectFailCount = 0;
|
||||
@@ -104,6 +106,10 @@ export default class WebSocketClient {
|
||||
this.eventCallback = callback;
|
||||
}
|
||||
|
||||
setFirstConnectCallback(callback) {
|
||||
this.firstConnectCallback = callback;
|
||||
}
|
||||
|
||||
setReconnectCallback(callback) {
|
||||
this.reconnectCallback = callback;
|
||||
}
|
||||
@@ -157,4 +163,10 @@ export default class WebSocketClient {
|
||||
getStatuses(callback) {
|
||||
this.sendMessage('get_statuses', null, callback);
|
||||
}
|
||||
|
||||
getStatusesByIds(userIds, callback) {
|
||||
const data = {};
|
||||
data.user_ids = userIds;
|
||||
this.sendMessage('get_statuses_by_ids', data, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class AdminNavbarDropdown extends React.Component {
|
||||
|
||||
this.state = {
|
||||
teams: TeamStore.getAll(),
|
||||
teamMembers: TeamStore.getTeamMembers()
|
||||
teamMembers: TeamStore.getMyTeamMembers()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export default class AdminNavbarDropdown extends React.Component {
|
||||
onTeamChange() {
|
||||
this.setState({
|
||||
teams: TeamStore.getAll(),
|
||||
teamMembers: TeamStore.getTeamMembers()
|
||||
teamMembers: TeamStore.getMyTeamMembers()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ import UserStore from 'stores/user_store.jsx';
|
||||
import ConfirmModal from '../confirm_modal.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default class UserItem extends React.Component {
|
||||
export default class AdminTeamMembersDropdown extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -50,7 +50,7 @@ export default class UserItem extends React.Component {
|
||||
}
|
||||
);
|
||||
Client.updateTeamMemberRoles(
|
||||
this.props.team.id,
|
||||
this.props.teamMember.team_id,
|
||||
this.props.user.id,
|
||||
'team_user',
|
||||
() => {
|
||||
@@ -74,7 +74,7 @@ export default class UserItem extends React.Component {
|
||||
|
||||
handleRemoveFromTeam() {
|
||||
Client.removeUserFromTeam(
|
||||
this.props.team.id,
|
||||
this.props.teamMember.team_id,
|
||||
this.props.user.id,
|
||||
() => {
|
||||
this.props.refreshProfiles();
|
||||
@@ -111,7 +111,7 @@ export default class UserItem extends React.Component {
|
||||
|
||||
doMakeTeamAdmin() {
|
||||
Client.updateTeamMemberRoles(
|
||||
this.props.team.id,
|
||||
this.props.teamMember.team_id,
|
||||
this.props.user.id,
|
||||
'team_user team_admin',
|
||||
() => {
|
||||
@@ -241,7 +241,6 @@ export default class UserItem extends React.Component {
|
||||
}
|
||||
|
||||
const me = UserStore.getCurrentUser();
|
||||
const email = user.email;
|
||||
let showMakeMember = Utils.isAdmin(teamMember.roles) || Utils.isSystemAdmin(user.roles);
|
||||
let showMakeAdmin = !Utils.isAdmin(teamMember.roles) && !Utils.isSystemAdmin(user.roles);
|
||||
let showMakeSystemAdmin = !Utils.isSystemAdmin(user.roles);
|
||||
@@ -406,39 +405,8 @@ export default class UserItem extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let mfaActiveText;
|
||||
if (mfaEnabled) {
|
||||
if (user.mfa_active) {
|
||||
mfaActiveText = (
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.mfaYes'
|
||||
defaultMessage=', <strong>MFA</strong>: Yes'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
mfaActiveText = (
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.mfaNo'
|
||||
defaultMessage=', <strong>MFA</strong>: No'
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let authServiceText;
|
||||
let passwordReset;
|
||||
if (user.auth_service) {
|
||||
const service = (user.auth_service === Constants.LDAP_SERVICE || user.auth_service === Constants.SAML_SERVICE) ? user.auth_service.toUpperCase() : Utils.toTitleCase(user.auth_service);
|
||||
authServiceText = (
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.authServiceNotEmail'
|
||||
defaultMessage=', <strong>Sign-in Method:</strong> {service}'
|
||||
values={{
|
||||
service
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
passwordReset = (
|
||||
<li role='presentation'>
|
||||
<a
|
||||
@@ -454,13 +422,6 @@ export default class UserItem extends React.Component {
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
authServiceText = (
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.authServiceEmail'
|
||||
defaultMessage=', <strong>Sign-in Method:</strong> Email'
|
||||
/>
|
||||
);
|
||||
|
||||
passwordReset = (
|
||||
<li role='presentation'>
|
||||
<a
|
||||
@@ -531,63 +492,38 @@ export default class UserItem extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='more-modal__row'>
|
||||
<img
|
||||
className='more-modal__image pull-left'
|
||||
src={`${Client.getUsersRoute()}/${user.id}/image?time=${user.update_at}`}
|
||||
height='36'
|
||||
width='36'
|
||||
/>
|
||||
<div className='more-modal__details'>
|
||||
<div className='more-modal__name'>{displayedName}</div>
|
||||
<div className='more-modal__description'>
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.emailTitle'
|
||||
defaultMessage='<strong>Email:</strong> {email}'
|
||||
values={{
|
||||
email
|
||||
}}
|
||||
/>
|
||||
{authServiceText}
|
||||
{mfaActiveText}
|
||||
</div>
|
||||
{serverError}
|
||||
</div>
|
||||
<div className='more-modal__actions'>
|
||||
<div className='dropdown member-drop'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<span>{currentRoles} </span>
|
||||
<span className='caret'/>
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu member-menu'
|
||||
role='menu'
|
||||
>
|
||||
{removeFromTeam}
|
||||
{makeAdmin}
|
||||
{makeMember}
|
||||
{makeActive}
|
||||
{makeNotActive}
|
||||
{makeSystemAdmin}
|
||||
{mfaReset}
|
||||
{passwordReset}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className='dropdown member-drop'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<span>{currentRoles} </span>
|
||||
<span className='caret'/>
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu member-menu'
|
||||
role='menu'
|
||||
>
|
||||
{removeFromTeam}
|
||||
{makeAdmin}
|
||||
{makeMember}
|
||||
{makeActive}
|
||||
{makeNotActive}
|
||||
{makeSystemAdmin}
|
||||
{mfaReset}
|
||||
{passwordReset}
|
||||
</ul>
|
||||
{makeDemoteModal}
|
||||
{serverError}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
UserItem.propTypes = {
|
||||
team: React.PropTypes.object.isRequired,
|
||||
AdminTeamMembersDropdown.propTypes = {
|
||||
user: React.PropTypes.object.isRequired,
|
||||
teamMember: React.PropTypes.object.isRequired,
|
||||
refreshProfiles: React.PropTypes.func.isRequired,
|
||||
@@ -1,16 +1,25 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import AdminStore from 'stores/admin_store.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import FormError from 'components/form_error.jsx';
|
||||
import LoadingScreen from '../loading_screen.jsx';
|
||||
import UserItem from './user_item.jsx';
|
||||
import SearchableUserList from 'components/searchable_user_list.jsx';
|
||||
import AdminTeamMembersDropdown from './admin_team_members_dropdown.jsx';
|
||||
import ResetPasswordModal from './reset_password_modal.jsx';
|
||||
import FormError from 'components/form_error.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import AdminStore from 'stores/admin_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {searchUsers, loadProfilesAndTeamMembers, loadTeamMembersForProfilesList} from 'actions/user_actions.jsx';
|
||||
import {getTeamStats} from 'utils/async_client.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
|
||||
|
||||
const USERS_PER_PAGE = 50;
|
||||
|
||||
export default class UserList extends React.Component {
|
||||
static get propTypes() {
|
||||
@@ -23,34 +32,49 @@ export default class UserList extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.onAllTeamsChange = this.onAllTeamsChange.bind(this);
|
||||
this.onStatsChange = this.onStatsChange.bind(this);
|
||||
this.onUsersChange = this.onUsersChange.bind(this);
|
||||
this.onTeamChange = this.onTeamChange.bind(this);
|
||||
|
||||
this.getTeamProfiles = this.getTeamProfiles.bind(this);
|
||||
this.getCurrentTeamProfiles = this.getCurrentTeamProfiles.bind(this);
|
||||
this.doPasswordReset = this.doPasswordReset.bind(this);
|
||||
this.doPasswordResetDismiss = this.doPasswordResetDismiss.bind(this);
|
||||
this.doPasswordResetSubmit = this.doPasswordResetSubmit.bind(this);
|
||||
this.getTeamMemberForUser = this.getTeamMemberForUser.bind(this);
|
||||
this.nextPage = this.nextPage.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
this.loadComplete = this.loadComplete.bind(this);
|
||||
|
||||
const stats = TeamStore.getStats(this.props.params.team);
|
||||
|
||||
this.state = {
|
||||
team: AdminStore.getTeam(this.props.params.team),
|
||||
users: null,
|
||||
teamMembers: null,
|
||||
users: [],
|
||||
teamMembers: TeamStore.getMembersInTeam(this.props.params.team),
|
||||
total: stats.member_count,
|
||||
serverError: null,
|
||||
showPasswordModal: false,
|
||||
loading: true,
|
||||
user: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getCurrentTeamProfiles();
|
||||
|
||||
AdminStore.addAllTeamsChangeListener(this.onAllTeamsChange);
|
||||
UserStore.addInTeamChangeListener(this.onUsersChange);
|
||||
TeamStore.addChangeListener(this.onTeamChange);
|
||||
TeamStore.addStatsChangeListener(this.onStatsChange);
|
||||
|
||||
loadProfilesAndTeamMembers(0, Constants.PROFILE_CHUNK_SIZE, this.props.params.team, this.loadComplete);
|
||||
getTeamStats(this.props.params.team);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.params.team !== this.props.params.team) {
|
||||
const stats = TeamStore.getStats(nextProps.params.team);
|
||||
this.setState({
|
||||
team: AdminStore.getTeam(nextProps.params.team)
|
||||
team: AdminStore.getTeam(nextProps.params.team),
|
||||
users: [],
|
||||
teamMembers: TeamStore.getMembersInTeam(nextProps.params.team),
|
||||
total: stats.member_count
|
||||
});
|
||||
|
||||
this.getTeamProfiles(nextProps.params.team);
|
||||
@@ -59,6 +83,13 @@ export default class UserList extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
AdminStore.removeAllTeamsChangeListener(this.onAllTeamsChange);
|
||||
UserStore.removeInTeamChangeListener(this.onUsersChange);
|
||||
TeamStore.removeChangeListener(this.onTeamChange);
|
||||
TeamStore.removeStatsChangeListener(this.onStatsChange);
|
||||
}
|
||||
|
||||
loadComplete() {
|
||||
this.setState({loading: false});
|
||||
}
|
||||
|
||||
onAllTeamsChange() {
|
||||
@@ -67,59 +98,21 @@ export default class UserList extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentTeamProfiles() {
|
||||
this.getTeamProfiles(this.props.params.team);
|
||||
onStatsChange() {
|
||||
const stats = TeamStore.getStats(this.props.params.team);
|
||||
this.setState({total: stats.member_count});
|
||||
}
|
||||
|
||||
getTeamProfiles(teamId) {
|
||||
Client.getTeamMembers(
|
||||
teamId,
|
||||
(data) => {
|
||||
this.setState({
|
||||
teamMembers: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({
|
||||
teamMembers: null,
|
||||
serverError: err.message
|
||||
});
|
||||
}
|
||||
);
|
||||
onUsersChange() {
|
||||
this.setState({users: UserStore.getProfileListInTeam(this.props.params.team)});
|
||||
}
|
||||
|
||||
Client.getProfilesForTeam(
|
||||
teamId,
|
||||
(users) => {
|
||||
var memberList = [];
|
||||
for (var id in users) {
|
||||
if (users.hasOwnProperty(id)) {
|
||||
memberList.push(users[id]);
|
||||
}
|
||||
}
|
||||
onTeamChange() {
|
||||
this.setState({teamMembers: TeamStore.getMembersInTeam(this.props.params.team)});
|
||||
}
|
||||
|
||||
memberList.sort((a, b) => {
|
||||
if (a.username < b.username) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a.username > b.username) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.setState({
|
||||
users: memberList
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({
|
||||
users: null,
|
||||
serverError: err.message
|
||||
});
|
||||
}
|
||||
);
|
||||
nextPage(page) {
|
||||
loadProfilesAndTeamMembers((page + 1) * USERS_PER_PAGE, USERS_PER_PAGE, this.props.params.team);
|
||||
}
|
||||
|
||||
doPasswordReset(user) {
|
||||
@@ -144,20 +137,21 @@ export default class UserList extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getTeamMemberForUser(userId) {
|
||||
if (this.state.teamMembers) {
|
||||
for (const index in this.state.teamMembers) {
|
||||
if (this.state.teamMembers.hasOwnProperty(index)) {
|
||||
var teamMember = this.state.teamMembers[index];
|
||||
|
||||
if (teamMember.user_id === userId) {
|
||||
return teamMember;
|
||||
}
|
||||
}
|
||||
}
|
||||
search(term) {
|
||||
if (term === '') {
|
||||
this.setState({search: false, users: UserStore.getProfileListInTeam(this.props.params.team)});
|
||||
return;
|
||||
}
|
||||
|
||||
return null;
|
||||
searchUsers(
|
||||
term,
|
||||
this.props.params.team,
|
||||
{},
|
||||
(users) => {
|
||||
this.setState({loading: true, search: true, users});
|
||||
loadTeamMembersForProfilesList(users, this.props.params.team, this.loadComplete);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -165,41 +159,71 @@ export default class UserList extends React.Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.state.users == null || this.state.teamMembers == null) {
|
||||
return (
|
||||
<div className='wrapper--fixed'>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id='admin.userList.title'
|
||||
defaultMessage='Users for {team}'
|
||||
values={{
|
||||
team: this.state.team.name
|
||||
}}
|
||||
/>
|
||||
</h3>
|
||||
<FormError error={this.state.serverError}/>
|
||||
<LoadingScreen/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const teamMembers = this.state.teamMembers;
|
||||
const users = this.state.users;
|
||||
const actionUserProps = {};
|
||||
const extraInfo = {};
|
||||
const mfaEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MFA === 'true' && global.window.mm_config.EnableMultifactorAuthentication === 'true';
|
||||
|
||||
var memberList = this.state.users.map((user) => {
|
||||
var teamMember = this.getTeamMemberForUser(user.id);
|
||||
let usersToDisplay;
|
||||
if (this.state.loading) {
|
||||
usersToDisplay = null;
|
||||
} else {
|
||||
usersToDisplay = [];
|
||||
|
||||
if (!teamMember || teamMember.delete_at > 0) {
|
||||
return null;
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
const user = users[i];
|
||||
|
||||
if (teamMembers[user.id]) {
|
||||
usersToDisplay.push(user);
|
||||
actionUserProps[user.id] = {
|
||||
teamMember: teamMembers[user.id]
|
||||
};
|
||||
|
||||
const info = [];
|
||||
|
||||
if (user.auth_service) {
|
||||
const service = (user.auth_service === Constants.LDAP_SERVICE || user.auth_service === Constants.SAML_SERVICE) ? user.auth_service.toUpperCase() : Utils.toTitleCase(user.auth_service);
|
||||
info.push(
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.authServiceNotEmail'
|
||||
defaultMessage='<strong>Sign-in Method:</strong> {service}'
|
||||
values={{
|
||||
service
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
info.push(
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.authServiceEmail'
|
||||
defaultMessage='<strong>Sign-in Method:</strong> Email'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (mfaEnabled) {
|
||||
if (user.mfa_active) {
|
||||
info.push(
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.mfaYes'
|
||||
defaultMessage='<strong>MFA</strong>: Yes'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
info.push(
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.mfaNo'
|
||||
defaultMessage='<strong>MFA</strong>: No'
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extraInfo[user.id] = info;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<UserItem
|
||||
team={this.state.team}
|
||||
key={'user_' + user.id}
|
||||
user={user}
|
||||
teamMember={teamMember}
|
||||
refreshProfiles={this.getCurrentTeamProfiles}
|
||||
doPasswordReset={this.doPasswordReset}
|
||||
/>);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='wrapper--fixed'>
|
||||
@@ -209,7 +233,7 @@ export default class UserList extends React.Component {
|
||||
defaultMessage='Users for {team} ({count})'
|
||||
values={{
|
||||
team: this.state.team.name,
|
||||
count: this.state.users.length
|
||||
count: this.state.total
|
||||
}}
|
||||
/>
|
||||
</h3>
|
||||
@@ -219,7 +243,20 @@ export default class UserList extends React.Component {
|
||||
role='form'
|
||||
>
|
||||
<div className='more-modal__list member-list-holder'>
|
||||
{memberList}
|
||||
<SearchableUserList
|
||||
users={usersToDisplay}
|
||||
usersPerPage={USERS_PER_PAGE}
|
||||
total={this.state.total}
|
||||
extraInfo={extraInfo}
|
||||
nextPage={this.nextPage}
|
||||
search={this.search}
|
||||
actions={[AdminTeamMembersDropdown]}
|
||||
actionProps={{
|
||||
refreshProfiles: this.getCurrentTeamProfiles,
|
||||
doPasswordReset: this.doPasswordReset
|
||||
}}
|
||||
actionUserProps={actionUserProps}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<ResetPasswordModal
|
||||
|
||||
@@ -82,6 +82,7 @@ class SystemAnalytics extends React.Component {
|
||||
const stats = this.state.stats;
|
||||
|
||||
let advancedCounts;
|
||||
let advancedStats;
|
||||
let advancedGraphs;
|
||||
let banner;
|
||||
if (global.window.mm_license.IsLicensed === 'true') {
|
||||
@@ -130,6 +131,41 @@ class SystemAnalytics extends React.Component {
|
||||
</div>
|
||||
);
|
||||
|
||||
advancedStats = (
|
||||
<div className='row'>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalWebsockets'
|
||||
defaultMessage='Websocket Conns'
|
||||
/>
|
||||
}
|
||||
icon='fa-user'
|
||||
count={stats[StatTypes.TOTAL_WEBSOCKET_CONNECTIONS]}
|
||||
/>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalMasterDbConnections'
|
||||
defaultMessage='Master DB Conns'
|
||||
/>
|
||||
}
|
||||
icon='fa-terminal'
|
||||
count={stats[StatTypes.TOTAL_MASTER_DB_CONNECTIONS]}
|
||||
/>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalReadDbConnections'
|
||||
defaultMessage='Replica DB Conns'
|
||||
/>
|
||||
}
|
||||
icon='fa-terminal'
|
||||
count={stats[StatTypes.TOTAL_READ_DB_CONNECTIONS]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const channelTypeData = formatChannelDoughtnutData(stats[StatTypes.TOTAL_PUBLIC_CHANNELS], stats[StatTypes.TOTAL_PRIVATE_GROUPS], this.props.intl);
|
||||
const postTypeData = formatPostDoughtnutData(stats[StatTypes.TOTAL_FILE_POSTS], stats[StatTypes.TOTAL_HASHTAG_POSTS], stats[StatTypes.TOTAL_POSTS], this.props.intl);
|
||||
|
||||
@@ -246,6 +282,7 @@ class SystemAnalytics extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
{advancedCounts}
|
||||
{advancedStats}
|
||||
{advancedGraphs}
|
||||
<div className='row'>
|
||||
<LineChart
|
||||
|
||||
@@ -63,13 +63,15 @@ export default class ChannelHeader extends React.Component {
|
||||
}
|
||||
|
||||
getStateFromStores() {
|
||||
const extraInfo = ChannelStore.getExtraInfo(this.props.channelId);
|
||||
const stats = ChannelStore.getStats(this.props.channelId);
|
||||
|
||||
const users = UserStore.getProfileListInChannel(this.props.channelId);
|
||||
|
||||
return {
|
||||
channel: ChannelStore.get(this.props.channelId),
|
||||
memberChannel: ChannelStore.getMember(this.props.channelId),
|
||||
users: extraInfo.members,
|
||||
userCount: extraInfo.member_count,
|
||||
memberChannel: ChannelStore.getMyMember(this.props.channelId),
|
||||
users,
|
||||
userCount: stats.member_count,
|
||||
currentUser: UserStore.getCurrentUser(),
|
||||
enableFormatting: PreferenceStore.getBool(Preferences.CATEGORY_ADVANCED_SETTINGS, 'formatting', true),
|
||||
isBusy: WebrtcStore.isBusy()
|
||||
@@ -89,10 +91,10 @@ export default class ChannelHeader extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
ChannelStore.addChangeListener(this.onListenerChange);
|
||||
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
|
||||
ChannelStore.addStatsChangeListener(this.onListenerChange);
|
||||
SearchStore.addSearchChangeListener(this.onListenerChange);
|
||||
PreferenceStore.addChangeListener(this.onListenerChange);
|
||||
UserStore.addChangeListener(this.onListenerChange);
|
||||
UserStore.addInChannelChangeListener(this.onListenerChange);
|
||||
UserStore.addStatusesChangeListener(this.onListenerChange);
|
||||
WebrtcStore.addChangedListener(this.onListenerChange);
|
||||
WebrtcStore.addBusyListener(this.onBusy);
|
||||
@@ -102,10 +104,10 @@ export default class ChannelHeader extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeChangeListener(this.onListenerChange);
|
||||
ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
|
||||
ChannelStore.removeStatsChangeListener(this.onListenerChange);
|
||||
SearchStore.removeSearchChangeListener(this.onListenerChange);
|
||||
PreferenceStore.removeChangeListener(this.onListenerChange);
|
||||
UserStore.removeChangeListener(this.onListenerChange);
|
||||
UserStore.removeInChannelChangeListener(this.onListenerChange);
|
||||
UserStore.removeStatusesChangeListener(this.onListenerChange);
|
||||
WebrtcStore.removeChangedListener(this.onListenerChange);
|
||||
WebrtcStore.removeBusyListener(this.onBusy);
|
||||
@@ -117,10 +119,7 @@ export default class ChannelHeader extends React.Component {
|
||||
}
|
||||
|
||||
onListenerChange() {
|
||||
const newState = this.getStateFromStores();
|
||||
if (!Utils.areObjectsEqual(newState, this.state)) {
|
||||
this.setState(newState);
|
||||
}
|
||||
this.setState(this.getStateFromStores());
|
||||
}
|
||||
|
||||
handleLeave() {
|
||||
@@ -265,7 +264,6 @@ export default class ChannelHeader extends React.Component {
|
||||
</Popover>
|
||||
);
|
||||
let channelTitle = channel.display_name;
|
||||
const currentId = this.state.currentUser.id;
|
||||
const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
|
||||
const isSystemAdmin = UserStore.isSystemAdminForCurrentUser();
|
||||
const isDirect = (this.state.channel.type === 'D');
|
||||
@@ -273,13 +271,8 @@ export default class ChannelHeader extends React.Component {
|
||||
|
||||
if (isDirect) {
|
||||
const userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
||||
let contact;
|
||||
if (this.state.users.length > 1) {
|
||||
if (this.state.users[0].id === currentId) {
|
||||
contact = this.state.users[1];
|
||||
} else {
|
||||
contact = this.state.users[0];
|
||||
}
|
||||
const contact = this.state.users[0];
|
||||
if (contact) {
|
||||
channelTitle = Utils.displayUsername(contact.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import SpinnerButton from 'components/spinner_button.jsx';
|
||||
|
||||
import {addUserToChannel} from 'actions/channel_actions.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
export default class ChannelInviteButton extends React.Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
@@ -37,7 +36,7 @@ export default class ChannelInviteButton extends React.Component {
|
||||
addingUser: true
|
||||
});
|
||||
|
||||
Client.addChannelMember(
|
||||
addUserToChannel(
|
||||
this.props.channel.id,
|
||||
this.props.user.id,
|
||||
() => {
|
||||
@@ -46,7 +45,6 @@ export default class ChannelInviteButton extends React.Component {
|
||||
});
|
||||
|
||||
this.props.onInviteError(null);
|
||||
AsyncClient.getChannelExtraInfo();
|
||||
},
|
||||
(err) => {
|
||||
this.setState({
|
||||
|
||||
@@ -1,124 +1,85 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import $ from 'jquery';
|
||||
import ChannelInviteButton from './channel_invite_button.jsx';
|
||||
import FilteredUserList from './filtered_user_list.jsx';
|
||||
import SearchableUserList from './searchable_user_list.jsx';
|
||||
import LoadingScreen from './loading_screen.jsx';
|
||||
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
import {searchUsers} from 'actions/user_actions.jsx';
|
||||
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {Modal} from 'react-bootstrap';
|
||||
|
||||
import React from 'react';
|
||||
const USERS_PER_PAGE = 50;
|
||||
|
||||
export default class ChannelInviteModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.onListenerChange = this.onListenerChange.bind(this);
|
||||
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.handleInviteError = this.handleInviteError.bind(this);
|
||||
this.nextPage = this.nextPage.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
|
||||
this.state = this.getStateFromStores();
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!this.props.show && !nextProps.show) {
|
||||
return false;
|
||||
}
|
||||
this.term = '';
|
||||
|
||||
if (!Utils.areObjectsEqual(this.props, nextProps)) {
|
||||
return true;
|
||||
}
|
||||
const channelStats = ChannelStore.getStats(props.channel.id);
|
||||
const teamStats = TeamStore.getCurrentStats();
|
||||
|
||||
if (!Utils.areObjectsEqual(this.state, nextState)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
getStateFromStores() {
|
||||
const users = UserStore.getActiveOnlyProfiles();
|
||||
|
||||
if ($.isEmptyObject(users)) {
|
||||
return {
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
// make sure we have all members of this channel before rendering
|
||||
const extraInfo = ChannelStore.getCurrentExtraInfo();
|
||||
if (extraInfo.member_count !== extraInfo.members.length) {
|
||||
AsyncClient.getChannelExtraInfo(this.props.channel.id, -1);
|
||||
|
||||
return {
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
const currentUser = UserStore.getCurrentUser();
|
||||
if (!currentUser) {
|
||||
return {
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
const currentMember = ChannelStore.getCurrentMember();
|
||||
if (!currentMember) {
|
||||
return {
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
const memberIds = extraInfo.members.map((user) => user.id);
|
||||
|
||||
var nonmembers = [];
|
||||
for (var id in users) {
|
||||
if (memberIds.indexOf(id) === -1) {
|
||||
nonmembers.push(users[id]);
|
||||
}
|
||||
}
|
||||
|
||||
nonmembers.sort((a, b) => {
|
||||
return a.username.localeCompare(b.username);
|
||||
});
|
||||
|
||||
return {
|
||||
nonmembers,
|
||||
loading: false,
|
||||
currentUser,
|
||||
currentMember
|
||||
this.state = {
|
||||
users: [],
|
||||
total: teamStats.member_count - channelStats.member_count,
|
||||
search: false
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!this.props.show && nextProps.show) {
|
||||
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
|
||||
ChannelStore.addChangeListener(this.onListenerChange);
|
||||
UserStore.addChangeListener(this.onListenerChange);
|
||||
this.onListenerChange();
|
||||
TeamStore.addStatsChangeListener(this.onChange);
|
||||
ChannelStore.addStatsChangeListener(this.onChange);
|
||||
UserStore.addNotInChannelChangeListener(this.onChange);
|
||||
UserStore.addStatusesChangeListener(this.onChange);
|
||||
|
||||
this.onChange();
|
||||
AsyncClient.getProfilesNotInChannel(this.props.channel.id, 0);
|
||||
AsyncClient.getTeamStats(TeamStore.getCurrentId());
|
||||
} else if (this.props.show && !nextProps.show) {
|
||||
ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
|
||||
ChannelStore.removeChangeListener(this.onListenerChange);
|
||||
UserStore.removeChangeListener(this.onListenerChange);
|
||||
TeamStore.removeStatsChangeListener(this.onChange);
|
||||
ChannelStore.removeStatsChangeListener(this.onChange);
|
||||
UserStore.removeNotInChannelChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onChange);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
|
||||
ChannelStore.removeChangeListener(this.onListenerChange);
|
||||
UserStore.removeChangeListener(this.onListenerChange);
|
||||
ChannelStore.removeStatsChangeListener(this.onChange);
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeNotInChannelChangeListener(this.onChange);
|
||||
}
|
||||
onListenerChange() {
|
||||
var newState = this.getStateFromStores();
|
||||
if (!Utils.areObjectsEqual(this.state, newState)) {
|
||||
this.setState(newState);
|
||||
|
||||
onChange() {
|
||||
if (this.state.search) {
|
||||
this.search(this.term);
|
||||
return;
|
||||
}
|
||||
|
||||
const channelStats = ChannelStore.getStats(this.props.channel.id);
|
||||
const teamStats = TeamStore.getCurrentStats();
|
||||
|
||||
this.setState({
|
||||
users: UserStore.getProfileListNotInChannel(this.props.channel.id),
|
||||
total: teamStats.member_count - channelStats.member_count
|
||||
});
|
||||
}
|
||||
|
||||
handleInviteError(err) {
|
||||
if (err) {
|
||||
this.setState({
|
||||
@@ -130,6 +91,29 @@ export default class ChannelInviteModal extends React.Component {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
nextPage(page) {
|
||||
AsyncClient.getProfilesNotInChannel(this.props.channel.id, (page + 1) * USERS_PER_PAGE, USERS_PER_PAGE);
|
||||
}
|
||||
|
||||
search(term) {
|
||||
this.term = term;
|
||||
|
||||
if (term === '') {
|
||||
this.setState({users: UserStore.getProfileListNotInChannel(), search: false});
|
||||
return;
|
||||
}
|
||||
|
||||
searchUsers(
|
||||
term,
|
||||
TeamStore.getCurrentId(),
|
||||
{not_in_channel: this.props.channel.id},
|
||||
(users) => {
|
||||
this.setState({search: true, users});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
var inviteError = null;
|
||||
if (this.state.inviteError) {
|
||||
@@ -145,9 +129,13 @@ export default class ChannelInviteModal extends React.Component {
|
||||
maxHeight = Utils.windowHeight() - 300;
|
||||
}
|
||||
content = (
|
||||
<FilteredUserList
|
||||
<SearchableUserList
|
||||
style={{maxHeight}}
|
||||
users={this.state.nonmembers}
|
||||
users={this.state.users}
|
||||
usersPerPage={USERS_PER_PAGE}
|
||||
total={this.state.total}
|
||||
nextPage={this.nextPage}
|
||||
search={this.search}
|
||||
actions={[ChannelInviteButton]}
|
||||
actionProps={{
|
||||
channel: this.props.channel,
|
||||
|
||||
@@ -1,122 +1,89 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import FilteredUserList from './filtered_user_list.jsx';
|
||||
import SearchableUserList from './searchable_user_list.jsx';
|
||||
import LoadingScreen from './loading_screen.jsx';
|
||||
import ChannelInviteModal from './channel_invite_modal.jsx';
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
import {searchUsers} from 'actions/user_actions.jsx';
|
||||
import {removeUserFromChannel} from 'actions/channel_actions.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {Modal} from 'react-bootstrap';
|
||||
|
||||
import React from 'react';
|
||||
const USERS_PER_PAGE = 50;
|
||||
|
||||
export default class ChannelMembersModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.handleRemove = this.handleRemove.bind(this);
|
||||
|
||||
this.createRemoveMemberButton = this.createRemoveMemberButton.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
this.nextPage = this.nextPage.bind(this);
|
||||
|
||||
this.term = '';
|
||||
|
||||
const stats = ChannelStore.getStats(props.channel.id);
|
||||
|
||||
// the rest of the state gets populated when the modal is shown
|
||||
this.state = {
|
||||
showInviteModal: false
|
||||
users: [],
|
||||
total: stats.member_count,
|
||||
showInviteModal: false,
|
||||
search: false
|
||||
};
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!Utils.areObjectsEqual(this.props, nextProps)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Utils.areObjectsEqual(this.state, nextState)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
getStateFromStores() {
|
||||
const extraInfo = ChannelStore.getCurrentExtraInfo();
|
||||
const profiles = UserStore.getActiveOnlyProfiles();
|
||||
|
||||
if (extraInfo.member_count !== extraInfo.members.length) {
|
||||
AsyncClient.getChannelExtraInfo(this.props.channel.id, -1);
|
||||
|
||||
return {
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
const memberList = extraInfo.members.map((member) => {
|
||||
return profiles[member.id];
|
||||
});
|
||||
|
||||
function compareByUsername(a, b) {
|
||||
if (a.username < b.username) {
|
||||
return -1;
|
||||
} else if (a.username > b.username) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
memberList.sort(compareByUsername);
|
||||
|
||||
return {
|
||||
memberList,
|
||||
loading: false
|
||||
};
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!this.props.show && nextProps.show) {
|
||||
ChannelStore.addExtraInfoChangeListener(this.onChange);
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
ChannelStore.addStatsChangeListener(this.onChange);
|
||||
UserStore.addInChannelChangeListener(this.onChange);
|
||||
UserStore.addStatusesChangeListener(this.onChange);
|
||||
|
||||
this.onChange();
|
||||
AsyncClient.getProfilesInChannel(this.props.channel.id, 0);
|
||||
} else if (this.props.show && !nextProps.show) {
|
||||
ChannelStore.removeExtraInfoChangeListener(this.onChange);
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
ChannelStore.removeStatsChangeListener(this.onChange);
|
||||
UserStore.removeInChannelChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onChange);
|
||||
}
|
||||
}
|
||||
|
||||
onChange() {
|
||||
const newState = this.getStateFromStores();
|
||||
if (!Utils.areObjectsEqual(this.state, newState)) {
|
||||
this.setState(newState);
|
||||
if (this.state.search) {
|
||||
this.search(this.term);
|
||||
return;
|
||||
}
|
||||
|
||||
const stats = ChannelStore.getStats(this.props.channel.id);
|
||||
this.setState({
|
||||
users: UserStore.getProfileListInChannel(this.props.channel.id),
|
||||
total: stats.member_count
|
||||
});
|
||||
}
|
||||
|
||||
handleRemove(user) {
|
||||
const userId = user.id;
|
||||
|
||||
Client.removeChannelMember(
|
||||
ChannelStore.getCurrentId(),
|
||||
removeUserFromChannel(
|
||||
this.props.channel.id,
|
||||
userId,
|
||||
() => {
|
||||
const memberList = this.state.memberList.slice();
|
||||
for (let i = 0; i < memberList.length; i++) {
|
||||
if (userId === memberList[i].id) {
|
||||
memberList.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({memberList});
|
||||
AsyncClient.getChannelExtraInfo();
|
||||
},
|
||||
null,
|
||||
(err) => {
|
||||
this.setState({inviteError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
createRemoveMemberButton({user}) {
|
||||
if (user.id === UserStore.getCurrentId()) {
|
||||
return null;
|
||||
@@ -135,6 +102,29 @@ export default class ChannelMembersModal extends React.Component {
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
nextPage(page) {
|
||||
AsyncClient.getProfilesInChannel(this.props.channel.id, (page + 1) * USERS_PER_PAGE, USERS_PER_PAGE);
|
||||
}
|
||||
|
||||
search(term) {
|
||||
this.term = term;
|
||||
|
||||
if (term === '') {
|
||||
this.setState({users: UserStore.getProfileListInChannel(this.props.channel.id), search: false});
|
||||
return;
|
||||
}
|
||||
|
||||
searchUsers(
|
||||
term,
|
||||
TeamStore.getCurrentId(),
|
||||
{in_channel: this.props.channel.id},
|
||||
(users) => {
|
||||
this.setState({search: true, users});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let content;
|
||||
if (this.state.loading) {
|
||||
@@ -151,9 +141,13 @@ export default class ChannelMembersModal extends React.Component {
|
||||
}
|
||||
|
||||
content = (
|
||||
<FilteredUserList
|
||||
<SearchableUserList
|
||||
style={{maxHeight}}
|
||||
users={this.state.memberList}
|
||||
users={this.state.users}
|
||||
usersPerPage={USERS_PER_PAGE}
|
||||
total={this.state.total}
|
||||
nextPage={this.nextPage}
|
||||
search={this.search}
|
||||
actions={removeButton}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -65,9 +65,9 @@ export default class ChannelNotificationsModal extends React.Component {
|
||||
Client.updateChannelNotifyProps(data,
|
||||
() => {
|
||||
// YUCK
|
||||
var member = ChannelStore.getMember(channelId);
|
||||
var member = ChannelStore.getMyMember(channelId);
|
||||
member.notify_props.desktop = notifyLevel;
|
||||
ChannelStore.setChannelMember(member);
|
||||
ChannelStore.storeMyChannelMember(member);
|
||||
this.updateSection('');
|
||||
},
|
||||
(err) => {
|
||||
@@ -256,13 +256,13 @@ export default class ChannelNotificationsModal extends React.Component {
|
||||
mark_unread: markUnreadLevel
|
||||
};
|
||||
|
||||
//TODO: This should be fixed, moved to event_helpers
|
||||
//TODO: This should be fixed, moved to actions
|
||||
Client.updateChannelNotifyProps(data,
|
||||
() => {
|
||||
// Yuck...
|
||||
var member = ChannelStore.getMember(channelId);
|
||||
var member = ChannelStore.getMyMember(channelId);
|
||||
member.notify_props.mark_unread = markUnreadLevel;
|
||||
ChannelStore.setChannelMember(member);
|
||||
ChannelStore.storeMyChannelMember(member);
|
||||
this.updateSection('');
|
||||
},
|
||||
(err) => {
|
||||
|
||||
@@ -8,12 +8,13 @@ import SwitchChannelProvider from './suggestion/switch_channel_provider.jsx';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
|
||||
import {goToChannel, openDirectChannelToUser} from 'actions/channel_actions.jsx';
|
||||
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import * as ChannelActions from 'actions/channel_actions.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import $ from 'jquery';
|
||||
@@ -27,30 +28,14 @@ export default class SwitchChannelModal extends React.Component {
|
||||
this.onExited = this.onExited.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleDmUserChange = this.handleDmUserChange.bind(this);
|
||||
this.suggestionProviders = [new SwitchChannelProvider()];
|
||||
|
||||
this.state = {
|
||||
dmUsers: UserStore.getDirectProfiles(),
|
||||
text: '',
|
||||
error: ''
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
UserStore.addDmListChangeListener(this.handleDmUserChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
UserStore.removeDmListChangeListener(this.handleDmUserChange);
|
||||
}
|
||||
|
||||
handleDmUserChange() {
|
||||
this.setState({
|
||||
dmUsers: UserStore.getDirectProfiles()
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.show && !prevProps.show) {
|
||||
const textbox = this.refs.search.getTextbox();
|
||||
@@ -97,18 +82,13 @@ export default class SwitchChannelModal extends React.Component {
|
||||
const name = this.state.text.trim();
|
||||
let channel = null;
|
||||
|
||||
// TODO: Replace this hack with something reasonable
|
||||
if (name.indexOf(Utils.localizeMessage('channel_switch_modal.dm', '(Direct Message)')) > 0) {
|
||||
const dmUsername = name.substr(0, name.indexOf(Utils.localizeMessage('channel_switch_modal.dm', '(Direct Message)')) - 1);
|
||||
let user = null;
|
||||
for (const id in this.state.dmUsers) {
|
||||
if (this.state.dmUsers[id].username === dmUsername) {
|
||||
user = this.state.dmUsers[id];
|
||||
break;
|
||||
}
|
||||
}
|
||||
const user = UserStore.getProfileByUsername(dmUsername);
|
||||
|
||||
if (user) {
|
||||
Utils.openDirectChannelToUser(
|
||||
openDirectChannelToUser(
|
||||
user,
|
||||
(ch) => {
|
||||
channel = ch;
|
||||
@@ -123,7 +103,7 @@ export default class SwitchChannelModal extends React.Component {
|
||||
}
|
||||
|
||||
if (channel !== null) {
|
||||
ChannelActions.goToChannel(channel);
|
||||
goToChannel(channel);
|
||||
this.onHide();
|
||||
} else if (this.state.text !== '') {
|
||||
this.setState({
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import $ from 'jquery';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as UserAgent from 'utils/user_agent.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import Textbox from './textbox.jsx';
|
||||
|
||||
import BrowserStore from 'stores/browser_store.jsx';
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
import MessageHistoryStore from 'stores/message_history_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import {loadPosts} from 'actions/post_actions.jsx';
|
||||
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as UserAgent from 'utils/user_agent.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
var KeyCodes = Constants.KeyCodes;
|
||||
|
||||
import $ from 'jquery';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
export default class EditPostModal extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -77,7 +78,7 @@ export default class EditPostModal extends React.Component {
|
||||
Client.updatePost(
|
||||
updatedPost,
|
||||
() => {
|
||||
AsyncClient.getPosts(updatedPost.channel_id);
|
||||
loadPosts(updatedPost.channel_id);
|
||||
window.scrollTo(0, 0);
|
||||
},
|
||||
(err) => {
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import EmojiListItem from './emoji_list_item.jsx';
|
||||
import LoadingScreen from 'components/loading_screen.jsx';
|
||||
|
||||
import EmojiStore from 'stores/emoji_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {loadEmoji} from 'actions/emoji_actions.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import EmojiStore from 'stores/emoji_store.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import EmojiListItem from './emoji_list_item.jsx';
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router';
|
||||
import LoadingScreen from 'components/loading_screen.jsx';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
export default class EmojiList extends React.Component {
|
||||
static get propTypes() {
|
||||
@@ -24,28 +28,30 @@ export default class EmojiList extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.handleEmojiChange = this.handleEmojiChange.bind(this);
|
||||
|
||||
this.handleUserChange = this.handleUserChange.bind(this);
|
||||
this.deleteEmoji = this.deleteEmoji.bind(this);
|
||||
|
||||
this.updateFilter = this.updateFilter.bind(this);
|
||||
|
||||
this.state = {
|
||||
emojis: EmojiStore.getCustomEmojiMap(),
|
||||
loading: !EmojiStore.hasReceivedCustomEmojis(),
|
||||
filter: ''
|
||||
filter: '',
|
||||
users: UserStore.getProfiles()
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
EmojiStore.addChangeListener(this.handleEmojiChange);
|
||||
UserStore.addChangeListener(this.handleUserChange);
|
||||
|
||||
if (window.mm_config.EnableCustomEmoji === 'true') {
|
||||
AsyncClient.listEmoji();
|
||||
loadEmoji();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
EmojiStore.removeChangeListener(this.handleEmojiChange);
|
||||
UserStore.removeChangeListener(this.handleUserChange);
|
||||
}
|
||||
|
||||
handleEmojiChange() {
|
||||
@@ -55,6 +61,10 @@ export default class EmojiList extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
handleUserChange() {
|
||||
this.setState({users: UserStore.getProfiles()});
|
||||
}
|
||||
|
||||
updateFilter(e) {
|
||||
this.setState({
|
||||
filter: e.target.value
|
||||
@@ -98,6 +108,7 @@ export default class EmojiList extends React.Component {
|
||||
emoji={emoji}
|
||||
onDelete={onDelete}
|
||||
filter={filter}
|
||||
creator={this.state.users[emoji.creator_id] || {}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import EmojiStore from 'stores/emoji_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
@@ -14,7 +14,8 @@ export default class EmojiListItem extends React.Component {
|
||||
return {
|
||||
emoji: React.PropTypes.object.isRequired,
|
||||
onDelete: React.PropTypes.func.isRequired,
|
||||
filter: React.PropTypes.string
|
||||
filter: React.PropTypes.string,
|
||||
creator: React.PropTypes.object.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,10 +23,6 @@ export default class EmojiListItem extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.handleDelete = this.handleDelete.bind(this);
|
||||
|
||||
this.state = {
|
||||
creator: UserStore.getProfile(this.props.emoji.creator_id)
|
||||
};
|
||||
}
|
||||
|
||||
handleDelete(e) {
|
||||
@@ -57,7 +54,7 @@ export default class EmojiListItem extends React.Component {
|
||||
|
||||
render() {
|
||||
const emoji = this.props.emoji;
|
||||
const creator = this.state.creator;
|
||||
const creator = this.props.creator;
|
||||
const filter = this.props.filter ? this.props.filter.toLowerCase() : '';
|
||||
|
||||
if (!this.matchesFilter(emoji, creator, filter)) {
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
export default class InstalledCommand extends React.Component {
|
||||
@@ -13,7 +10,8 @@ export default class InstalledCommand extends React.Component {
|
||||
command: React.PropTypes.object.isRequired,
|
||||
onRegenToken: React.PropTypes.func.isRequired,
|
||||
onDelete: React.PropTypes.func.isRequired,
|
||||
filter: React.PropTypes.string
|
||||
filter: React.PropTypes.string,
|
||||
creator: React.PropTypes.object.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,7 +111,7 @@ export default class InstalledCommand extends React.Component {
|
||||
id='installed_integrations.creation'
|
||||
defaultMessage='Created by {creator} on {createAt, date, full}'
|
||||
values={{
|
||||
creator: Utils.displayUsername(command.creator_id),
|
||||
creator: this.props.creator.username,
|
||||
createAt: command.create_at
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import BackstageList from 'components/backstage/components/backstage_list.jsx';
|
||||
import InstalledCommand from './installed_command.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import IntegrationStore from 'stores/integration_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {loadTeamCommands} from 'actions/integration_actions.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import BackstageList from 'components/backstage/components/backstage_list.jsx';
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import InstalledCommand from './installed_command.jsx';
|
||||
|
||||
export default class InstalledCommands extends React.Component {
|
||||
static get propTypes() {
|
||||
@@ -23,7 +27,7 @@ export default class InstalledCommands extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.handleIntegrationChange = this.handleIntegrationChange.bind(this);
|
||||
|
||||
this.handleUserChange = this.handleUserChange.bind(this);
|
||||
this.regenCommandToken = this.regenCommandToken.bind(this);
|
||||
this.deleteCommand = this.deleteCommand.bind(this);
|
||||
|
||||
@@ -31,20 +35,23 @@ export default class InstalledCommands extends React.Component {
|
||||
|
||||
this.state = {
|
||||
commands: IntegrationStore.getCommands(teamId),
|
||||
loading: !IntegrationStore.hasReceivedCommands(teamId)
|
||||
loading: !IntegrationStore.hasReceivedCommands(teamId),
|
||||
users: UserStore.getProfiles()
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
IntegrationStore.addChangeListener(this.handleIntegrationChange);
|
||||
UserStore.addChangeListener(this.handleUserChange);
|
||||
|
||||
if (window.mm_config.EnableCommands === 'true') {
|
||||
AsyncClient.listTeamCommands();
|
||||
loadTeamCommands();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
IntegrationStore.removeChangeListener(this.handleIntegrationChange);
|
||||
UserStore.removeChangeListener(this.handleUserChange);
|
||||
}
|
||||
|
||||
handleIntegrationChange() {
|
||||
@@ -56,6 +63,10 @@ export default class InstalledCommands extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
handleUserChange() {
|
||||
this.setState({users: UserStore.getProfiles()});
|
||||
}
|
||||
|
||||
regenCommandToken(command) {
|
||||
AsyncClient.regenCommandToken(command.id);
|
||||
}
|
||||
@@ -72,6 +83,7 @@ export default class InstalledCommands extends React.Component {
|
||||
command={command}
|
||||
onRegenToken={this.regenCommandToken}
|
||||
onDelete={this.deleteCommand}
|
||||
creator={this.state.users[command.creator_id] || {}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -13,7 +13,8 @@ export default class InstalledIncomingWebhook extends React.Component {
|
||||
return {
|
||||
incomingWebhook: React.PropTypes.object.isRequired,
|
||||
onDelete: React.PropTypes.func.isRequired,
|
||||
filter: React.PropTypes.string
|
||||
filter: React.PropTypes.string,
|
||||
creator: React.PropTypes.object.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
@@ -108,7 +109,7 @@ export default class InstalledIncomingWebhook extends React.Component {
|
||||
id='installed_integrations.creation'
|
||||
defaultMessage='Created by {creator} on {createAt, date, full}'
|
||||
values={{
|
||||
creator: Utils.displayUsername(incomingWebhook.user_id),
|
||||
creator: this.props.creator.username,
|
||||
createAt: incomingWebhook.create_at
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import BackstageList from 'components/backstage/components/backstage_list.jsx';
|
||||
import InstalledIncomingWebhook from './installed_incoming_webhook.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import IntegrationStore from 'stores/integration_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {loadIncomingHooks} from 'actions/integration_actions.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import BackstageList from 'components/backstage/components/backstage_list.jsx';
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import InstalledIncomingWebhook from './installed_incoming_webhook.jsx';
|
||||
|
||||
export default class InstalledIncomingWebhooks extends React.Component {
|
||||
static get propTypes() {
|
||||
@@ -23,27 +27,30 @@ export default class InstalledIncomingWebhooks extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.handleIntegrationChange = this.handleIntegrationChange.bind(this);
|
||||
|
||||
this.handleUserChange = this.handleUserChange.bind(this);
|
||||
this.deleteIncomingWebhook = this.deleteIncomingWebhook.bind(this);
|
||||
|
||||
const teamId = TeamStore.getCurrentId();
|
||||
|
||||
this.state = {
|
||||
incomingWebhooks: IntegrationStore.getIncomingWebhooks(teamId),
|
||||
loading: !IntegrationStore.hasReceivedIncomingWebhooks(teamId)
|
||||
loading: !IntegrationStore.hasReceivedIncomingWebhooks(teamId),
|
||||
users: UserStore.getProfiles()
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
IntegrationStore.addChangeListener(this.handleIntegrationChange);
|
||||
UserStore.addChangeListener(this.handleUserChange);
|
||||
|
||||
if (window.mm_config.EnableIncomingWebhooks === 'true') {
|
||||
AsyncClient.listIncomingHooks();
|
||||
loadIncomingHooks();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
IntegrationStore.removeChangeListener(this.handleIntegrationChange);
|
||||
UserStore.removeChangeListener(this.handleUserChange);
|
||||
}
|
||||
|
||||
handleIntegrationChange() {
|
||||
@@ -55,6 +62,12 @@ export default class InstalledIncomingWebhooks extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
handleUserChange() {
|
||||
this.setState({
|
||||
users: UserStore.getProfiles()
|
||||
});
|
||||
}
|
||||
|
||||
deleteIncomingWebhook(incomingWebhook) {
|
||||
AsyncClient.deleteIncomingHook(incomingWebhook.id);
|
||||
}
|
||||
@@ -66,6 +79,7 @@ export default class InstalledIncomingWebhooks extends React.Component {
|
||||
key={incomingWebhook.id}
|
||||
incomingWebhook={incomingWebhook}
|
||||
onDelete={this.deleteIncomingWebhook}
|
||||
creator={this.state.users[incomingWebhook.user_id] || {}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
@@ -14,7 +13,8 @@ export default class InstalledOutgoingWebhook extends React.Component {
|
||||
outgoingWebhook: React.PropTypes.object.isRequired,
|
||||
onRegenToken: React.PropTypes.func.isRequired,
|
||||
onDelete: React.PropTypes.func.isRequired,
|
||||
filter: React.PropTypes.string
|
||||
filter: React.PropTypes.string,
|
||||
creator: React.PropTypes.object.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ export default class InstalledOutgoingWebhook extends React.Component {
|
||||
id='installed_integrations.creation'
|
||||
defaultMessage='Created by {creator} on {createAt, date, full}'
|
||||
values={{
|
||||
creator: Utils.displayUsername(outgoingWebhook.creator_id),
|
||||
creator: this.props.creator.username,
|
||||
createAt: outgoingWebhook.create_at
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import BackstageList from 'components/backstage/components/backstage_list.jsx';
|
||||
import InstalledOutgoingWebhook from './installed_outgoing_webhook.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import IntegrationStore from 'stores/integration_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import BackstageList from 'components/backstage/components/backstage_list.jsx';
|
||||
import {loadOutgoingHooks} from 'actions/integration_actions.jsx';
|
||||
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import InstalledOutgoingWebhook from './installed_outgoing_webhook.jsx';
|
||||
|
||||
export default class InstalledOutgoingWebhooks extends React.Component {
|
||||
static get propTypes() {
|
||||
@@ -23,7 +27,7 @@ export default class InstalledOutgoingWebhooks extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.handleIntegrationChange = this.handleIntegrationChange.bind(this);
|
||||
|
||||
this.handleUserChange = this.handleUserChange.bind(this);
|
||||
this.regenOutgoingWebhookToken = this.regenOutgoingWebhookToken.bind(this);
|
||||
this.deleteOutgoingWebhook = this.deleteOutgoingWebhook.bind(this);
|
||||
|
||||
@@ -31,20 +35,23 @@ export default class InstalledOutgoingWebhooks extends React.Component {
|
||||
|
||||
this.state = {
|
||||
outgoingWebhooks: IntegrationStore.getOutgoingWebhooks(teamId),
|
||||
loading: !IntegrationStore.hasReceivedOutgoingWebhooks(teamId)
|
||||
loading: !IntegrationStore.hasReceivedOutgoingWebhooks(teamId),
|
||||
users: UserStore.getProfiles()
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
IntegrationStore.addChangeListener(this.handleIntegrationChange);
|
||||
UserStore.addChangeListener(this.handleUserChange);
|
||||
|
||||
if (window.mm_config.EnableOutgoingWebhooks === 'true') {
|
||||
AsyncClient.listOutgoingHooks();
|
||||
loadOutgoingHooks();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
IntegrationStore.removeChangeListener(this.handleIntegrationChange);
|
||||
UserStore.removeChangeListener(this.handleUserChange);
|
||||
}
|
||||
|
||||
handleIntegrationChange() {
|
||||
@@ -56,6 +63,10 @@ export default class InstalledOutgoingWebhooks extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
handleUserChange() {
|
||||
this.setState({users: UserStore.getProfiles()});
|
||||
}
|
||||
|
||||
regenOutgoingWebhookToken(outgoingWebhook) {
|
||||
AsyncClient.regenOutgoingHookToken(outgoingWebhook.id);
|
||||
}
|
||||
@@ -72,6 +83,7 @@ export default class InstalledOutgoingWebhooks extends React.Component {
|
||||
outgoingWebhook={outgoingWebhook}
|
||||
onRegenToken={this.regenOutgoingWebhookToken}
|
||||
onDelete={this.deleteOutgoingWebhook}
|
||||
creator={this.state.users[outgoingWebhook.creator_id] || {}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import $ from 'jquery';
|
||||
import LoadingScreen from 'components/loading_screen.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import BrowserStore from 'stores/browser_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import * as WebSocketActions from 'actions/websocket_actions.jsx';
|
||||
import {loadEmoji} from 'actions/emoji_actions.jsx';
|
||||
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
|
||||
const BACKSPACE_CHAR = 8;
|
||||
|
||||
import $ from 'jquery';
|
||||
import React from 'react';
|
||||
|
||||
// import the EmojiStore so that it'll register to receive the results of the listEmojis call further down
|
||||
@@ -148,7 +151,7 @@ export default class LoggedIn extends React.Component {
|
||||
|
||||
// Get custom emoji from the server
|
||||
if (window.mm_config.EnableCustomEmoji === 'true') {
|
||||
AsyncClient.listEmoji();
|
||||
loadEmoji(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +1,94 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import FilteredUserList from './filtered_user_list.jsx';
|
||||
import TeamMembersDropdown from './team_members_dropdown.jsx';
|
||||
import SearchableUserList from 'components/searchable_user_list.jsx';
|
||||
import TeamMembersDropdown from 'components/team_members_dropdown.jsx';
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import {searchUsers, loadProfilesAndTeamMembers, loadTeamMembersForProfilesList} from 'actions/user_actions.jsx';
|
||||
import {getTeamStats} from 'utils/async_client.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const USERS_PER_PAGE = 50;
|
||||
|
||||
export default class MemberListTeam extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.getUsers = this.getUsers.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onTeamChange = this.onTeamChange.bind(this);
|
||||
this.onStatsChange = this.onStatsChange.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
this.loadComplete = this.loadComplete.bind(this);
|
||||
|
||||
const stats = TeamStore.getCurrentStats();
|
||||
|
||||
this.state = {
|
||||
users: this.getUsers(),
|
||||
teamMembers: TeamStore.getMembersForTeam()
|
||||
users: UserStore.getProfileListInTeam(),
|
||||
teamMembers: Object.assign([], TeamStore.getMembersInTeam()),
|
||||
total: stats.member_count,
|
||||
search: false,
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
UserStore.addChangeListener(this.onChange);
|
||||
TeamStore.addChangeListener(this.onTeamChange);
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
UserStore.addInTeamChangeListener(this.onChange);
|
||||
UserStore.addStatusesChangeListener(this.onChange);
|
||||
TeamStore.addChangeListener(this.onChange);
|
||||
TeamStore.addStatsChangeListener(this.onStatsChange);
|
||||
|
||||
loadProfilesAndTeamMembers(0, Constants.PROFILE_CHUNK_SIZE, TeamStore.getCurrentId(), this.loadComplete);
|
||||
getTeamStats(TeamStore.getCurrentId());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
UserStore.removeChangeListener(this.onChange);
|
||||
TeamStore.removeChangeListener(this.onTeamChange);
|
||||
UserStore.removeInTeamChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onChange);
|
||||
TeamStore.removeChangeListener(this.onChange);
|
||||
TeamStore.removeStatsChangeListener(this.onStatsChange);
|
||||
}
|
||||
|
||||
getUsers() {
|
||||
const profiles = UserStore.getProfiles();
|
||||
const users = [];
|
||||
|
||||
for (const id of Object.keys(profiles)) {
|
||||
users.push(profiles[id]);
|
||||
}
|
||||
|
||||
users.sort((a, b) => a.username.localeCompare(b.username));
|
||||
|
||||
return users;
|
||||
loadComplete() {
|
||||
this.setState({loading: false});
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.setState({
|
||||
users: this.getUsers()
|
||||
});
|
||||
if (!this.state.search) {
|
||||
this.setState({users: UserStore.getProfileListInTeam()});
|
||||
}
|
||||
|
||||
this.setState({teamMembers: Object.assign([], TeamStore.getMembersInTeam())});
|
||||
}
|
||||
|
||||
onTeamChange() {
|
||||
this.setState({
|
||||
teamMembers: TeamStore.getMembersForTeam()
|
||||
});
|
||||
onStatsChange() {
|
||||
const stats = TeamStore.getCurrentStats();
|
||||
this.setState({total: stats.member_count});
|
||||
}
|
||||
|
||||
nextPage(page) {
|
||||
loadProfilesAndTeamMembers((page + 1) * USERS_PER_PAGE, USERS_PER_PAGE);
|
||||
}
|
||||
|
||||
search(term) {
|
||||
if (term === '') {
|
||||
this.setState({search: false, users: UserStore.getProfileListInTeam()});
|
||||
return;
|
||||
}
|
||||
|
||||
searchUsers(
|
||||
term,
|
||||
TeamStore.getCurrentId(),
|
||||
{},
|
||||
(users) => {
|
||||
this.setState({loading: true, search: true, users});
|
||||
loadTeamMembersForProfilesList(users, TeamStore.getCurrentId(), this.loadComplete);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -65,12 +97,38 @@ export default class MemberListTeam extends React.Component {
|
||||
teamMembersDropdown = [TeamMembersDropdown];
|
||||
}
|
||||
|
||||
const teamMembers = this.state.teamMembers;
|
||||
const users = this.state.users;
|
||||
const actionUserProps = {};
|
||||
|
||||
let usersToDisplay;
|
||||
if (this.state.loading) {
|
||||
usersToDisplay = null;
|
||||
} else {
|
||||
usersToDisplay = [];
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
const user = users[i];
|
||||
|
||||
if (teamMembers[user.id]) {
|
||||
usersToDisplay.push(user);
|
||||
actionUserProps[user.id] = {
|
||||
teamMember: teamMembers[user.id]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FilteredUserList
|
||||
<SearchableUserList
|
||||
style={this.props.style}
|
||||
users={this.state.users}
|
||||
teamMembers={this.state.teamMembers}
|
||||
users={usersToDisplay}
|
||||
usersPerPage={USERS_PER_PAGE}
|
||||
total={this.state.total}
|
||||
nextPage={this.nextPage}
|
||||
search={this.search}
|
||||
actions={teamMembersDropdown}
|
||||
actionUserProps={actionUserProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,73 +1,67 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import FilteredUserList from 'components/filtered_user_list.jsx';
|
||||
import SearchableUserList from 'components/searchable_user_list.jsx';
|
||||
import SpinnerButton from 'components/spinner_button.jsx';
|
||||
import LoadingScreen from 'components/loading_screen.jsx';
|
||||
|
||||
import {getMoreDmList} from 'actions/user_actions.jsx';
|
||||
import {searchUsers} from 'actions/user_actions.jsx';
|
||||
import {openDirectChannelToUser} from 'actions/channel_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 * as Utils from 'utils/utils.jsx';
|
||||
import Constants from 'utils/constants.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;
|
||||
|
||||
export default class MoreDirectChannels extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleHide = this.handleHide.bind(this);
|
||||
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
||||
this.handleUserChange = this.handleUserChange.bind(this);
|
||||
this.onTeamChange = this.onTeamChange.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.createJoinDirectChannelButton = this.createJoinDirectChannelButton.bind(this);
|
||||
this.toggleList = this.toggleList.bind(this);
|
||||
this.nextPage = this.nextPage.bind(this);
|
||||
this.search = this.search.bind(this);
|
||||
this.loadComplete = this.loadComplete.bind(this);
|
||||
|
||||
this.state = {
|
||||
users: UserStore.getProfilesForDmList(),
|
||||
teamMembers: TeamStore.getMembersForTeam(),
|
||||
users: UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true),
|
||||
loadingDMChannel: -1,
|
||||
usersLoaded: false,
|
||||
teamMembersLoaded: false
|
||||
listType: 'team',
|
||||
loading: false,
|
||||
search: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
UserStore.addDmListChangeListener(this.handleUserChange);
|
||||
TeamStore.addChangeListener(this.onTeamChange);
|
||||
UserStore.addChangeListener(this.onChange);
|
||||
UserStore.addInTeamChangeListener(this.onChange);
|
||||
UserStore.addStatusesChangeListener(this.onChange);
|
||||
TeamStore.addChangeListener(this.onChange);
|
||||
|
||||
AsyncClient.getProfiles(0, Constants.PROFILE_CHUNK_SIZE);
|
||||
AsyncClient.getProfilesInTeam(TeamStore.getCurrentId(), 0, Constants.PROFILE_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
UserStore.removeDmListChangeListener(this.handleUserChange);
|
||||
TeamStore.removeChangeListener(this.onTeamChange);
|
||||
UserStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeInTeamChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onChange);
|
||||
TeamStore.removeChangeListener(this.onChange);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (nextProps.show !== this.props.show) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nextProps.onModalDismissed.toString() !== this.props.onModalDismissed.toString()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nextState.loadingDMChannel !== this.state.loadingDMChannel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Utils.areObjectsEqual(nextState.users, this.state.users)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Utils.areObjectsEqual(nextState.teamMembers, this.state.teamMembers)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
loadComplete() {
|
||||
this.setState({loading: false});
|
||||
}
|
||||
|
||||
handleHide() {
|
||||
@@ -84,7 +78,7 @@ export default class MoreDirectChannels extends React.Component {
|
||||
}
|
||||
|
||||
this.setState({loadingDMChannel: teammate.id});
|
||||
Utils.openDirectChannelToUser(
|
||||
openDirectChannelToUser(
|
||||
teammate,
|
||||
(channel) => {
|
||||
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name);
|
||||
@@ -97,17 +91,35 @@ export default class MoreDirectChannels extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
handleUserChange() {
|
||||
onChange(force) {
|
||||
if (this.state.search && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
let users;
|
||||
if (this.state.listType === 'any') {
|
||||
users = UserStore.getProfileList();
|
||||
} else {
|
||||
users = UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: UserStore.getProfilesForDmList(),
|
||||
usersLoaded: true
|
||||
users
|
||||
});
|
||||
}
|
||||
|
||||
onTeamChange() {
|
||||
toggleList(e) {
|
||||
const listType = e.target.value;
|
||||
let users;
|
||||
if (listType === 'any') {
|
||||
users = UserStore.getProfileList();
|
||||
} else {
|
||||
users = UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
teamMembers: TeamStore.getMembersForTeam(),
|
||||
teamMembersLoaded: true
|
||||
users,
|
||||
listType
|
||||
});
|
||||
}
|
||||
|
||||
@@ -126,38 +138,96 @@ export default class MoreDirectChannels extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
nextPage(page) {
|
||||
if (this.state.listType === 'any') {
|
||||
AsyncClient.getProfiles((page + 1) * USERS_PER_PAGE, USERS_PER_PAGE);
|
||||
} else {
|
||||
AsyncClient.getProfilesInTeam(TeamStore.getCurrentId(), (page + 1) * USERS_PER_PAGE, USERS_PER_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
search(term) {
|
||||
if (term === '') {
|
||||
this.onChange(true);
|
||||
this.setState({search: false});
|
||||
return;
|
||||
}
|
||||
|
||||
let teamId;
|
||||
if (this.state.listType === 'any') {
|
||||
teamId = '';
|
||||
} else {
|
||||
teamId = TeamStore.getCurrentId();
|
||||
}
|
||||
|
||||
searchUsers(
|
||||
term,
|
||||
teamId,
|
||||
{},
|
||||
(users) => {
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
if (users[i].id === UserStore.getCurrentId()) {
|
||||
users.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setState({search: true, users});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let maxHeight = 1000;
|
||||
if (Utils.windowHeight() <= 1200) {
|
||||
maxHeight = Utils.windowHeight() - 300;
|
||||
}
|
||||
|
||||
var body = null;
|
||||
if (!this.state.usersLoaded || !this.state.teamMembersLoaded) {
|
||||
body = (<LoadingScreen/>);
|
||||
} else {
|
||||
var showTeamToggle = false;
|
||||
if (global.window.mm_config.RestrictDirectMessage === 'any') {
|
||||
showTeamToggle = true;
|
||||
}
|
||||
|
||||
body = (
|
||||
<FilteredUserList
|
||||
style={{maxHeight}}
|
||||
users={this.state.users}
|
||||
teamMembers={this.state.teamMembers}
|
||||
actions={[this.createJoinDirectChannelButton]}
|
||||
showTeamToggle={showTeamToggle}
|
||||
/>
|
||||
let teamToggle;
|
||||
if (global.window.mm_config.RestrictDirectMessage === 'any') {
|
||||
teamToggle = (
|
||||
<div className='member-select__container'>
|
||||
<select
|
||||
className='form-control'
|
||||
id='restrictList'
|
||||
ref='restrictList'
|
||||
defaultValue='team'
|
||||
onChange={this.toggleList}
|
||||
>
|
||||
<option value='any'>
|
||||
<FormattedMessage
|
||||
id='filtered_user_list.any_team'
|
||||
defaultMessage='All Users'
|
||||
/>
|
||||
</option>
|
||||
<option value='team'>
|
||||
<FormattedMessage
|
||||
id='filtered_user_list.team_only'
|
||||
defaultMessage='Members of this Team'
|
||||
/>
|
||||
</option>
|
||||
</select>
|
||||
<span
|
||||
className='member-show'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='filtered_user_list.show'
|
||||
defaultMessage='Filter:'
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let users = this.state.users;
|
||||
if (this.state.loading) {
|
||||
users = null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dialogClassName='more-modal more-direct-channels'
|
||||
show={this.props.show}
|
||||
onHide={this.handleHide}
|
||||
onEntered={getMoreDmList}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>
|
||||
@@ -168,7 +238,16 @@ export default class MoreDirectChannels extends React.Component {
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{body}
|
||||
{teamToggle}
|
||||
<SearchableUserList
|
||||
key={'moreDirectChannelsList_' + this.state.listType}
|
||||
style={{maxHeight}}
|
||||
users={users}
|
||||
usersPerPage={USERS_PER_PAGE}
|
||||
nextPage={this.nextPage}
|
||||
search={this.search}
|
||||
actions={[this.createJoinDirectChannelButton]}
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
|
||||
@@ -69,8 +69,8 @@ export default class Navbar extends React.Component {
|
||||
return {
|
||||
channel: ChannelStore.getCurrent(),
|
||||
member: ChannelStore.getCurrentMember(),
|
||||
users: ChannelStore.getCurrentExtraInfo().members,
|
||||
userCount: ChannelStore.getCurrentExtraInfo().member_count,
|
||||
users: [],
|
||||
userCount: ChannelStore.getCurrentStats().member_count,
|
||||
currentUser: UserStore.getCurrentUser()
|
||||
};
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export default class Navbar extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
ChannelStore.addExtraInfoChangeListener(this.onChange);
|
||||
ChannelStore.addStatsChangeListener(this.onChange);
|
||||
UserStore.addStatusesChangeListener(this.onChange);
|
||||
$('.inner-wrap').click(this.hideSidebars);
|
||||
document.addEventListener('keydown', this.showChannelSwitchModal);
|
||||
@@ -89,7 +89,7 @@ export default class Navbar extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
ChannelStore.removeExtraInfoChangeListener(this.onChange);
|
||||
ChannelStore.removeStatsChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onChange);
|
||||
document.removeEventListener('keydown', this.showChannelSwitchModal);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import UserStore from 'stores/user_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import {startPeriodicStatusUpdates, stopPeriodicStatusUpdates} from 'actions/status_actions.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const TutorialSteps = Constants.TutorialSteps;
|
||||
const Preferences = Constants.Preferences;
|
||||
@@ -80,6 +81,7 @@ export default class NeedsTeam extends React.Component {
|
||||
if (tutorialStep <= TutorialSteps.INTRO_SCREENS) {
|
||||
browserHistory.push(TeamStore.getCurrentTeamRelativeUrl() + '/tutorial');
|
||||
}
|
||||
stopPeriodicStatusUpdates();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -89,6 +91,8 @@ export default class NeedsTeam extends React.Component {
|
||||
// Emit view action
|
||||
GlobalActions.viewLoggedIn();
|
||||
|
||||
startPeriodicStatusUpdates();
|
||||
|
||||
// Set up tracking for whether the window is active
|
||||
window.isActive = true;
|
||||
$(window).on('focus', () => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import ChannelStore from 'stores/channel_store.jsx';
|
||||
function getCountsStateFromStores() {
|
||||
var count = 0;
|
||||
var channels = ChannelStore.getAll();
|
||||
var members = ChannelStore.getAllMembers();
|
||||
var members = ChannelStore.getMyMembers();
|
||||
|
||||
channels.forEach((channel) => {
|
||||
var channelMember = members[channel.id];
|
||||
|
||||
@@ -6,9 +6,11 @@ import ProfilePicture from 'components/profile_picture.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {openDirectChannelToUser} from 'actions/channel_actions.jsx';
|
||||
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
|
||||
import $ from 'jquery';
|
||||
import React from 'react';
|
||||
@@ -22,20 +24,18 @@ export default class PopoverListMembers extends React.Component {
|
||||
|
||||
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
||||
this.closePopover = this.closePopover.bind(this);
|
||||
|
||||
this.state = {showPopover: false};
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
$('.member-list__popover .popover-content').perfectScrollbar();
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({showPopover: false});
|
||||
}
|
||||
|
||||
handleShowDirectChannel(teammate, e) {
|
||||
e.preventDefault();
|
||||
|
||||
Utils.openDirectChannelToUser(
|
||||
openDirectChannelToUser(
|
||||
teammate,
|
||||
(channel, channelAlreadyExisted) => {
|
||||
browserHistory.push(TeamStore.getCurrentTeamRelativeUrl() + '/channels/' + channel.name);
|
||||
@@ -90,12 +90,6 @@ export default class PopoverListMembers extends React.Component {
|
||||
}
|
||||
|
||||
if (name) {
|
||||
let status;
|
||||
if (m.status) {
|
||||
status = m.status;
|
||||
} else {
|
||||
status = UserStore.getStatus(m.id);
|
||||
}
|
||||
popoverHtml.push(
|
||||
<div
|
||||
className='more-modal__row'
|
||||
@@ -103,7 +97,6 @@ export default class PopoverListMembers extends React.Component {
|
||||
>
|
||||
<ProfilePicture
|
||||
src={`${Client.getUsersRoute()}/${m.id}/image?time=${m.update_at}`}
|
||||
status={status}
|
||||
width='26'
|
||||
height='26'
|
||||
/>
|
||||
@@ -123,19 +116,27 @@ export default class PopoverListMembers extends React.Component {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
popoverHtml.push(
|
||||
<div
|
||||
className='more-modal__row'
|
||||
key={'popover-member-more'}
|
||||
>
|
||||
<div className='col-sm-5'/>
|
||||
<div className='more-modal__details'>
|
||||
<div
|
||||
className='more-modal__name'
|
||||
>
|
||||
{'...'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let count = this.props.memberCount;
|
||||
const count = this.props.memberCount;
|
||||
let countText = '-';
|
||||
|
||||
// fall back to checking the length of the member list if the count isn't set
|
||||
if (!count && members) {
|
||||
count = members.length;
|
||||
}
|
||||
|
||||
if (count > Constants.MAX_CHANNEL_POPOVER_COUNT) {
|
||||
countText = Constants.MAX_CHANNEL_POPOVER_COUNT + '+';
|
||||
} else if (count > 0) {
|
||||
if (count > 0) {
|
||||
countText = count.toString();
|
||||
}
|
||||
|
||||
@@ -151,7 +152,10 @@ export default class PopoverListMembers extends React.Component {
|
||||
id='member_popover'
|
||||
className='member-popover__trigger'
|
||||
ref='member_popover_target'
|
||||
onClick={(e) => this.setState({popoverTarget: e.target, showPopover: !this.state.showPopover})}
|
||||
onClick={(e) => {
|
||||
this.setState({popoverTarget: e.target, showPopover: !this.state.showPopover});
|
||||
AsyncClient.getProfilesInChannel(this.props.channel.id, 0);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{countText}
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
|
||||
import {loadPosts} from 'actions/post_actions.jsx';
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
|
||||
@@ -29,13 +28,13 @@ export default class PendingPostOptions extends React.Component {
|
||||
var post = this.props.post;
|
||||
Client.createPost(post,
|
||||
(data) => {
|
||||
AsyncClient.getPosts(post.channel_id);
|
||||
loadPosts(post.channel_id);
|
||||
|
||||
var channel = ChannelStore.get(post.channel_id);
|
||||
var member = ChannelStore.getMember(post.channel_id);
|
||||
var member = ChannelStore.getMyMember(post.channel_id);
|
||||
member.msg_count = channel.total_msg_count;
|
||||
member.last_viewed_at = (new Date()).getTime();
|
||||
ChannelStore.setChannelMember(member);
|
||||
ChannelStore.storeMyChannelMember(member);
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POST,
|
||||
|
||||
@@ -66,6 +66,16 @@ export default class PostList extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// TODO: Clean-up intro text creation
|
||||
if (this.props.channel && this.props.channel.type === Constants.DM_CHANNEL) {
|
||||
const teammateId = Utils.getUserIdFromChannelName(this.props.channel);
|
||||
if (!this.props.profiles[teammateId] && nextProps.profiles[teammateId]) {
|
||||
this.introText = createChannelIntroMessage(this.props.channel, this.state.fullWidthIntro);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
if (e.which === Constants.KeyCodes.ESCAPE && $('.popover.in,.modal.in').length === 0) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -35,10 +35,7 @@ export default class PostFocusView extends React.Component {
|
||||
const focusedPostId = PostStore.getFocusedPostId();
|
||||
|
||||
const channel = ChannelStore.getCurrent();
|
||||
let profiles = UserStore.getProfiles();
|
||||
if (channel && channel.type === Constants.DM_CHANNEL) {
|
||||
profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
|
||||
}
|
||||
const profiles = UserStore.getProfiles();
|
||||
|
||||
const joinLeaveEnabled = PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'join_leave', true);
|
||||
|
||||
@@ -115,12 +112,7 @@ export default class PostFocusView extends React.Component {
|
||||
}
|
||||
|
||||
onUserChange() {
|
||||
const channel = ChannelStore.getCurrent();
|
||||
let profiles = UserStore.getProfiles();
|
||||
if (channel && channel.type === Constants.DM_CHANNEL) {
|
||||
profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
|
||||
}
|
||||
this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(profiles))});
|
||||
this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))});
|
||||
}
|
||||
|
||||
onStatusChange() {
|
||||
|
||||
@@ -34,13 +34,10 @@ export default class PostViewController extends React.Component {
|
||||
this.onBusy = this.onBusy.bind(this);
|
||||
|
||||
const channel = props.channel;
|
||||
let profiles = UserStore.getProfiles();
|
||||
if (channel && channel.type === Constants.DM_CHANNEL) {
|
||||
profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
|
||||
}
|
||||
const profiles = UserStore.getProfiles();
|
||||
|
||||
let lastViewed = Number.MAX_VALUE;
|
||||
const member = ChannelStore.getMember(channel.id);
|
||||
const member = ChannelStore.getMyMember(channel.id);
|
||||
if (member != null) {
|
||||
lastViewed = member.last_viewed_at;
|
||||
}
|
||||
@@ -107,12 +104,7 @@ export default class PostViewController extends React.Component {
|
||||
}
|
||||
|
||||
onUserChange() {
|
||||
const channel = this.state.channel;
|
||||
let profiles = UserStore.getProfiles();
|
||||
if (channel && channel.type === Constants.DM_CHANNEL) {
|
||||
profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
|
||||
}
|
||||
this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(profiles))});
|
||||
this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))});
|
||||
}
|
||||
|
||||
onPostsChange() {
|
||||
@@ -165,15 +157,12 @@ export default class PostViewController extends React.Component {
|
||||
const channel = nextProps.channel;
|
||||
|
||||
let lastViewed = Number.MAX_VALUE;
|
||||
const member = ChannelStore.getMember(channel.id);
|
||||
const member = ChannelStore.getMyMember(channel.id);
|
||||
if (member != null) {
|
||||
lastViewed = member.last_viewed_at;
|
||||
}
|
||||
|
||||
let profiles = UserStore.getProfiles();
|
||||
if (channel && channel.type === Constants.DM_CHANNEL) {
|
||||
profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
|
||||
}
|
||||
const profiles = UserStore.getProfiles();
|
||||
|
||||
const joinLeaveEnabled = PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'join_leave', true);
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ export default class RhsRootPost extends React.Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Utils.areObjectsEqual(nextProps.currentUser, this.props.currentUser)) {
|
||||
return true;
|
||||
}
|
||||
@@ -85,7 +89,7 @@ export default class RhsRootPost extends React.Component {
|
||||
var isOwner = this.props.currentUser.id === post.user_id;
|
||||
var isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
|
||||
const isSystemMessage = post.type && post.type.startsWith(Constants.SYSTEM_MESSAGE_PREFIX);
|
||||
var timestamp = UserStore.getProfile(post.user_id).update_at;
|
||||
var timestamp = user.update_at;
|
||||
var channel = ChannelStore.get(post.channel_id);
|
||||
const flagIcon = Constants.FLAG_ICON_SVG;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import RootPost from './rhs_root_post.jsx';
|
||||
import Comment from './rhs_comment.jsx';
|
||||
import FileUploadOverlay from './file_upload_overlay.jsx';
|
||||
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
@@ -238,12 +237,7 @@ export default class RhsThread extends React.Component {
|
||||
render() {
|
||||
const postsArray = this.state.postsArray;
|
||||
const selected = this.state.selected;
|
||||
const channel = ChannelStore.get(this.state.selected.channel_id);
|
||||
|
||||
let profiles = this.state.profiles || {};
|
||||
if (channel && channel.type === Constants.DM_CHANNEL) {
|
||||
profiles = Object.assign({}, profiles, UserStore.getDirectProfiles());
|
||||
}
|
||||
const profiles = this.state.profiles || {};
|
||||
|
||||
if (postsArray == null || selected == null) {
|
||||
return (
|
||||
|
||||
226
webapp/components/searchable_user_list.jsx
Обычный файл
226
webapp/components/searchable_user_list.jsx
Обычный файл
@@ -0,0 +1,226 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import UserList from 'components/user_list.jsx';
|
||||
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
import $ from 'jquery';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
const NEXT_BUTTON_TIMEOUT = 500;
|
||||
|
||||
export default class SearchableUserList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.nextPage = this.nextPage.bind(this);
|
||||
this.previousPage = this.previousPage.bind(this);
|
||||
this.doSearch = this.doSearch.bind(this);
|
||||
this.onSearchBoxKeyPress = this.onSearchBoxKeyPress.bind(this);
|
||||
this.onSearchBoxChange = this.onSearchBoxChange.bind(this);
|
||||
|
||||
this.nextTimeoutId = 0;
|
||||
|
||||
this.state = {
|
||||
page: 0,
|
||||
search: false,
|
||||
nextDisabled: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.state.page !== prevState.page) {
|
||||
$(ReactDOM.findDOMNode(this.refs.userList)).scrollTop(0);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.nextTimeoutId);
|
||||
}
|
||||
|
||||
nextPage(e) {
|
||||
e.preventDefault();
|
||||
this.setState({page: this.state.page + 1, nextDisabled: true});
|
||||
this.nextTimeoutId = setTimeout(() => this.setState({nextDisabled: false}), NEXT_BUTTON_TIMEOUT);
|
||||
this.props.nextPage(this.state.page + 1);
|
||||
}
|
||||
|
||||
previousPage(e) {
|
||||
e.preventDefault();
|
||||
this.setState({page: this.state.page - 1});
|
||||
}
|
||||
|
||||
doSearch() {
|
||||
const term = this.refs.filter.value;
|
||||
this.props.search(term);
|
||||
if (term === '') {
|
||||
this.setState({page: 0, search: false});
|
||||
} else {
|
||||
this.setState({search: true});
|
||||
}
|
||||
}
|
||||
|
||||
onSearchBoxKeyPress(e) {
|
||||
if (e.charCode === KeyCodes.ENTER) {
|
||||
e.preventDefault();
|
||||
this.doSearch();
|
||||
}
|
||||
}
|
||||
|
||||
onSearchBoxChange(e) {
|
||||
if (e.target.value === '') {
|
||||
this.props.search(''); // clear search
|
||||
this.setState({page: 0, search: false});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let nextButton;
|
||||
let previousButton;
|
||||
let usersToDisplay;
|
||||
let count;
|
||||
|
||||
if (this.props.users == null) {
|
||||
usersToDisplay = this.props.users;
|
||||
} else if (this.state.search || this.props.users == null) {
|
||||
usersToDisplay = this.props.users;
|
||||
|
||||
if (this.props.total) {
|
||||
count = (
|
||||
<FormattedMessage
|
||||
id='filtered_user_list.countTotal'
|
||||
defaultMessage='{count} {count, plural, =0 {0 members} one {member} other {members}} of {total} total'
|
||||
values={{
|
||||
count: usersToDisplay.length || 0,
|
||||
total: this.props.total
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const pageStart = this.state.page * this.props.usersPerPage;
|
||||
const pageEnd = pageStart + this.props.usersPerPage;
|
||||
usersToDisplay = this.props.users.slice(pageStart, pageEnd);
|
||||
|
||||
if (usersToDisplay.length >= this.props.usersPerPage) {
|
||||
nextButton = (
|
||||
<button
|
||||
className='btn btn-default filter-control filter-control__next'
|
||||
onClick={this.nextPage}
|
||||
disabled={this.state.nextDisabled}
|
||||
>
|
||||
{'Next'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.page > 0) {
|
||||
previousButton = (
|
||||
<button
|
||||
className='btn btn-default filter-control filter-control__prev'
|
||||
onClick={this.previousPage}
|
||||
>
|
||||
{'Previous'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.total) {
|
||||
const startCount = this.state.page * this.props.usersPerPage;
|
||||
const endCount = startCount + usersToDisplay.length;
|
||||
|
||||
count = (
|
||||
<FormattedMessage
|
||||
id='filtered_user_list.countTotalPage'
|
||||
defaultMessage='{startCount, number} - {endCount, number} {count, plural, =0 {0 members} one {member} other {members}} of {total} total'
|
||||
values={{
|
||||
count: usersToDisplay.length,
|
||||
startCount: startCount + 1,
|
||||
endCount,
|
||||
total: this.props.total
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='filtered-user-list'
|
||||
style={this.props.style}
|
||||
>
|
||||
<div className='filter-row'>
|
||||
<div className='col-sm-5'>
|
||||
<input
|
||||
ref='filter'
|
||||
className='form-control filter-textbox'
|
||||
placeholder={Utils.localizeMessage('filtered_user_list.search', 'Press enter to search')}
|
||||
onKeyPress={this.onSearchBoxKeyPress}
|
||||
onChange={this.onSearchBoxChange}
|
||||
/>
|
||||
</div>
|
||||
<div className='col-sm-2 filter-button'>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-primary'
|
||||
onClick={this.doSearch}
|
||||
disabled={this.props.users == null}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='filtered_user_list.searchButton'
|
||||
defaultMessage='Search'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className='col-sm-12'>
|
||||
<span className='member-count pull-left'>{count}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref='userList'
|
||||
className='more-modal__list'
|
||||
>
|
||||
<UserList
|
||||
users={usersToDisplay}
|
||||
extraInfo={this.props.extraInfo}
|
||||
actions={this.props.actions}
|
||||
actionProps={this.props.actionProps}
|
||||
actionUserProps={this.props.actionUserProps}
|
||||
/>
|
||||
</div>
|
||||
<div className='filter-controls'>
|
||||
{previousButton}
|
||||
{nextButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SearchableUserList.defaultProps = {
|
||||
users: [],
|
||||
usersPerPage: 50, //eslint-disable-line no-magic-numbers
|
||||
extraInfo: {},
|
||||
actions: [],
|
||||
actionProps: {},
|
||||
actionUserProps: {},
|
||||
showTeamToggle: false
|
||||
};
|
||||
|
||||
SearchableUserList.propTypes = {
|
||||
users: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||
usersPerPage: React.PropTypes.number,
|
||||
total: React.PropTypes.number,
|
||||
extraInfo: React.PropTypes.object,
|
||||
nextPage: React.PropTypes.func.isRequired,
|
||||
search: React.PropTypes.func.isRequired,
|
||||
actions: React.PropTypes.arrayOf(React.PropTypes.func),
|
||||
actionProps: React.PropTypes.object,
|
||||
actionUserProps: React.PropTypes.object,
|
||||
style: React.PropTypes.object
|
||||
};
|
||||
@@ -46,7 +46,7 @@ export default class SelectTeam extends React.Component {
|
||||
getStateFromStores(loaded) {
|
||||
return {
|
||||
teams: TeamStore.getAll(),
|
||||
teamMembers: TeamStore.getTeamMembers(),
|
||||
teamMembers: TeamStore.getMyTeamMembers(),
|
||||
teamListings: TeamStore.getTeamListings(),
|
||||
loaded
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ import LocalizationStore from 'stores/localization_store.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import * as ChannelActions from 'actions/channel_actions.jsx';
|
||||
import {loadProfilesAndTeamMembersForDMSidebar} from 'actions/user_actions.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
|
||||
@@ -93,7 +94,7 @@ export default class Sidebar extends React.Component {
|
||||
}
|
||||
|
||||
getStateFromStores() {
|
||||
const members = ChannelStore.getAllMembers();
|
||||
const members = ChannelStore.getMyMembers();
|
||||
const currentChannelId = ChannelStore.getCurrentId();
|
||||
const currentUserId = UserStore.getCurrentId();
|
||||
|
||||
@@ -133,9 +134,9 @@ export default class Sidebar extends React.Component {
|
||||
directChannel.teammate_id = teammateId;
|
||||
directChannel.status = UserStore.getStatus(teammateId) || 'offline';
|
||||
|
||||
if (UserStore.hasTeamProfile(teammateId) && TeamStore.hasActiveMemberForTeam(teammateId)) {
|
||||
if (TeamStore.hasActiveMemberInTeam(TeamStore.getCurrentId(), teammateId)) {
|
||||
directChannels.push(directChannel);
|
||||
} else {
|
||||
} else if (TeamStore.hasMemberNotInTeam(TeamStore.getCurrentId(), teammateId)) {
|
||||
directNonTeamChannels.push(directChannel);
|
||||
}
|
||||
}
|
||||
@@ -164,6 +165,7 @@ export default class Sidebar extends React.Component {
|
||||
componentDidMount() {
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
UserStore.addChangeListener(this.onChange);
|
||||
UserStore.addInTeamChangeListener(this.onChange);
|
||||
UserStore.addStatusesChangeListener(this.onChange);
|
||||
TeamStore.addChangeListener(this.onChange);
|
||||
PreferenceStore.addChangeListener(this.onChange);
|
||||
@@ -173,6 +175,8 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
document.addEventListener('keydown', this.navigateChannelShortcut);
|
||||
document.addEventListener('keydown', this.navigateUnreadChannelShortcut);
|
||||
|
||||
loadProfilesAndTeamMembersForDMSidebar();
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
@@ -205,6 +209,7 @@ export default class Sidebar extends React.Component {
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeChangeListener(this.onChange);
|
||||
UserStore.removeInTeamChangeListener(this.onChange);
|
||||
UserStore.removeStatusesChangeListener(this.onChange);
|
||||
TeamStore.removeChangeListener(this.onChange);
|
||||
PreferenceStore.removeChangeListener(this.onChange);
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class SidebarHeaderDropdown extends React.Component {
|
||||
|
||||
this.state = {
|
||||
teams: TeamStore.getAll(),
|
||||
teamMembers: TeamStore.getTeamMembers(),
|
||||
teamMembers: TeamStore.getMyTeamMembers(),
|
||||
showDropdown: false
|
||||
};
|
||||
}
|
||||
@@ -118,7 +118,7 @@ export default class SidebarHeaderDropdown extends React.Component {
|
||||
onTeamChange() {
|
||||
this.setState({
|
||||
teams: TeamStore.getAll(),
|
||||
teamMembers: TeamStore.getTeamMembers()
|
||||
teamMembers: TeamStore.getMyTeamMembers()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import SuggestionStore from 'stores/suggestion_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import Suggestion from './suggestion.jsx';
|
||||
|
||||
const MaxUserSuggestions = 40;
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
|
||||
import {autocompleteUsersInChannel} from 'actions/user_actions.jsx';
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import {Constants, ActionTypes} from 'utils/constants.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
class AtMentionSuggestion extends Suggestion {
|
||||
render() {
|
||||
@@ -99,92 +99,66 @@ class AtMentionSuggestion extends Suggestion {
|
||||
}
|
||||
}
|
||||
|
||||
function filterUsersByPrefix(users, prefix, limit, type) {
|
||||
const filtered = [];
|
||||
|
||||
for (const id of Object.keys(users)) {
|
||||
if (filtered.length >= limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
const user = users[id];
|
||||
|
||||
if (user.delete_at > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (user.username.startsWith(prefix) ||
|
||||
(user.first_name && user.first_name.toLowerCase().startsWith(prefix)) ||
|
||||
(user.last_name && user.last_name.toLowerCase().startsWith(prefix)) ||
|
||||
(user.nickname && user.nickname.toLowerCase().startsWith(prefix))) {
|
||||
// create a new object here since we're mutating it by adding the type field
|
||||
filtered.push(Object.assign({}, user, {type}));
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
export default class AtMentionProvider {
|
||||
constructor(channelId) {
|
||||
this.channelId = channelId;
|
||||
this.timeoutId = '';
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
|
||||
handlePretextChanged(suggestionId, pretext) {
|
||||
clearTimeout(this.timeoutId);
|
||||
|
||||
const captured = (/@([a-z0-9\-\._]*)$/i).exec(pretext.toLowerCase());
|
||||
if (captured) {
|
||||
const prefix = captured[1];
|
||||
|
||||
// Group users into members and nonmembers of the channel.
|
||||
const users = UserStore.getActiveOnlyProfiles(true);
|
||||
const channelMembers = {};
|
||||
const channelNonmembers = users;
|
||||
if (this.channelId != null) {
|
||||
const extraInfo = ChannelStore.getExtraInfo(this.channelId);
|
||||
for (let i = 0; i < extraInfo.members.length; i++) {
|
||||
const id = extraInfo.members[i].id;
|
||||
if (users[id]) {
|
||||
channelMembers[id] = users[id];
|
||||
Reflect.deleteProperty(channelNonmembers, id);
|
||||
function autocomplete() {
|
||||
autocompleteUsersInChannel(
|
||||
prefix,
|
||||
this.channelId,
|
||||
(data) => {
|
||||
const members = data.in_channel;
|
||||
for (const id of Object.keys(members)) {
|
||||
members[id].type = Constants.MENTION_MEMBERS;
|
||||
}
|
||||
|
||||
const nonmembers = data.out_of_channel;
|
||||
for (const id of Object.keys(nonmembers)) {
|
||||
nonmembers[id].type = Constants.MENTION_NONMEMBERS;
|
||||
}
|
||||
|
||||
let specialMentions = [];
|
||||
if (!pretext.startsWith('/msg')) {
|
||||
specialMentions = ['here', 'channel', 'all'].filter((item) => {
|
||||
return item.startsWith(prefix);
|
||||
}).map((name) => {
|
||||
return {username: name, type: Constants.MENTION_SPECIAL};
|
||||
});
|
||||
}
|
||||
|
||||
const users = members.concat(specialMentions).concat(nonmembers);
|
||||
const mentions = users.map((user) => '@' + user.username);
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
|
||||
id: suggestionId,
|
||||
matchedPretext: captured[0],
|
||||
terms: mentions,
|
||||
items: users,
|
||||
component: AtMentionSuggestion
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Filter users by prefix.
|
||||
const filteredMembers = filterUsersByPrefix(
|
||||
channelMembers, prefix, MaxUserSuggestions, Constants.MENTION_MEMBERS);
|
||||
const filteredNonmembers = filterUsersByPrefix(
|
||||
channelNonmembers, prefix, MaxUserSuggestions - filteredMembers.length, Constants.MENTION_NONMEMBERS);
|
||||
let filteredSpecialMentions = [];
|
||||
if (!pretext.startsWith('/msg')) {
|
||||
filteredSpecialMentions = ['here', 'channel', 'all'].filter((item) => {
|
||||
return item.startsWith(prefix);
|
||||
}).map((name) => {
|
||||
return {username: name, type: Constants.MENTION_SPECIAL};
|
||||
});
|
||||
}
|
||||
|
||||
// Sort users by username.
|
||||
[filteredMembers, filteredNonmembers].forEach((items) => {
|
||||
items.sort((a, b) => {
|
||||
const aPrefix = a.username.startsWith(prefix);
|
||||
const bPrefix = b.username.startsWith(prefix);
|
||||
|
||||
if (aPrefix === bPrefix) {
|
||||
return a.username.localeCompare(b.username);
|
||||
} else if (aPrefix) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
});
|
||||
|
||||
const filtered = filteredMembers.concat(filteredSpecialMentions).concat(filteredNonmembers);
|
||||
|
||||
const mentions = filtered.map((user) => '@' + user.username);
|
||||
|
||||
SuggestionStore.addSuggestions(suggestionId, mentions, filtered, AtMentionSuggestion, captured[0]);
|
||||
this.timeoutId = setTimeout(
|
||||
autocomplete.bind(this),
|
||||
Constants.AUTOCOMPLETE_TIMEOUT
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Client from 'client/web_client.jsx';
|
||||
import SuggestionStore from 'stores/suggestion_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import Suggestion from './suggestion.jsx';
|
||||
|
||||
import {autocompleteUsersInTeam} from 'actions/user_actions.jsx';
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import {Constants, ActionTypes} from 'utils/constants.jsx';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
class SearchUserSuggestion extends Suggestion {
|
||||
render() {
|
||||
const {item, isSelection} = this.props;
|
||||
@@ -18,6 +21,17 @@ class SearchUserSuggestion extends Suggestion {
|
||||
className += ' selected';
|
||||
}
|
||||
|
||||
const username = item.username;
|
||||
let description = '';
|
||||
|
||||
if ((item.first_name || item.last_name) && item.nickname) {
|
||||
description = `- ${Utils.getFullName(item)} (${item.nickname})`;
|
||||
} else if (item.nickname) {
|
||||
description = `- (${item.nickname})`;
|
||||
} else if (item.first_name || item.last_name) {
|
||||
description = `- ${Utils.getFullName(item)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
@@ -27,34 +41,60 @@ class SearchUserSuggestion extends Suggestion {
|
||||
className='profile-img rounded'
|
||||
src={Client.getUsersRoute() + '/' + item.id + '/image?time=' + item.update_at}
|
||||
/>
|
||||
<i className='fa fa fa-plus-square'/>{item.username}
|
||||
<i className='fa fa fa-plus-square'/>
|
||||
<div className='mention--align'>
|
||||
<span>
|
||||
{username}
|
||||
</span>
|
||||
<span className='mention__fullname'>
|
||||
{' '}
|
||||
{description}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class SearchUserProvider {
|
||||
constructor() {
|
||||
this.timeoutId = '';
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
|
||||
handlePretextChanged(suggestionId, pretext) {
|
||||
clearTimeout(this.timeoutId);
|
||||
|
||||
const captured = (/\bfrom:\s*(\S*)$/i).exec(pretext.toLowerCase());
|
||||
if (captured) {
|
||||
const usernamePrefix = captured[1];
|
||||
|
||||
const users = UserStore.getProfiles();
|
||||
let filtered = [];
|
||||
function autocomplete() {
|
||||
autocompleteUsersInTeam(
|
||||
usernamePrefix,
|
||||
(data) => {
|
||||
const users = data.in_team;
|
||||
const mentions = users.map((user) => user.username);
|
||||
|
||||
for (const id of Object.keys(users)) {
|
||||
const user = users[id];
|
||||
|
||||
if (user.username.startsWith(usernamePrefix)) {
|
||||
filtered.push(user);
|
||||
}
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
|
||||
id: suggestionId,
|
||||
matchedPretext: usernamePrefix,
|
||||
terms: mentions,
|
||||
items: users,
|
||||
component: SearchUserSuggestion
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
filtered = filtered.sort((a, b) => a.username.localeCompare(b.username));
|
||||
|
||||
const usernames = filtered.map((user) => user.username);
|
||||
|
||||
SuggestionStore.addSuggestions(suggestionId, usernames, filtered, SearchUserSuggestion, usernamePrefix);
|
||||
this.timeoutId = setTimeout(
|
||||
autocomplete.bind(this),
|
||||
Constants.AUTOCOMPLETE_TIMEOUT
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,4 +163,4 @@ SuggestionList.propTypes = {
|
||||
|
||||
SuggestionList.defaultProps = {
|
||||
renderDividers: false
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import SuggestionStore from 'stores/suggestion_store.jsx';
|
||||
import Suggestion from './suggestion.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
@@ -58,7 +57,10 @@ export default class SwitchChannelProvider {
|
||||
const channel = allChannels[id];
|
||||
if (channel.display_name.toLowerCase().startsWith(channelPrefix.toLowerCase())) {
|
||||
channels.push(channel);
|
||||
} else if (channel.type === Constants.DM_CHANNEL && Utils.getDirectTeammate(channel.id).username.startsWith(channelPrefix.toLowerCase())) {
|
||||
}
|
||||
|
||||
// TODO: Fix with auto-complete refactor
|
||||
/*else if (channel.type === Constants.DM_CHANNEL && Utils.getDirectTeammate(channel.id).username.startsWith(channelPrefix.toLowerCase())) {
|
||||
// New channel to not modify existing channel
|
||||
const otherUser = Utils.getDirectTeammate(channel.id);
|
||||
const newChannel = {
|
||||
@@ -68,7 +70,7 @@ export default class SwitchChannelProvider {
|
||||
status: UserStore.getStatus(otherUser.id) || 'offline'
|
||||
};
|
||||
channels.push(newChannel);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
channels.sort((a, b) => {
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import ConfirmModal from './confirm_modal.jsx';
|
||||
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
|
||||
import {removeUserFromTeam} from 'actions/team_actions.jsx';
|
||||
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import ConfirmModal from './confirm_modal.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
|
||||
export default class TeamMembersDropdown extends React.Component {
|
||||
@@ -44,8 +47,8 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
this.props.user.id,
|
||||
'team_user',
|
||||
() => {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
AsyncClient.getTeamMember(this.props.teamMember.team_id);
|
||||
AsyncClient.getUser(this.props.user.id);
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
@@ -54,24 +57,23 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
}
|
||||
}
|
||||
handleRemoveFromTeam() {
|
||||
Client.removeUserFromTeam(
|
||||
'',
|
||||
this.props.user.id,
|
||||
() => {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
);
|
||||
removeUserFromTeam(
|
||||
this.props.teamMember.team_id,
|
||||
this.props.user.id,
|
||||
() => {
|
||||
AsyncClient.getTeamStats(this.props.teamMember.team_id);
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
handleMakeActive() {
|
||||
Client.updateActive(this.props.user.id, true,
|
||||
() => {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
AsyncClient.getChannelExtraInfo(ChannelStore.getCurrentId());
|
||||
AsyncClient.getUser(this.props.user.id);
|
||||
AsyncClient.getChannelStats(ChannelStore.getCurrentId());
|
||||
AsyncClient.getTeamStats(this.props.teamMember.team_id);
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
@@ -81,9 +83,9 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
handleMakeNotActive() {
|
||||
Client.updateActive(this.props.user.id, false,
|
||||
() => {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
AsyncClient.getChannelExtraInfo(ChannelStore.getCurrentId());
|
||||
AsyncClient.getUser(this.props.user.id);
|
||||
AsyncClient.getChannelStats(ChannelStore.getCurrentId());
|
||||
AsyncClient.getTeamStats(this.props.teamMember.team_id);
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
@@ -100,8 +102,8 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
this.props.user.id,
|
||||
'team_user team_admin',
|
||||
() => {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
AsyncClient.getTeamMember(this.props.teamMember.team_id, this.props.user.id);
|
||||
AsyncClient.getUser(this.props.user.id);
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
@@ -133,8 +135,8 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
this.props.user.id,
|
||||
this.state.newRole,
|
||||
() => {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
AsyncClient.getTeamMember(this.props.teamMember.team_id, this.props.user.id);
|
||||
AsyncClient.getUser(this.props.user.id);
|
||||
|
||||
const teamUrl = TeamStore.getCurrentTeamUrl();
|
||||
if (teamUrl) {
|
||||
|
||||
@@ -109,7 +109,7 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.channelId !== this.channelId) {
|
||||
if (nextProps.channelId !== this.props.channelId) {
|
||||
// Update channel id for AtMentionProvider.
|
||||
const providers = this.suggestionProviders;
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import UserListRow from './user_list_row.jsx';
|
||||
import LoadingScreen from 'components/loading_screen.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
export default class UserList extends React.Component {
|
||||
render() {
|
||||
const users = this.props.users;
|
||||
|
||||
let content;
|
||||
if (users.length > 0) {
|
||||
if (users == null) {
|
||||
return <LoadingScreen/>;
|
||||
} else if (users.length > 0) {
|
||||
content = users.map((user) => {
|
||||
var teamMember;
|
||||
for (var index in this.props.teamMembers) {
|
||||
if (this.props.teamMembers[index].user_id === user.id) {
|
||||
teamMember = this.props.teamMembers[index];
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<UserListRow
|
||||
key={user.id}
|
||||
user={user}
|
||||
teamMember={teamMember}
|
||||
extraInfo={this.props.extraInfo[user.id]}
|
||||
actions={this.props.actions}
|
||||
actionProps={this.props.actionProps}
|
||||
actionUserProps={this.props.actionUserProps[user.id]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -56,14 +53,15 @@ export default class UserList extends React.Component {
|
||||
|
||||
UserList.defaultProps = {
|
||||
users: [],
|
||||
teamMembers: [],
|
||||
extraInfo: {},
|
||||
actions: [],
|
||||
actionProps: {}
|
||||
};
|
||||
|
||||
UserList.propTypes = {
|
||||
users: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||
teamMembers: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||
extraInfo: React.PropTypes.object,
|
||||
actions: React.PropTypes.arrayOf(React.PropTypes.func),
|
||||
actionProps: React.PropTypes.object
|
||||
actionProps: React.PropTypes.object,
|
||||
actionUserProps: React.PropTypes.object
|
||||
};
|
||||
|
||||
@@ -11,8 +11,9 @@ import * as Utils from 'utils/utils.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedHTMLMessage} from 'react-intl';
|
||||
|
||||
export default function UserListRow({user, teamMember, actions, actionProps}) {
|
||||
export default function UserListRow({user, extraInfo, actions, actionProps, actionUserProps}) {
|
||||
const nameFormat = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', '');
|
||||
|
||||
let name = user.username;
|
||||
@@ -29,15 +30,29 @@ export default function UserListRow({user, teamMember, actions, actionProps}) {
|
||||
<Action
|
||||
key={index.toString()}
|
||||
user={user}
|
||||
teamMember={teamMember}
|
||||
{...actionProps}
|
||||
{...actionUserProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// QUICK HACK, NEEDS A PROP FOR TOGGLING STATUS
|
||||
let email = user.email;
|
||||
let emailStyle = 'more-modal__description';
|
||||
let status;
|
||||
if (user.status) {
|
||||
if (extraInfo && extraInfo.length > 0) {
|
||||
email = (
|
||||
<FormattedHTMLMessage
|
||||
id='admin.user_item.emailTitle'
|
||||
defaultMessage='<strong>Email:</strong> {email}'
|
||||
values={{
|
||||
email: user.email
|
||||
}}
|
||||
/>
|
||||
);
|
||||
emailStyle = '';
|
||||
} else if (user.status) {
|
||||
status = user.status;
|
||||
} else {
|
||||
status = UserStore.getStatus(user.id);
|
||||
@@ -60,9 +75,10 @@ export default function UserListRow({user, teamMember, actions, actionProps}) {
|
||||
<div className='more-modal__name'>
|
||||
{name}
|
||||
</div>
|
||||
<div className='more-modal__description'>
|
||||
{user.email}
|
||||
<div className={emailStyle}>
|
||||
{email}
|
||||
</div>
|
||||
{extraInfo}
|
||||
</div>
|
||||
<div
|
||||
className='more-modal__actions'
|
||||
@@ -74,17 +90,16 @@ export default function UserListRow({user, teamMember, actions, actionProps}) {
|
||||
}
|
||||
|
||||
UserListRow.defaultProps = {
|
||||
teamMember: {
|
||||
team_id: '',
|
||||
roles: ''
|
||||
},
|
||||
extraInfo: [],
|
||||
actions: [],
|
||||
actionProps: {}
|
||||
actionProps: {},
|
||||
actionUserProps: {}
|
||||
};
|
||||
|
||||
UserListRow.propTypes = {
|
||||
user: React.PropTypes.object.isRequired,
|
||||
teamMember: React.PropTypes.object.isRequired,
|
||||
extraInfo: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||
actions: React.PropTypes.arrayOf(React.PropTypes.func),
|
||||
actionProps: React.PropTypes.object
|
||||
actionProps: React.PropTypes.object,
|
||||
actionUserProps: React.PropTypes.object
|
||||
};
|
||||
|
||||
@@ -808,8 +808,8 @@
|
||||
"admin.true": "true",
|
||||
"admin.userList.title": "Users for {team}",
|
||||
"admin.userList.title2": "Users for {team} ({count})",
|
||||
"admin.user_item.authServiceEmail": ", <strong>Sign-in Method:</strong> Email",
|
||||
"admin.user_item.authServiceNotEmail": ", <strong>Sign-in Method:</strong> {service}",
|
||||
"admin.user_item.authServiceEmail": "<strong>Sign-in Method:</strong> Email",
|
||||
"admin.user_item.authServiceNotEmail": "<strong>Sign-in Method:</strong> {service}",
|
||||
"admin.user_item.confirmDemoteDescription": "If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.",
|
||||
"admin.user_item.confirmDemoteRoleTitle": "Confirm demotion from System Admin role",
|
||||
"admin.user_item.confirmDemotion": "Confirm Demotion",
|
||||
@@ -822,8 +822,8 @@
|
||||
"admin.user_item.makeSysAdmin": "Make System Admin",
|
||||
"admin.user_item.makeTeamAdmin": "Make Team Admin",
|
||||
"admin.user_item.member": "Member",
|
||||
"admin.user_item.mfaNo": ", <strong>MFA</strong>: No",
|
||||
"admin.user_item.mfaYes": ", <strong>MFA</strong>: Yes",
|
||||
"admin.user_item.mfaNo": "<strong>MFA</strong>: No",
|
||||
"admin.user_item.mfaYes": "<strong>MFA</strong>: Yes",
|
||||
"admin.user_item.resetMfa": "Remove MFA",
|
||||
"admin.user_item.resetPwd": "Reset Password",
|
||||
"admin.user_item.switchToEmail": "Switch to Email/Password",
|
||||
@@ -881,6 +881,9 @@
|
||||
"analytics.system.totalSessions": "Total Sessions",
|
||||
"analytics.system.totalTeams": "Total Teams",
|
||||
"analytics.system.totalUsers": "Total Users",
|
||||
"analytics.system.totalWebsockets" : "Websocket Conns",
|
||||
"analytics.system.totalMasterDbConnections": "Master DB Conns",
|
||||
"analytics.system.totalReadDbConnections": "Replica DB Conns",
|
||||
"analytics.team.activeUsers": "Active Users With Posts",
|
||||
"analytics.team.newlyCreated": "Newly Created Users",
|
||||
"analytics.team.privateGroups": "Private Groups",
|
||||
@@ -1192,13 +1195,14 @@
|
||||
"file_upload.limited": "Uploads limited to {count} files maximum. Please use additional posts for more files.",
|
||||
"file_upload.pasted": "Image Pasted at ",
|
||||
"filtered_channels_list.count": "{count} {count, plural, =0 {0 channels} one {channel} other {channels}}",
|
||||
"filtered_channels_list.countTotal": "{count} {count, plural, =0 {0 channels} one {channel} other {channels}} of {total} Total",
|
||||
"filtered_channels_list.countTotal": "{count} {count, plural, =0 {0 channels} one {channel} other {channels}} of {total} total",
|
||||
"filtered_channels_list.search": "Search channels",
|
||||
"filtered_user_list.any_team": "All Users",
|
||||
"filtered_user_list.count": "{count} {count, plural, =0 {0 members} one {member} other {members}}",
|
||||
"filtered_user_list.countTotal": "{count} {count, plural, =0 {0 members} one {member} other {members}} of {total} Total",
|
||||
"filtered_user_list.countTotal": "{count} {count, plural, =0 {0 members} one {member} other {members}} of {total} total",
|
||||
"filtered_user_list.countTotalPage": "{startCount, number} - {endCount, number} {count, plural, =0 {0 members} one {member} other {members}} of {total} total",
|
||||
"filtered_user_list.member": "Member",
|
||||
"filtered_user_list.search": "Search members",
|
||||
"filtered_user_list.search": "Press enter to search",
|
||||
"filtered_user_list.show": "Filter:",
|
||||
"filtered_user_list.team_only": "Members of this Team",
|
||||
"find_team.email": "Email",
|
||||
|
||||
@@ -58,7 +58,6 @@ function preNeedsTeam(nextState, replace, callback) {
|
||||
// for the current url.
|
||||
const teamName = nextState.params.team;
|
||||
var team = TeamStore.getByName(teamName);
|
||||
const oldTeamId = TeamStore.getCurrentId();
|
||||
|
||||
if (!team) {
|
||||
browserHistory.push('/');
|
||||
@@ -70,15 +69,7 @@ function preNeedsTeam(nextState, replace, callback) {
|
||||
TeamStore.saveMyTeam(team);
|
||||
TeamStore.emitChange();
|
||||
|
||||
// If the old team id is null then we will already have the direct
|
||||
// profiles from initial load
|
||||
if (oldTeamId != null) {
|
||||
AsyncClient.getDirectProfiles();
|
||||
}
|
||||
|
||||
var d1 = $.Deferred(); //eslint-disable-line new-cap
|
||||
var d2 = $.Deferred(); //eslint-disable-line new-cap
|
||||
var d3 = $.Deferred(); //eslint-disable-line new-cap
|
||||
|
||||
Client.getChannels(
|
||||
(data) => {
|
||||
@@ -96,38 +87,7 @@ function preNeedsTeam(nextState, replace, callback) {
|
||||
}
|
||||
);
|
||||
|
||||
Client.getProfiles(
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES,
|
||||
profiles: data
|
||||
});
|
||||
|
||||
d2.resolve();
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getProfiles');
|
||||
d2.resolve();
|
||||
}
|
||||
);
|
||||
|
||||
Client.getTeamMembers(
|
||||
TeamStore.getCurrentId(),
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_MEMBERS_FOR_TEAM,
|
||||
team_members: data
|
||||
});
|
||||
|
||||
d3.resolve();
|
||||
},
|
||||
(err) => {
|
||||
AsyncClient.dispatchError(err, 'getTeamMembers');
|
||||
d3.resolve();
|
||||
}
|
||||
);
|
||||
|
||||
$.when(d1, d2, d3).done(() => {
|
||||
$.when(d1).done(() => {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
&.more-channel__modal {
|
||||
.modal-body {
|
||||
overflow-x: hidden;
|
||||
padding: 10px 0 20px;
|
||||
padding: 10px 0 15px;
|
||||
}
|
||||
|
||||
.channel-count {
|
||||
@@ -457,12 +457,12 @@
|
||||
|
||||
.modal-body {
|
||||
overflow-x: hidden;
|
||||
padding: 10px 0 20px;
|
||||
padding: 10px 0 15px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
@include clearfix;
|
||||
margin: 10px 0;
|
||||
margin: 5px 0 10px;
|
||||
}
|
||||
|
||||
.member-count {
|
||||
@@ -602,9 +602,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.member-select__container {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
}
|
||||
|
||||
.filtered-user-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.filter-row {
|
||||
flex-grow: 0;
|
||||
@@ -615,4 +622,20 @@
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.filter-controls {
|
||||
@include clearfix;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
padding: 1em 1.5em 0;
|
||||
|
||||
.filter-control__next {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-button {
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,13 @@
|
||||
}
|
||||
|
||||
.member-select__container {
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.user-popover {
|
||||
@@ -844,6 +850,10 @@
|
||||
@include translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
.nav-pills__container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding-bottom: 70px;
|
||||
}
|
||||
|
||||
@@ -239,6 +239,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.more-modal__list {
|
||||
.filtered-user-list {
|
||||
.filter-controls {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
margin: 10px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.member-list-holder {
|
||||
background: $white;
|
||||
margin-bottom: 4em;
|
||||
@@ -451,4 +464,3 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const NotificationPrefs = Constants.NotificationPrefs;
|
||||
const CHANGE_EVENT = 'change';
|
||||
const LEAVE_EVENT = 'leave';
|
||||
const MORE_CHANGE_EVENT = 'change';
|
||||
const EXTRA_INFO_EVENT = 'extra_info';
|
||||
const STATS_EVENT = 'stats';
|
||||
const LAST_VIEVED_EVENT = 'last_viewed';
|
||||
|
||||
class ChannelStoreClass extends EventEmitter {
|
||||
@@ -21,41 +21,13 @@ class ChannelStoreClass extends EventEmitter {
|
||||
|
||||
this.setMaxListeners(15);
|
||||
|
||||
this.emitChange = this.emitChange.bind(this);
|
||||
this.addChangeListener = this.addChangeListener.bind(this);
|
||||
this.removeChangeListener = this.removeChangeListener.bind(this);
|
||||
this.emitMoreChange = this.emitMoreChange.bind(this);
|
||||
this.addMoreChangeListener = this.addMoreChangeListener.bind(this);
|
||||
this.removeMoreChangeListener = this.removeMoreChangeListener.bind(this);
|
||||
this.emitExtraInfoChange = this.emitExtraInfoChange.bind(this);
|
||||
this.addExtraInfoChangeListener = this.addExtraInfoChangeListener.bind(this);
|
||||
this.removeExtraInfoChangeListener = this.removeExtraInfoChangeListener.bind(this);
|
||||
this.emitLeave = this.emitLeave.bind(this);
|
||||
this.addLeaveListener = this.addLeaveListener.bind(this);
|
||||
this.removeLeaveListener = this.removeLeaveListener.bind(this);
|
||||
this.emitLastViewed = this.emitLastViewed.bind(this);
|
||||
this.addLastViewedListener = this.addLastViewedListener.bind(this);
|
||||
this.removeLastViewedListener = this.removeLastViewedListener.bind(this);
|
||||
this.findFirstBy = this.findFirstBy.bind(this);
|
||||
this.get = this.get.bind(this);
|
||||
this.getMember = this.getMember.bind(this);
|
||||
this.getByName = this.getByName.bind(this);
|
||||
this.getByDisplayName = this.getByDisplayName.bind(this);
|
||||
this.setPostMode = this.setPostMode.bind(this);
|
||||
this.getPostMode = this.getPostMode.bind(this);
|
||||
this.setUnreadCount = this.setUnreadCount.bind(this);
|
||||
this.setUnreadCounts = this.setUnreadCounts.bind(this);
|
||||
this.getUnreadCount = this.getUnreadCount.bind(this);
|
||||
this.getUnreadCounts = this.getUnreadCounts.bind(this);
|
||||
this.getChannelNamesMap = this.getChannelNamesMap.bind(this);
|
||||
|
||||
this.currentId = null;
|
||||
this.postMode = this.POST_MODE_CHANNEL;
|
||||
this.channels = [];
|
||||
this.channelMembers = {};
|
||||
this.myChannelMembers = {};
|
||||
this.moreChannels = {};
|
||||
this.moreChannels.loading = true;
|
||||
this.extraInfos = {};
|
||||
this.stats = {};
|
||||
this.unreadCounts = {};
|
||||
}
|
||||
|
||||
@@ -91,16 +63,16 @@ class ChannelStoreClass extends EventEmitter {
|
||||
this.removeListener(MORE_CHANGE_EVENT, callback);
|
||||
}
|
||||
|
||||
emitExtraInfoChange() {
|
||||
this.emit(EXTRA_INFO_EVENT);
|
||||
emitStatsChange() {
|
||||
this.emit(STATS_EVENT);
|
||||
}
|
||||
|
||||
addExtraInfoChangeListener(callback) {
|
||||
this.on(EXTRA_INFO_EVENT, callback);
|
||||
addStatsChangeListener(callback) {
|
||||
this.on(STATS_EVENT, callback);
|
||||
}
|
||||
|
||||
removeExtraInfoChangeListener(callback) {
|
||||
this.removeListener(EXTRA_INFO_EVENT, callback);
|
||||
removeStatsChangeListener(callback) {
|
||||
this.removeListener(STATS_EVENT, callback);
|
||||
}
|
||||
emitLeave(id) {
|
||||
this.emit(LEAVE_EVENT, id);
|
||||
@@ -148,8 +120,8 @@ class ChannelStoreClass extends EventEmitter {
|
||||
return this.findFirstBy('id', id);
|
||||
}
|
||||
|
||||
getMember(id) {
|
||||
return this.getAllMembers()[id];
|
||||
getMyMember(id) {
|
||||
return this.getMyMembers()[id];
|
||||
}
|
||||
|
||||
getByName(name) {
|
||||
@@ -168,10 +140,6 @@ class ChannelStoreClass extends EventEmitter {
|
||||
return this.getChannels();
|
||||
}
|
||||
|
||||
getAllMembers() {
|
||||
return this.getChannelMembers();
|
||||
}
|
||||
|
||||
getMoreAll() {
|
||||
return this.getMoreChannels();
|
||||
}
|
||||
@@ -181,7 +149,7 @@ class ChannelStoreClass extends EventEmitter {
|
||||
}
|
||||
|
||||
resetCounts(id) {
|
||||
const cm = this.channelMembers;
|
||||
const cm = this.myChannelMembers;
|
||||
for (var cmid in cm) {
|
||||
if (cm[cmid].channel_id === id) {
|
||||
var c = this.get(id);
|
||||
@@ -213,41 +181,34 @@ class ChannelStoreClass extends EventEmitter {
|
||||
var currentId = this.getCurrentId();
|
||||
|
||||
if (currentId) {
|
||||
return this.getAllMembers()[currentId];
|
||||
return this.getMyMembers()[currentId];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
setChannelMember(member) {
|
||||
var members = this.getChannelMembers();
|
||||
members[member.channel_id] = member;
|
||||
this.storeChannelMembers(members);
|
||||
this.emitChange();
|
||||
getCurrentStats() {
|
||||
return this.getStats(this.getCurrentId());
|
||||
}
|
||||
|
||||
getCurrentExtraInfo() {
|
||||
return this.getExtraInfo(this.getCurrentId());
|
||||
}
|
||||
|
||||
getExtraInfo(channelId) {
|
||||
var extra = null;
|
||||
getStats(channelId) {
|
||||
let stats;
|
||||
|
||||
if (channelId) {
|
||||
extra = this.getExtraInfos()[channelId];
|
||||
stats = this.stats[channelId];
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
if (stats) {
|
||||
// create a defensive copy
|
||||
extra = JSON.parse(JSON.stringify(extra));
|
||||
stats = Object.assign({}, stats);
|
||||
} else {
|
||||
extra = {members: []};
|
||||
stats = {member_count: 0};
|
||||
}
|
||||
|
||||
return extra;
|
||||
return stats;
|
||||
}
|
||||
|
||||
pStoreChannel(channel) {
|
||||
storeChannel(channel) {
|
||||
var channels = this.getChannels();
|
||||
var found;
|
||||
|
||||
@@ -279,18 +240,18 @@ class ChannelStoreClass extends EventEmitter {
|
||||
return this.channels;
|
||||
}
|
||||
|
||||
pStoreChannelMember(channelMember) {
|
||||
var members = this.getChannelMembers();
|
||||
storeMyChannelMember(channelMember) {
|
||||
const members = Object.assign({}, this.getMyMembers());
|
||||
members[channelMember.channel_id] = channelMember;
|
||||
this.storeChannelMembers(members);
|
||||
this.storeMyChannelMembers(members);
|
||||
}
|
||||
|
||||
storeChannelMembers(channelMembers) {
|
||||
this.channelMembers = channelMembers;
|
||||
storeMyChannelMembers(channelMembers) {
|
||||
this.myChannelMembers = channelMembers;
|
||||
}
|
||||
|
||||
getChannelMembers() {
|
||||
return this.channelMembers;
|
||||
getMyMembers() {
|
||||
return this.myChannelMembers;
|
||||
}
|
||||
|
||||
storeMoreChannels(channels) {
|
||||
@@ -301,12 +262,8 @@ class ChannelStoreClass extends EventEmitter {
|
||||
return this.moreChannels;
|
||||
}
|
||||
|
||||
storeExtraInfos(extraInfos) {
|
||||
this.extraInfos = extraInfos;
|
||||
}
|
||||
|
||||
getExtraInfos() {
|
||||
return this.extraInfos;
|
||||
storeStats(stats) {
|
||||
this.stats = stats;
|
||||
}
|
||||
|
||||
isDefault(channel) {
|
||||
@@ -323,7 +280,7 @@ class ChannelStoreClass extends EventEmitter {
|
||||
|
||||
setUnreadCount(id) {
|
||||
const ch = this.get(id);
|
||||
const chMember = this.getMember(id);
|
||||
const chMember = this.getMyMember(id);
|
||||
|
||||
const chMentionCount = chMember.mention_count;
|
||||
let chUnreadCount = ch.total_msg_count - chMember.msg_count;
|
||||
@@ -351,7 +308,7 @@ class ChannelStoreClass extends EventEmitter {
|
||||
}
|
||||
|
||||
leaveChannel(id) {
|
||||
Reflect.deleteProperty(this.channelMembers, id);
|
||||
Reflect.deleteProperty(this.myChannelMembers, id);
|
||||
const element = this.channels.indexOf(id);
|
||||
if (element > -1) {
|
||||
this.channels.splice(element, 1);
|
||||
@@ -405,7 +362,7 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||
|
||||
case ActionTypes.RECEIVED_CHANNELS:
|
||||
ChannelStore.storeChannels(action.channels);
|
||||
ChannelStore.storeChannelMembers(action.members);
|
||||
ChannelStore.storeMyChannelMembers(action.members);
|
||||
currentId = ChannelStore.getCurrentId();
|
||||
if (currentId && window.isActive) {
|
||||
ChannelStore.resetCounts(currentId);
|
||||
@@ -415,9 +372,9 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||
break;
|
||||
|
||||
case ActionTypes.RECEIVED_CHANNEL:
|
||||
ChannelStore.pStoreChannel(action.channel);
|
||||
ChannelStore.storeChannel(action.channel);
|
||||
if (action.member) {
|
||||
ChannelStore.pStoreChannelMember(action.member);
|
||||
ChannelStore.storeMyChannelMember(action.member);
|
||||
}
|
||||
currentId = ChannelStore.getCurrentId();
|
||||
if (currentId && window.isActive) {
|
||||
@@ -432,11 +389,11 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||
ChannelStore.emitMoreChange();
|
||||
break;
|
||||
|
||||
case ActionTypes.RECEIVED_CHANNEL_EXTRA_INFO:
|
||||
var extraInfos = ChannelStore.getExtraInfos();
|
||||
extraInfos[action.extra_info.id] = action.extra_info;
|
||||
ChannelStore.storeExtraInfos(extraInfos);
|
||||
ChannelStore.emitExtraInfoChange();
|
||||
case ActionTypes.RECEIVED_CHANNEL_STATS:
|
||||
var stats = Object.assign({}, ChannelStore.getStats());
|
||||
stats[action.stats.channel_id] = action.stats;
|
||||
ChannelStore.storeStats(stats);
|
||||
ChannelStore.emitStatsChange();
|
||||
break;
|
||||
|
||||
case ActionTypes.LEAVE_CHANNEL:
|
||||
|
||||
@@ -44,7 +44,7 @@ class NotificationStoreClass extends EventEmitter {
|
||||
|
||||
const channel = ChannelStore.get(post.channel_id);
|
||||
const user = UserStore.getCurrentUser();
|
||||
const member = ChannelStore.getMember(post.channel_id);
|
||||
const member = ChannelStore.getMyMember(post.channel_id);
|
||||
|
||||
let notifyLevel = member && member.notify_props ? member.notify_props.desktop : 'default';
|
||||
if (notifyLevel === 'default') {
|
||||
|
||||
@@ -178,15 +178,15 @@ class PostStoreClass extends EventEmitter {
|
||||
}
|
||||
|
||||
// Returns true if posts need to be fetched
|
||||
requestVisibilityIncrease(id, ammount) {
|
||||
requestVisibilityIncrease(id, amount) {
|
||||
const endVisible = this.postsInfo[id].endVisible;
|
||||
const postList = this.postsInfo[id].postList;
|
||||
if (this.getVisibilityAtTop(id)) {
|
||||
return false;
|
||||
}
|
||||
this.postsInfo[id].endVisible += ammount;
|
||||
this.postsInfo[id].endVisible += amount;
|
||||
this.emitChange();
|
||||
return endVisible + ammount > postList.order.length;
|
||||
return endVisible + amount > postList.order.length;
|
||||
}
|
||||
|
||||
getFocusedPostId() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
@@ -222,7 +222,9 @@ class SuggestionStore extends EventEmitter {
|
||||
|
||||
switch (type) {
|
||||
case ActionTypes.SUGGESTION_PRETEXT_CHANGED:
|
||||
this.clearSuggestions(id);
|
||||
if (other.pretext === '') {
|
||||
this.clearSuggestions(id);
|
||||
}
|
||||
|
||||
this.setPretext(id, other.pretext);
|
||||
this.emitPretextChanged(id, other.pretext);
|
||||
@@ -231,6 +233,8 @@ class SuggestionStore extends EventEmitter {
|
||||
this.emitSuggestionsChanged(id);
|
||||
break;
|
||||
case ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS:
|
||||
this.clearSuggestions(id);
|
||||
|
||||
// ensure the matched pretext hasn't changed so that we don't receive suggestions for outdated pretext
|
||||
this.addSuggestions(id, other.terms, other.items, other.component, other.matchedPretext);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
|
||||
const CHANGE_EVENT = 'change';
|
||||
const STATS_EVENT = 'stats';
|
||||
|
||||
var Utils;
|
||||
|
||||
@@ -20,8 +21,10 @@ class TeamStoreClass extends EventEmitter {
|
||||
|
||||
clear() {
|
||||
this.teams = {};
|
||||
this.team_members = [];
|
||||
this.members_for_team = [];
|
||||
this.my_team_members = [];
|
||||
this.members_in_team = {};
|
||||
this.members_not_in_team = {};
|
||||
this.stats = {};
|
||||
this.teamListings = {};
|
||||
this.currentTeamId = '';
|
||||
}
|
||||
@@ -38,6 +41,18 @@ class TeamStoreClass extends EventEmitter {
|
||||
this.removeListener(CHANGE_EVENT, callback);
|
||||
}
|
||||
|
||||
emitStatsChange() {
|
||||
this.emit(STATS_EVENT);
|
||||
}
|
||||
|
||||
addStatsChangeListener(callback) {
|
||||
this.on(STATS_EVENT, callback);
|
||||
}
|
||||
|
||||
removeStatsChangeListener(callback) {
|
||||
this.removeListener(STATS_EVENT, callback);
|
||||
}
|
||||
|
||||
get(id) {
|
||||
var c = this.getAll();
|
||||
return c[id];
|
||||
@@ -114,6 +129,27 @@ class TeamStoreClass extends EventEmitter {
|
||||
return origin + '/' + team.name;
|
||||
}
|
||||
|
||||
getCurrentStats() {
|
||||
return this.getStats(this.getCurrentId());
|
||||
}
|
||||
|
||||
getStats(teamId) {
|
||||
let stats;
|
||||
|
||||
if (teamId) {
|
||||
stats = this.stats[teamId];
|
||||
}
|
||||
|
||||
if (stats) {
|
||||
// create a defensive copy
|
||||
stats = Object.assign({}, stats);
|
||||
} else {
|
||||
stats = {member_count: 0};
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
saveTeam(team) {
|
||||
this.teams[team.id] = team;
|
||||
}
|
||||
@@ -127,44 +163,62 @@ class TeamStoreClass extends EventEmitter {
|
||||
this.currentTeamId = team.id;
|
||||
}
|
||||
|
||||
saveTeamMembers(members) {
|
||||
this.team_members = members;
|
||||
saveStats(teamId, stats) {
|
||||
this.stats[teamId] = stats;
|
||||
}
|
||||
|
||||
appendTeamMember(member) {
|
||||
this.team_members.push(member);
|
||||
saveMyTeamMembers(members) {
|
||||
this.my_team_members = members;
|
||||
}
|
||||
|
||||
removeTeamMember(teamId) {
|
||||
for (var index in this.team_members) {
|
||||
if (this.team_members.hasOwnProperty(index)) {
|
||||
if (this.team_members[index].team_id === teamId) {
|
||||
this.team_members.splice(index, 1);
|
||||
appendMyTeamMember(member) {
|
||||
this.my_team_members.push(member);
|
||||
}
|
||||
|
||||
removeMyTeamMember(teamId) {
|
||||
for (var index in this.my_team_members) {
|
||||
if (this.my_team_members.hasOwnProperty(index)) {
|
||||
if (this.my_team_members[index].team_id === teamId) {
|
||||
Reflect.deleteProperty(this.my_team_members, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTeamMembers() {
|
||||
return this.team_members;
|
||||
getMyTeamMembers() {
|
||||
return this.my_team_members;
|
||||
}
|
||||
|
||||
saveMembersForTeam(members) {
|
||||
this.members_for_team = members;
|
||||
saveMembersInTeam(teamId = this.getCurrentId(), members) {
|
||||
const oldMembers = this.members_in_team[teamId] || {};
|
||||
this.members_in_team[teamId] = Object.assign({}, oldMembers, members);
|
||||
}
|
||||
|
||||
getMembersForTeam() {
|
||||
return this.members_for_team;
|
||||
saveMembersNotInTeam(teamId = this.getCurrentId(), nonmembers) {
|
||||
this.members_not_in_team[teamId] = nonmembers;
|
||||
}
|
||||
|
||||
hasActiveMemberForTeam(userId) {
|
||||
for (var index in this.members_for_team) {
|
||||
if (this.members_for_team.hasOwnProperty(index)) {
|
||||
if (this.members_for_team[index].user_id === userId &&
|
||||
this.members_for_team[index].team_id === this.currentTeamId) {
|
||||
return this.members_for_team[index].delete_at === 0;
|
||||
}
|
||||
}
|
||||
removeMemberInTeam(teamId = this.getCurrentId(), userId) {
|
||||
if (this.members_in_team[teamId]) {
|
||||
Reflect.deleteProperty(this.members_in_team[teamId], userId);
|
||||
}
|
||||
}
|
||||
|
||||
getMembersInTeam(teamId = this.getCurrentId()) {
|
||||
return this.members_in_team[teamId] || {};
|
||||
}
|
||||
|
||||
hasActiveMemberInTeam(teamId = this.getCurrentId(), userId) {
|
||||
if (this.members_in_team[teamId] && this.members_in_team[teamId][userId]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
hasMemberNotInTeam(teamId = this.getCurrentId(), userId) {
|
||||
if (this.members_not_in_team[teamId] && this.members_not_in_team[teamId][userId]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -187,7 +241,7 @@ class TeamStoreClass extends EventEmitter {
|
||||
Utils = require('utils/utils.jsx'); //eslint-disable-line global-require
|
||||
}
|
||||
|
||||
var teamMembers = this.getTeamMembers();
|
||||
var teamMembers = this.getMyTeamMembers();
|
||||
const teamMember = teamMembers.find((m) => m.user_id === userId && m.team_id === teamId);
|
||||
|
||||
if (teamMember) {
|
||||
@@ -210,25 +264,32 @@ TeamStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||
break;
|
||||
case ActionTypes.CREATED_TEAM:
|
||||
TeamStore.saveTeam(action.team);
|
||||
TeamStore.appendTeamMember(action.member);
|
||||
TeamStore.appendMyTeamMember(action.member);
|
||||
TeamStore.emitChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_ALL_TEAMS:
|
||||
TeamStore.saveTeams(action.teams);
|
||||
TeamStore.emitChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_TEAM_MEMBERS:
|
||||
TeamStore.saveTeamMembers(action.team_members);
|
||||
case ActionTypes.RECEIVED_MY_TEAM_MEMBERS:
|
||||
TeamStore.saveMyTeamMembers(action.team_members);
|
||||
TeamStore.emitChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_ALL_TEAM_LISTINGS:
|
||||
TeamStore.saveTeamListings(action.teams);
|
||||
TeamStore.emitChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_MEMBERS_FOR_TEAM:
|
||||
TeamStore.saveMembersForTeam(action.team_members);
|
||||
case ActionTypes.RECEIVED_MEMBERS_IN_TEAM:
|
||||
TeamStore.saveMembersInTeam(action.team_id, action.team_members);
|
||||
if (action.non_team_members) {
|
||||
TeamStore.saveMembersNotInTeam(action.team_id, action.non_team_members);
|
||||
}
|
||||
TeamStore.emitChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_TEAM_STATS:
|
||||
TeamStore.saveStats(action.team_id, action.stats);
|
||||
TeamStore.emitStatsChange();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,12 +6,16 @@ import EventEmitter from 'events';
|
||||
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import LocalizationStore from './localization_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
const UserStatuses = Constants.UserStatuses;
|
||||
|
||||
const CHANGE_EVENT_DM_LIST = 'change_dm_list';
|
||||
const CHANGE_EVENT_NOT_IN_CHANNEL = 'change_not_in_channel';
|
||||
const CHANGE_EVENT_IN_CHANNEL = 'change_in_channel';
|
||||
const CHANGE_EVENT_IN_TEAM = 'change_in_team';
|
||||
const CHANGE_EVENT = 'change';
|
||||
const CHANGE_EVENT_SESSIONS = 'change_sessions';
|
||||
const CHANGE_EVENT_AUDITS = 'change_audits';
|
||||
@@ -26,9 +30,26 @@ class UserStoreClass extends EventEmitter {
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.profiles_for_dm_list = {};
|
||||
// All the profiles, regardless of where they came from
|
||||
this.profiles = {};
|
||||
this.direct_profiles = {};
|
||||
this.paging_offset = 0;
|
||||
this.paging_count = 0;
|
||||
|
||||
// Lists of sorted IDs for users in a team
|
||||
this.profiles_in_team = {};
|
||||
this.in_team_offset = 0;
|
||||
this.in_team_count = 0;
|
||||
|
||||
// Lists of sorted IDs for users in a channel
|
||||
this.profiles_in_channel = {};
|
||||
this.in_channel_offset = {};
|
||||
this.in_channel_count = {};
|
||||
|
||||
// Lists of sorted IDs for users not in a channel
|
||||
this.profiles_not_in_channel = {};
|
||||
this.not_in_channel_offset = {};
|
||||
this.not_in_channel_count = {};
|
||||
|
||||
this.statuses = {};
|
||||
this.sessions = {};
|
||||
this.audits = {};
|
||||
@@ -48,16 +69,40 @@ class UserStoreClass extends EventEmitter {
|
||||
this.removeListener(CHANGE_EVENT, callback);
|
||||
}
|
||||
|
||||
emitDmListChange() {
|
||||
this.emit(CHANGE_EVENT_DM_LIST);
|
||||
emitInTeamChange() {
|
||||
this.emit(CHANGE_EVENT_IN_TEAM);
|
||||
}
|
||||
|
||||
addDmListChangeListener(callback) {
|
||||
this.on(CHANGE_EVENT_DM_LIST, callback);
|
||||
addInTeamChangeListener(callback) {
|
||||
this.on(CHANGE_EVENT_IN_TEAM, callback);
|
||||
}
|
||||
|
||||
removeDmListChangeListener(callback) {
|
||||
this.removeListener(CHANGE_EVENT_DM_LIST, callback);
|
||||
removeInTeamChangeListener(callback) {
|
||||
this.removeListener(CHANGE_EVENT_IN_TEAM, callback);
|
||||
}
|
||||
|
||||
emitInChannelChange() {
|
||||
this.emit(CHANGE_EVENT_IN_CHANNEL);
|
||||
}
|
||||
|
||||
addInChannelChangeListener(callback) {
|
||||
this.on(CHANGE_EVENT_IN_CHANNEL, callback);
|
||||
}
|
||||
|
||||
removeInChannelChangeListener(callback) {
|
||||
this.removeListener(CHANGE_EVENT_IN_CHANNEL, callback);
|
||||
}
|
||||
|
||||
emitNotInChannelChange() {
|
||||
this.emit(CHANGE_EVENT_NOT_IN_CHANNEL);
|
||||
}
|
||||
|
||||
addNotInChannelChangeListener(callback) {
|
||||
this.on(CHANGE_EVENT_NOT_IN_CHANNEL, callback);
|
||||
}
|
||||
|
||||
removeNotInChannelChangeListener(callback) {
|
||||
this.removeListener(CHANGE_EVENT_NOT_IN_CHANNEL, callback);
|
||||
}
|
||||
|
||||
emitSessionsChange() {
|
||||
@@ -96,6 +141,8 @@ class UserStoreClass extends EventEmitter {
|
||||
this.removeListener(CHANGE_EVENT_STATUSES, callback);
|
||||
}
|
||||
|
||||
// General
|
||||
|
||||
getCurrentUser() {
|
||||
return this.getProfiles()[this.currentUserId];
|
||||
}
|
||||
@@ -119,29 +166,30 @@ class UserStoreClass extends EventEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
hasProfile(userId) {
|
||||
return this.getProfile(userId) != null;
|
||||
// System-Wide Profiles
|
||||
|
||||
saveProfiles(profiles) {
|
||||
const currentId = this.getCurrentId();
|
||||
if (profiles[currentId]) {
|
||||
Reflect.deleteProperty(profiles, currentId);
|
||||
}
|
||||
this.profiles = Object.assign({}, this.profiles, profiles);
|
||||
}
|
||||
|
||||
hasTeamProfile(userId) {
|
||||
return this.getProfiles()[userId];
|
||||
}
|
||||
|
||||
hasDirectProfile(userId) {
|
||||
return this.getDirectProfiles()[userId];
|
||||
getProfiles() {
|
||||
return this.profiles;
|
||||
}
|
||||
|
||||
getProfile(userId) {
|
||||
if (userId === this.getCurrentId()) {
|
||||
return this.getCurrentUser();
|
||||
if (this.profiles[userId]) {
|
||||
return Object.assign({}, this.profiles[userId]);
|
||||
}
|
||||
|
||||
const user = this.getProfiles()[userId];
|
||||
if (user) {
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getDirectProfiles()[userId];
|
||||
hasProfile(userId) {
|
||||
return this.getProfile(userId) != null;
|
||||
}
|
||||
|
||||
getProfileByUsername(username) {
|
||||
@@ -162,22 +210,6 @@ class UserStoreClass extends EventEmitter {
|
||||
return profileUsernameMap;
|
||||
}
|
||||
|
||||
getDirectProfiles() {
|
||||
return this.direct_profiles;
|
||||
}
|
||||
|
||||
saveDirectProfile(profile) {
|
||||
this.direct_profiles[profile.id] = profile;
|
||||
}
|
||||
|
||||
saveDirectProfiles(profiles) {
|
||||
this.direct_profiles = profiles;
|
||||
}
|
||||
|
||||
getProfiles() {
|
||||
return this.profiles;
|
||||
}
|
||||
|
||||
getActiveOnlyProfiles(skipCurrent) {
|
||||
const active = {};
|
||||
const profiles = this.getProfiles();
|
||||
@@ -195,14 +227,54 @@ class UserStoreClass extends EventEmitter {
|
||||
getActiveOnlyProfileList() {
|
||||
const profileMap = this.getActiveOnlyProfiles();
|
||||
const profiles = [];
|
||||
const currentId = this.getCurrentId();
|
||||
|
||||
for (const id in profileMap) {
|
||||
if (profileMap.hasOwnProperty(id) && id !== currentId) {
|
||||
if (profileMap.hasOwnProperty(id)) {
|
||||
profiles.push(profileMap[id]);
|
||||
}
|
||||
}
|
||||
|
||||
profiles.sort((a, b) => {
|
||||
if (a.username < b.username) {
|
||||
return -1;
|
||||
}
|
||||
if (a.username > b.username) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
getProfileList(skipCurrent) {
|
||||
const profiles = [];
|
||||
const currentId = this.getCurrentId();
|
||||
|
||||
for (const id in this.profiles) {
|
||||
if (this.profiles.hasOwnProperty(id)) {
|
||||
var profile = this.profiles[id];
|
||||
|
||||
if (skipCurrent && id === currentId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (profile.delete_at === 0) {
|
||||
profiles.push(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profiles.sort((a, b) => {
|
||||
if (a.username < b.username) {
|
||||
return -1;
|
||||
}
|
||||
if (a.username > b.username) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
@@ -210,44 +282,194 @@ class UserStoreClass extends EventEmitter {
|
||||
this.profiles[profile.id] = profile;
|
||||
}
|
||||
|
||||
saveProfiles(profiles) {
|
||||
const currentId = this.getCurrentId();
|
||||
const currentUser = this.profiles[currentId];
|
||||
if (currentUser) {
|
||||
if (currentId in this.profiles) {
|
||||
Reflect.deleteProperty(this.profiles, currentId);
|
||||
}
|
||||
// Team-Wide Profiles
|
||||
|
||||
this.profiles = profiles;
|
||||
this.profiles[currentId] = currentUser;
|
||||
} else {
|
||||
this.profiles = profiles;
|
||||
saveProfilesInTeam(teamId, profiles) {
|
||||
const oldProfileList = this.profiles_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_in_team[teamId] = newProfileList;
|
||||
this.saveProfiles(profiles);
|
||||
}
|
||||
|
||||
getProfilesForDmList() {
|
||||
const currentId = this.getCurrentId();
|
||||
getProfileListInTeam(teamId = TeamStore.getCurrentId(), skipCurrent) {
|
||||
const userIds = this.profiles_in_team[teamId] || [];
|
||||
const profiles = [];
|
||||
const currentId = this.getCurrentId();
|
||||
|
||||
for (const id in this.profiles_for_dm_list) {
|
||||
if (this.profiles_for_dm_list.hasOwnProperty(id) && id !== currentId) {
|
||||
var profile = this.profiles_for_dm_list[id];
|
||||
for (let i = 0; i < userIds.length; i++) {
|
||||
const profile = this.getProfile(userIds[i]);
|
||||
|
||||
if (profile.delete_at === 0) {
|
||||
profiles.push(profile);
|
||||
}
|
||||
if (skipCurrent && profile.id === currentId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (profile) {
|
||||
profiles.push(profile);
|
||||
}
|
||||
}
|
||||
|
||||
profiles.sort((a, b) => a.username.localeCompare(b.username));
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
saveProfilesForDmList(profiles) {
|
||||
this.profiles_for_dm_list = profiles;
|
||||
// Channel-Wide Profiles
|
||||
|
||||
saveProfilesInChannel(channelId = ChannelStore.getCurrentId(), profiles) {
|
||||
const oldProfileList = this.profiles_in_channel[channelId] || [];
|
||||
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_in_channel[channelId] = newProfileList;
|
||||
this.saveProfiles(profiles);
|
||||
}
|
||||
|
||||
saveProfileInChannel(channelId = ChannelStore.getCurrentId(), profile) {
|
||||
const profileMap = {};
|
||||
profileMap[profile.id] = profile;
|
||||
this.saveProfilesInChannel(channelId, profileMap);
|
||||
}
|
||||
|
||||
saveUserIdInChannel(channelId = ChannelStore.getCurrentId(), userId) {
|
||||
const profile = this.getProfile(userId);
|
||||
|
||||
// Must have profile or we can't sort the list
|
||||
if (!profile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.saveProfileInChannel(channelId, profile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
removeProfileInChannel(channelId, userId) {
|
||||
const userIds = this.profiles_in_channel[channelId];
|
||||
if (!userIds) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = userIds.indexOf(userId);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
userIds.splice(index, 1);
|
||||
}
|
||||
|
||||
getProfileListInChannel(channelId = ChannelStore.getCurrentId()) {
|
||||
const userIds = this.profiles_in_channel[channelId] || [];
|
||||
const profiles = [];
|
||||
|
||||
for (let i = 0; i < userIds.length; i++) {
|
||||
const profile = this.getProfile(userIds[i]);
|
||||
if (profile) {
|
||||
profiles.push(profile);
|
||||
}
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
saveProfilesNotInChannel(channelId = ChannelStore.getCurrentId(), profiles) {
|
||||
const oldProfileList = this.profiles_not_in_channel[channelId] || [];
|
||||
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_channel[channelId] = newProfileList;
|
||||
this.saveProfiles(profiles);
|
||||
}
|
||||
|
||||
saveProfileNotInChannel(channelId = ChannelStore.getCurrentId(), profile) {
|
||||
const profileMap = {};
|
||||
profileMap[profile.id] = profile;
|
||||
this.saveProfilesNotInChannel(channelId, profileMap);
|
||||
}
|
||||
|
||||
removeProfileNotInChannel(channelId, userId) {
|
||||
const userIds = this.profiles_not_in_channel[channelId];
|
||||
if (!userIds) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = userIds.indexOf(userId);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
userIds.splice(index, 1);
|
||||
}
|
||||
|
||||
getProfileListNotInChannel(channelId = ChannelStore.getCurrentId()) {
|
||||
const userIds = this.profiles_not_in_channel[channelId] || [];
|
||||
const profiles = [];
|
||||
|
||||
for (let i = 0; i < userIds.length; i++) {
|
||||
const profile = this.getProfile(userIds[i]);
|
||||
if (profile) {
|
||||
profiles.push(profile);
|
||||
}
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
// Other
|
||||
|
||||
setSessions(sessions) {
|
||||
this.sessions = sessions;
|
||||
}
|
||||
@@ -331,6 +553,58 @@ class UserStoreClass extends EventEmitter {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setPage(offset, count) {
|
||||
this.paging_offset = offset + count;
|
||||
this.paging_count = this.paging_count + count;
|
||||
}
|
||||
|
||||
getPagingOffset() {
|
||||
return this.paging_offset;
|
||||
}
|
||||
|
||||
getPagingCount() {
|
||||
return this.paging_count;
|
||||
}
|
||||
|
||||
setInTeamPage(offset, count) {
|
||||
this.in_team_offset = offset + count;
|
||||
this.in_team_count = this.in_team_count + count;
|
||||
}
|
||||
|
||||
getInTeamPagingOffset() {
|
||||
return this.in_team_offset;
|
||||
}
|
||||
|
||||
getInTeamPagingCount() {
|
||||
return this.in_team_count;
|
||||
}
|
||||
|
||||
setInChannelPage(channelId, offset, count) {
|
||||
this.in_channel_offset[channelId] = offset + count;
|
||||
this.in_channel_count[channelId] = this.dm_paging_count + count;
|
||||
}
|
||||
|
||||
getInChannelPagingOffset(channelId) {
|
||||
return this.in_channel_offset[channelId] | 0;
|
||||
}
|
||||
|
||||
getInChannelPagingCount(channelId) {
|
||||
return this.in_channel_count[channelId] | 0;
|
||||
}
|
||||
|
||||
setNotInChannelPage(channelId, offset, count) {
|
||||
this.not_in_channel_offset[channelId] = offset + count;
|
||||
this.not_in_channel_count[channelId] = this.dm_paging_count + count;
|
||||
}
|
||||
|
||||
getNotInChannelPagingOffset(channelId) {
|
||||
return this.not_in_channel_offset[channelId] | 0;
|
||||
}
|
||||
|
||||
getNotInChannelPagingCount(channelId) {
|
||||
return this.not_in_channel_count[channelId] | 0;
|
||||
}
|
||||
}
|
||||
|
||||
var UserStore = new UserStoreClass();
|
||||
@@ -340,16 +614,36 @@ UserStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||
var action = payload.action;
|
||||
|
||||
switch (action.type) {
|
||||
case ActionTypes.RECEIVED_PROFILES_FOR_DM_LIST:
|
||||
UserStore.saveProfilesForDmList(action.profiles);
|
||||
UserStore.emitDmListChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_PROFILES:
|
||||
UserStore.saveProfiles(action.profiles);
|
||||
if (action.offset != null && action.count != null) {
|
||||
UserStore.setPage(action.offset, action.count);
|
||||
}
|
||||
UserStore.emitChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_DIRECT_PROFILES:
|
||||
UserStore.saveDirectProfiles(action.profiles);
|
||||
case ActionTypes.RECEIVED_PROFILES_IN_TEAM:
|
||||
UserStore.saveProfilesInTeam(action.team_id, action.profiles);
|
||||
if (action.offset != null && action.count != null) {
|
||||
UserStore.setInTeamPage(action.offset, action.count);
|
||||
}
|
||||
UserStore.emitInTeamChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_PROFILES_IN_CHANNEL:
|
||||
UserStore.saveProfilesInChannel(action.channel_id, action.profiles);
|
||||
if (action.offset != null && action.count != null) {
|
||||
UserStore.setInChannelPage(action.offset, action.count);
|
||||
}
|
||||
UserStore.emitInChannelChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_PROFILES_NOT_IN_CHANNEL:
|
||||
UserStore.saveProfilesNotInChannel(action.channel_id, action.profiles);
|
||||
if (action.offset != null && action.count != null) {
|
||||
UserStore.setNotInChannelPage(action.offset, action.count);
|
||||
}
|
||||
UserStore.emitNotInChannelChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_PROFILE:
|
||||
UserStore.saveProfile(action.profile);
|
||||
UserStore.emitChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_ME:
|
||||
|
||||
@@ -285,11 +285,10 @@ describe('Client.Channels', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getChannelExtraInfo', function(done) {
|
||||
it('getChannelStats', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getChannelExtraInfo(
|
||||
TestHelper.basicClient().getChannelStats(
|
||||
TestHelper.basicChannel().id,
|
||||
5,
|
||||
function(data) {
|
||||
assert.equal(data.member_count, 1);
|
||||
done();
|
||||
@@ -301,6 +300,23 @@ describe('Client.Channels', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getChannelMember', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getChannelMember(
|
||||
TestHelper.basicChannel().id,
|
||||
TestHelper.basicUser().id,
|
||||
function(data) {
|
||||
assert.equal(data.channel_id, TestHelper.basicChannel().id);
|
||||
assert.equal(data.user_id, TestHelper.basicUser().id);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
/* TODO FIX THIS TEST
|
||||
it('addChannelMember', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
|
||||
@@ -130,10 +130,12 @@ describe('Client.Team', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('GetTeamMembers', function(done) {
|
||||
it('getTeamMembers', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getTeamMembers(
|
||||
TestHelper.basicTeam().id,
|
||||
0,
|
||||
100,
|
||||
function(data) {
|
||||
assert.equal(data.length > 0, true);
|
||||
done();
|
||||
@@ -145,6 +147,55 @@ describe('Client.Team', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getTeamMember', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getTeamMember(
|
||||
TestHelper.basicTeam().id,
|
||||
TestHelper.basicUser().id,
|
||||
function(data) {
|
||||
assert.equal(data.user_id, TestHelper.basicUser().id);
|
||||
assert.equal(data.team_id, TestHelper.basicTeam().id);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTeamStats', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getTeamStats(
|
||||
TestHelper.basicTeam().id,
|
||||
function(data) {
|
||||
assert.equal(data.member_count > 0, true);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTeamMembersByIds', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getTeamMembersByIds(
|
||||
TestHelper.basicTeam().id,
|
||||
[TestHelper.basicUser().id],
|
||||
function(data) {
|
||||
assert.equal(data[0].user_id, TestHelper.basicUser().id);
|
||||
assert.equal(data[0].team_id, TestHelper.basicTeam().id);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('inviteMembers', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
var data = {};
|
||||
|
||||
@@ -444,52 +444,11 @@ describe('Client.User', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getDirectProfiles', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getDirectProfiles(
|
||||
function(data) {
|
||||
assert.equal(Object.keys(data).length === 0, true);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.getDirectProfiles));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getProfiles', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getProfiles(
|
||||
function(data) {
|
||||
assert.equal(data[TestHelper.basicUser().id].id, TestHelper.basicUser().id);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getProfilesForTeam', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getProfilesForTeam(
|
||||
TestHelper.basicTeam().id,
|
||||
function(data) {
|
||||
assert.equal(data[TestHelper.basicUser().id].id, TestHelper.basicUser().id);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getProfilesForDirectMessageList', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getProfilesForDirectMessageList(
|
||||
0,
|
||||
100,
|
||||
function(data) {
|
||||
assert.equal(Object.keys(data).length > 0, true);
|
||||
done();
|
||||
@@ -501,16 +460,129 @@ describe('Client.User', function() {
|
||||
});
|
||||
});
|
||||
|
||||
/* TODO: FIX THIS TEST
|
||||
it('getStatuses', function(done) {
|
||||
it('getProfilesInTeam', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
var ids = [];
|
||||
ids.push(TestHelper.basicUser().id);
|
||||
|
||||
TestHelper.basicClient().getStatuses(
|
||||
ids,
|
||||
TestHelper.basicClient().getProfilesInTeam(
|
||||
TestHelper.basicTeam().id,
|
||||
0,
|
||||
100,
|
||||
function(data) {
|
||||
assert.equal(data[TestHelper.basicUser().id], 'online');
|
||||
assert.equal(data[TestHelper.basicUser().id].id, TestHelper.basicUser().id);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getProfilesByIds', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getProfilesByIds(
|
||||
[TestHelper.basicUser().id],
|
||||
function(data) {
|
||||
assert.equal(data[TestHelper.basicUser().id].id, TestHelper.basicUser().id);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getProfilesInChannel', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getProfilesInChannel(
|
||||
TestHelper.basicChannel().id,
|
||||
0,
|
||||
100,
|
||||
function(data) {
|
||||
assert.equal(Object.keys(data).length > 0, true);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getProfilesNotInChannel', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().getProfilesNotInChannel(
|
||||
TestHelper.basicChannel().id,
|
||||
0,
|
||||
100,
|
||||
function(data) {
|
||||
assert.equal(Object.keys(data).length > 0, false);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('searchUsers', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().searchUsers(
|
||||
'uid',
|
||||
TestHelper.basicTeam().id,
|
||||
{},
|
||||
function(data) {
|
||||
assert.equal(data.length > 0, true);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('autocompleteUsersInChannel', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().autocompleteUsersInChannel(
|
||||
'uid',
|
||||
TestHelper.basicChannel().id,
|
||||
function(data) {
|
||||
assert.equal(data != null, true);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('autocompleteUsersInTeam', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
TestHelper.basicClient().autocompleteUsersInTeam(
|
||||
'uid',
|
||||
function(data) {
|
||||
assert.equal(data != null, true);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
done(new Error(err.message));
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('getStatusesByIds', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
var ids = [];
|
||||
ids.push(TestHelper.basicUser().id);
|
||||
|
||||
TestHelper.basicClient().getStatusesByIds(
|
||||
ids,
|
||||
function(data) {
|
||||
assert.equal(data[TestHelper.basicUser().id] != null, true);
|
||||
done();
|
||||
},
|
||||
function(err) {
|
||||
@@ -519,7 +591,6 @@ describe('Client.User', function() {
|
||||
);
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
it('setActiveChannel', function(done) {
|
||||
TestHelper.initBasic(() => {
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import $ from 'jquery';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||
import BrowserStore from 'stores/browser_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import * as utils from './utils.jsx';
|
||||
import * as UserAgent from './user_agent.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import ErrorStore from 'stores/error_store.jsx';
|
||||
|
||||
import Constants from './constants.jsx';
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import {loadStatusesForProfilesMap} from 'actions/status_actions.jsx';
|
||||
|
||||
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as utils from 'utils/utils.jsx';
|
||||
import * as UserAgent from 'utils/user_agent.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
const StatTypes = Constants.StatTypes;
|
||||
|
||||
@@ -215,94 +217,89 @@ export function getMoreChannels(force) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getChannelExtraInfo(id, memberLimit) {
|
||||
let channelId;
|
||||
if (id) {
|
||||
channelId = id;
|
||||
} else {
|
||||
channelId = ChannelStore.getCurrentId();
|
||||
}
|
||||
|
||||
if (channelId != null) {
|
||||
if (isCallInProgress('getChannelExtraInfo_' + channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker['getChannelExtraInfo_' + channelId] = utils.getTimestamp();
|
||||
|
||||
Client.getChannelExtraInfo(
|
||||
channelId,
|
||||
memberLimit,
|
||||
(data) => {
|
||||
callTracker['getChannelExtraInfo_' + channelId] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_CHANNEL_EXTRA_INFO,
|
||||
extra_info: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker['getChannelExtraInfo_' + channelId] = 0;
|
||||
dispatchError(err, 'getChannelExtraInfo');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getTeamMembers(teamId) {
|
||||
if (isCallInProgress('getTeamMembers')) {
|
||||
export function getChannelStats(channelId = ChannelStore.getCurrentId()) {
|
||||
if (isCallInProgress('getChannelStats' + channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.getTeamMembers = utils.getTimestamp();
|
||||
Client.getTeamMembers(
|
||||
teamId,
|
||||
callTracker['getChannelStats' + channelId] = utils.getTimestamp();
|
||||
|
||||
Client.getChannelStats(
|
||||
channelId,
|
||||
(data) => {
|
||||
callTracker.getTeamMembers = 0;
|
||||
callTracker['getChannelStats' + channelId] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_MEMBERS_FOR_TEAM,
|
||||
team_members: data
|
||||
type: ActionTypes.RECEIVED_CHANNEL_STATS,
|
||||
stats: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.getTeamMembers = 0;
|
||||
dispatchError(err, 'getTeamMembers');
|
||||
callTracker['getChannelStats' + channelId] = 0;
|
||||
dispatchError(err, 'getChannelStats');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getProfilesForDirectMessageList() {
|
||||
if (isCallInProgress('getProfilesForDirectMessageList')) {
|
||||
export function getChannelMember(channelId, userId) {
|
||||
if (isCallInProgress(`getChannelMember${channelId}${userId}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.getProfilesForDirectMessageList = utils.getTimestamp();
|
||||
Client.getProfilesForDirectMessageList(
|
||||
callTracker[`getChannelMember${channelId}${userId}`] = utils.getTimestamp();
|
||||
|
||||
Client.getChannelMember(
|
||||
channelId,
|
||||
userId,
|
||||
(data) => {
|
||||
callTracker.getProfilesForDirectMessageList = 0;
|
||||
callTracker[`getChannelMember${channelId}${userId}`] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES_FOR_DM_LIST,
|
||||
profiles: data
|
||||
type: ActionTypes.RECEIVED_CHANNEL_MEMBER,
|
||||
member: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.getProfilesForDirectMessageList = 0;
|
||||
dispatchError(err, 'getProfilesForDirectMessageList');
|
||||
callTracker[`getChannelMember${channelId}${userId}`] = 0;
|
||||
dispatchError(err, 'getChannelMember');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getProfiles() {
|
||||
if (isCallInProgress('getProfiles')) {
|
||||
export function getUser(userId) {
|
||||
if (isCallInProgress(`getUser${userId}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.getProfiles = utils.getTimestamp();
|
||||
callTracker[`getUser${userId}`] = utils.getTimestamp();
|
||||
Client.getUser(
|
||||
userId,
|
||||
(data) => {
|
||||
callTracker[`getUser${userId}`] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILE,
|
||||
profile: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker[`getUser${userId}`] = 0;
|
||||
dispatchError(err, 'getUser');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getProfiles(offset = UserStore.getPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
|
||||
if (isCallInProgress(`getProfiles${offset}${limit}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker[`getProfiles${offset}${limit}`] = utils.getTimestamp();
|
||||
Client.getProfiles(
|
||||
offset,
|
||||
limit,
|
||||
(data) => {
|
||||
callTracker.getProfiles = 0;
|
||||
callTracker[`getProfiles${offset}${limit}`] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES,
|
||||
@@ -310,30 +307,123 @@ export function getProfiles() {
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.getProfiles = 0;
|
||||
callTracker[`getProfiles${offset}${limit}`] = 0;
|
||||
dispatchError(err, 'getProfiles');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getDirectProfiles() {
|
||||
if (isCallInProgress('getDirectProfiles')) {
|
||||
export function getProfilesInTeam(teamId = TeamStore.getCurrentId(), offset = UserStore.getInTeamPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
|
||||
if (isCallInProgress(`getProfilesInTeam${offset}${limit}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.getDirectProfiles = utils.getTimestamp();
|
||||
Client.getDirectProfiles(
|
||||
callTracker[`getProfilesInTeam${offset}${limit}`] = utils.getTimestamp();
|
||||
Client.getProfilesInTeam(
|
||||
teamId,
|
||||
offset,
|
||||
limit,
|
||||
(data) => {
|
||||
callTracker.getDirectProfiles = 0;
|
||||
callTracker[`getProfilesInTeam${offset}${limit}`] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_DIRECT_PROFILES,
|
||||
type: ActionTypes.RECEIVED_PROFILES_IN_TEAM,
|
||||
profiles: data,
|
||||
team_id: teamId,
|
||||
offset,
|
||||
count: Object.keys(data).length
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker[`getProfilesInTeam${offset}${limit}`] = 0;
|
||||
dispatchError(err, 'getProfilesInTeam');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getProfilesInChannel(channelId = ChannelStore.getCurrentId(), offset = UserStore.getInChannelPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
|
||||
if (isCallInProgress(`getProfilesInChannel${offset}${limit}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker[`getProfilesInChannel${offset}${limit}`] = utils.getTimestamp();
|
||||
Client.getProfilesInChannel(
|
||||
channelId,
|
||||
offset,
|
||||
limit,
|
||||
(data) => {
|
||||
callTracker[`getProfilesInChannel${offset}${limit}`] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES_IN_CHANNEL,
|
||||
channel_id: channelId,
|
||||
profiles: data,
|
||||
offset,
|
||||
count: Object.keys(data).length
|
||||
});
|
||||
|
||||
loadStatusesForProfilesMap(data);
|
||||
},
|
||||
(err) => {
|
||||
callTracker[`getProfilesInChannel${offset}${limit}`] = 0;
|
||||
dispatchError(err, 'getProfilesInChannel');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getProfilesNotInChannel(channelId = ChannelStore.getCurrentId(), offset = UserStore.getNotInChannelPagingOffset(), limit = Constants.PROFILE_CHUNK_SIZE) {
|
||||
if (isCallInProgress(`getProfilesNotInChannel${offset}${limit}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker[`getProfilesNotInChannel${offset}${limit}`] = utils.getTimestamp();
|
||||
Client.getProfilesNotInChannel(
|
||||
channelId,
|
||||
offset,
|
||||
limit,
|
||||
(data) => {
|
||||
callTracker[`getProfilesNotInChannel${offset}${limit}`] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES_NOT_IN_CHANNEL,
|
||||
channel_id: channelId,
|
||||
profiles: data,
|
||||
offset,
|
||||
count: Object.keys(data).length
|
||||
});
|
||||
|
||||
loadStatusesForProfilesMap(data);
|
||||
},
|
||||
(err) => {
|
||||
callTracker[`getProfilesNotInChannel${offset}${limit}`] = 0;
|
||||
dispatchError(err, 'getProfilesNotInChannel');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getProfilesByIds(userIds) {
|
||||
if (isCallInProgress('getProfilesByIds')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userIds || userIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.getProfilesByIds = utils.getTimestamp();
|
||||
Client.getProfilesByIds(
|
||||
userIds,
|
||||
(data) => {
|
||||
callTracker.getProfilesByIds = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_PROFILES,
|
||||
profiles: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.getDirectProfiles = 0;
|
||||
dispatchError(err, 'getDirectProfiles');
|
||||
callTracker.getProfilesByIds = 0;
|
||||
dispatchError(err, 'getProfilesByIds');
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -548,173 +638,6 @@ export function search(terms, isOrSearch) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getPostsPage(id, maxPosts) {
|
||||
let channelId = id;
|
||||
if (channelId == null) {
|
||||
channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCallInProgress('getPostsPage_' + channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var postList = PostStore.getAllPosts(id);
|
||||
|
||||
var max = maxPosts;
|
||||
if (max == null) {
|
||||
max = Constants.POST_CHUNK_SIZE * Constants.MAX_POST_CHUNKS;
|
||||
}
|
||||
|
||||
// if we already have more than POST_CHUNK_SIZE posts,
|
||||
// let's get the amount we have but rounded up to next multiple of POST_CHUNK_SIZE,
|
||||
// with a max at maxPosts
|
||||
var numPosts = Math.min(max, Constants.POST_CHUNK_SIZE);
|
||||
if (postList && postList.order.length > 0) {
|
||||
numPosts = Math.min(max, Constants.POST_CHUNK_SIZE * Math.ceil(postList.order.length / Constants.POST_CHUNK_SIZE));
|
||||
}
|
||||
|
||||
if (channelId != null) {
|
||||
callTracker['getPostsPage_' + channelId] = utils.getTimestamp();
|
||||
|
||||
Client.getPostsPage(
|
||||
channelId,
|
||||
0,
|
||||
numPosts,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: true,
|
||||
numRequested: numPosts,
|
||||
checkLatest: true,
|
||||
post_list: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
dispatchError(err, 'getPostsPage');
|
||||
},
|
||||
() => {
|
||||
callTracker['getPostsPage_' + channelId] = 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getPosts(id) {
|
||||
let channelId = id;
|
||||
if (channelId == null) {
|
||||
channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCallInProgress('getPosts_' + channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const postList = PostStore.getAllPosts(channelId);
|
||||
const latestPostTime = PostStore.getLatestPostFromPageTime(id);
|
||||
|
||||
if ($.isEmptyObject(postList) || postList.order.length < Constants.POST_CHUNK_SIZE || latestPostTime === 0) {
|
||||
getPostsPage(channelId, Constants.POST_CHUNK_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker['getPosts_' + channelId] = utils.getTimestamp();
|
||||
|
||||
Client.getPosts(
|
||||
channelId,
|
||||
latestPostTime,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: true,
|
||||
numRequested: 0,
|
||||
post_list: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
dispatchError(err, 'getPosts');
|
||||
},
|
||||
() => {
|
||||
callTracker['getPosts_' + channelId] = 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getPostsBefore(postId, offset, numPost, isPost) {
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCallInProgress('getPostsBefore_' + channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Client.getPostsBefore(
|
||||
channelId,
|
||||
postId,
|
||||
offset,
|
||||
numPost,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: true,
|
||||
numRequested: numPost,
|
||||
post_list: data,
|
||||
isPost
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
dispatchError(err, 'getPostsBefore');
|
||||
},
|
||||
() => {
|
||||
callTracker['getPostsBefore_' + channelId] = 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getPostsAfter(postId, offset, numPost, isPost) {
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCallInProgress('getPostsAfter_' + channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Client.getPostsAfter(
|
||||
channelId,
|
||||
postId,
|
||||
offset,
|
||||
numPost,
|
||||
(data) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_POSTS,
|
||||
id: channelId,
|
||||
before: false,
|
||||
numRequested: numPost,
|
||||
post_list: data,
|
||||
isPost
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
dispatchError(err, 'getPostsAfter');
|
||||
},
|
||||
() => {
|
||||
callTracker['getPostsAfter_' + channelId] = 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getFileInfosForPost(channelId, postId) {
|
||||
const callName = 'getFileInfosForPost' + postId;
|
||||
|
||||
@@ -828,6 +751,58 @@ export function getMyTeam() {
|
||||
);
|
||||
}
|
||||
|
||||
export function getTeamMember(teamId, userId) {
|
||||
const callName = `getTeamMember${teamId}${userId}`;
|
||||
if (isCallInProgress(callName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker[callName] = utils.getTimestamp();
|
||||
Client.getTeamMember(
|
||||
(data) => {
|
||||
callTracker[callName] = 0;
|
||||
|
||||
const memberMap = {};
|
||||
memberMap[userId] = data;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_MEMBERS_IN_TEAM,
|
||||
team_id: teamId,
|
||||
team_members: memberMap
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker[callName] = 0;
|
||||
dispatchError(err, 'getTeamMember');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getTeamStats(teamId) {
|
||||
const callName = `getTeamStats${teamId}`;
|
||||
if (isCallInProgress(callName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker[callName] = utils.getTimestamp();
|
||||
Client.getTeamStats(
|
||||
teamId,
|
||||
(data) => {
|
||||
callTracker[callName] = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_TEAM_STATS,
|
||||
team_id: teamId,
|
||||
stats: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker[callName] = 0;
|
||||
dispatchError(err, 'getTeamStats');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function getAllPreferences() {
|
||||
if (isCallInProgress('getAllPreferences')) {
|
||||
return;
|
||||
@@ -987,6 +962,18 @@ export function getStandardAnalytics(teamId) {
|
||||
if (data[index].name === 'team_count' && teamId == null) {
|
||||
stats[StatTypes.TOTAL_TEAMS] = data[index].value;
|
||||
}
|
||||
|
||||
if (data[index].name === 'total_websocket_connections') {
|
||||
stats[StatTypes.TOTAL_WEBSOCKET_CONNECTIONS] = data[index].value;
|
||||
}
|
||||
|
||||
if (data[index].name === 'total_master_db_connections') {
|
||||
stats[StatTypes.TOTAL_MASTER_DB_CONNECTIONS] = data[index].value;
|
||||
}
|
||||
|
||||
if (data[index].name === 'total_read_db_connections') {
|
||||
stats[StatTypes.TOTAL_READ_DB_CONNECTIONS] = data[index].value;
|
||||
}
|
||||
}
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
@@ -1212,54 +1199,6 @@ export function getRecentAndNewUsersAnalytics(teamId) {
|
||||
);
|
||||
}
|
||||
|
||||
export function listIncomingHooks() {
|
||||
if (isCallInProgress('listIncomingHooks')) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.listIncomingHooks = utils.getTimestamp();
|
||||
|
||||
Client.listIncomingHooks(
|
||||
(data) => {
|
||||
callTracker.listIncomingHooks = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_INCOMING_WEBHOOKS,
|
||||
teamId: Client.teamId,
|
||||
incomingWebhooks: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.listIncomingHooks = 0;
|
||||
dispatchError(err, 'getIncomingHooks');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function listOutgoingHooks() {
|
||||
if (isCallInProgress('listOutgoingHooks')) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.listOutgoingHooks = utils.getTimestamp();
|
||||
|
||||
Client.listOutgoingHooks(
|
||||
(data) => {
|
||||
callTracker.listOutgoingHooks = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_OUTGOING_WEBHOOKS,
|
||||
teamId: Client.teamId,
|
||||
outgoingWebhooks: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.listOutgoingHooks = 0;
|
||||
dispatchError(err, 'getOutgoingHooks');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function addIncomingHook(hook, success, error) {
|
||||
Client.addIncomingHook(
|
||||
hook,
|
||||
@@ -1353,30 +1292,6 @@ export function regenOutgoingHookToken(id) {
|
||||
);
|
||||
}
|
||||
|
||||
export function listTeamCommands() {
|
||||
if (isCallInProgress('listTeamCommands')) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.listTeamCommands = utils.getTimestamp();
|
||||
|
||||
Client.listTeamCommands(
|
||||
(data) => {
|
||||
callTracker.listTeamCommands = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_COMMANDS,
|
||||
teamId: Client.teamId,
|
||||
commands: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.listTeamCommands = 0;
|
||||
dispatchError(err, 'listTeamCommands');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function addCommand(command, success, error) {
|
||||
Client.addCommand(
|
||||
command,
|
||||
@@ -1459,29 +1374,6 @@ export function getPublicLink(fileId, success, error) {
|
||||
);
|
||||
}
|
||||
|
||||
export function listEmoji() {
|
||||
if (isCallInProgress('listEmoji')) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.listEmoji = utils.getTimestamp();
|
||||
|
||||
Client.listEmoji(
|
||||
(data) => {
|
||||
callTracker.listEmoji = 0;
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_CUSTOM_EMOJIS,
|
||||
emojis: data
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
callTracker.listEmoji = 0;
|
||||
dispatchError(err, 'listEmoji');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function addEmoji(emoji, image, success, error) {
|
||||
const callName = 'addEmoji' + emoji.name;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export const ActionTypes = keyMirror({
|
||||
RECEIVED_CHANNELS: null,
|
||||
RECEIVED_CHANNEL: null,
|
||||
RECEIVED_MORE_CHANNELS: null,
|
||||
RECEIVED_CHANNEL_EXTRA_INFO: null,
|
||||
RECEIVED_CHANNEL_STATS: null,
|
||||
|
||||
FOCUS_POST: null,
|
||||
RECEIVED_POSTS: null,
|
||||
@@ -84,9 +84,11 @@ export const ActionTypes = keyMirror({
|
||||
RECEIVED_MENTION_DATA: null,
|
||||
RECEIVED_ADD_MENTION: null,
|
||||
|
||||
RECEIVED_PROFILES_FOR_DM_LIST: null,
|
||||
RECEIVED_PROFILES: null,
|
||||
RECEIVED_DIRECT_PROFILES: null,
|
||||
RECEIVED_PROFILES_IN_TEAM: null,
|
||||
RECEIVED_PROFILE: null,
|
||||
RECEIVED_PROFILES_IN_CHANNEL: null,
|
||||
RECEIVED_PROFILE_NOT_IN_CHANNEL: null,
|
||||
RECEIVED_ME: null,
|
||||
RECEIVED_SESSIONS: null,
|
||||
RECEIVED_AUDITS: null,
|
||||
@@ -129,8 +131,9 @@ export const ActionTypes = keyMirror({
|
||||
RECEIVED_SERVER_COMPLIANCE_REPORTS: null,
|
||||
RECEIVED_ALL_TEAMS: null,
|
||||
RECEIVED_ALL_TEAM_LISTINGS: null,
|
||||
RECEIVED_TEAM_MEMBERS: null,
|
||||
RECEIVED_MEMBERS_FOR_TEAM: null,
|
||||
RECEIVED_MY_TEAM_MEMBERS: null,
|
||||
RECEIVED_MEMBERS_IN_TEAM: null,
|
||||
RECEIVED_TEAM_STATS: null,
|
||||
|
||||
RECEIVED_LOCALE: null,
|
||||
|
||||
@@ -232,7 +235,10 @@ export const Constants = {
|
||||
POST_PER_DAY: null,
|
||||
USERS_WITH_POSTS_PER_DAY: null,
|
||||
RECENTLY_ACTIVE_USERS: null,
|
||||
NEWLY_CREATED_USERS: null
|
||||
NEWLY_CREATED_USERS: null,
|
||||
TOTAL_WEBSOCKET_CONNECTIONS: null,
|
||||
TOTAL_MASTER_DB_CONNECTIONS: null,
|
||||
TOTAL_READ_DB_CONNECTIONS: null
|
||||
}),
|
||||
STAT_MAX_ACTIVE_USERS: 20,
|
||||
STAT_MAX_NEW_USERS: 20,
|
||||
@@ -313,7 +319,7 @@ export const Constants = {
|
||||
SIGNIN_VERIFIED: 'verified',
|
||||
SESSION_EXPIRED: 'expired',
|
||||
POST_CHUNK_SIZE: 60,
|
||||
MAX_POST_CHUNKS: 3,
|
||||
PROFILE_CHUNK_SIZE: 100,
|
||||
POST_FOCUS_CONTEXT_RADIUS: 10,
|
||||
POST_LOADING: 'loading',
|
||||
POST_FAILED: 'failed',
|
||||
@@ -843,7 +849,9 @@ export const Constants = {
|
||||
MENTION_MEMBERS: 'mention.members',
|
||||
MENTION_NONMEMBERS: 'mention.nonmembers',
|
||||
MENTION_SPECIAL: 'mention.special',
|
||||
DEFAULT_NOTIFICATION_DURATION: 5000
|
||||
DEFAULT_NOTIFICATION_DURATION: 5000,
|
||||
STATUS_INTERVAL: 60000,
|
||||
AUTOCOMPLETE_TIMEOUT: 200
|
||||
};
|
||||
|
||||
export default Constants;
|
||||
|
||||
@@ -10,7 +10,6 @@ import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
var ActionTypes = Constants.ActionTypes;
|
||||
import * as AsyncClient from './async_client.jsx';
|
||||
import Client from 'client/web_client.jsx';
|
||||
import * as UserAgent from 'utils/user_agent.jsx';
|
||||
|
||||
@@ -1074,66 +1073,6 @@ export function windowHeight() {
|
||||
return $(window).height();
|
||||
}
|
||||
|
||||
export function openDirectChannelToUser(user, successCb, errorCb) {
|
||||
AsyncClient.savePreference(
|
||||
Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW,
|
||||
user.id,
|
||||
'true'
|
||||
);
|
||||
|
||||
// if the user in another team and isn't already in the direct message
|
||||
// list then we should add him so his name shows up correctly.
|
||||
var profileUser = UserStore.getProfile(user.id);
|
||||
if (!profileUser) {
|
||||
UserStore.getDirectProfiles()[user.id] = user;
|
||||
}
|
||||
|
||||
const channelName = this.getDirectChannelName(UserStore.getCurrentId(), user.id);
|
||||
let channel = ChannelStore.getByName(channelName);
|
||||
|
||||
if (channel) {
|
||||
if ($.isFunction(successCb)) {
|
||||
successCb(channel, true);
|
||||
}
|
||||
} else {
|
||||
channel = {
|
||||
name: channelName,
|
||||
last_post_at: 0,
|
||||
total_msg_count: 0,
|
||||
type: 'D',
|
||||
display_name: user.username,
|
||||
teammate_id: user.id,
|
||||
status: UserStore.getStatus(user.id)
|
||||
};
|
||||
|
||||
Client.createDirectChannel(
|
||||
user.id,
|
||||
(data) => {
|
||||
Client.getChannel(
|
||||
data.id,
|
||||
(data2) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_CHANNEL,
|
||||
channel: data2.channel,
|
||||
member: data2.member
|
||||
});
|
||||
|
||||
if ($.isFunction(successCb)) {
|
||||
successCb(data2.channel, false);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
() => {
|
||||
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/' + channelName);
|
||||
if ($.isFunction(errorCb)) {
|
||||
errorCb();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Use when sorting multiple channels or teams by their `display_name` field
|
||||
export function sortByDisplayName(a, b) {
|
||||
let aDisplayName = '';
|
||||
|
||||
Ссылка в новой задаче
Block a user