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
Этот коммит содержится в:
Joram Wilander
2016-10-19 14:49:25 -04:00
коммит произвёл GitHub
родитель 0512bd26ee
Коммит 365b8b465e
129 изменённых файлов: 6411 добавлений и 2530 удалений

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

@@ -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: