summaryrefslogtreecommitdiffstats
path: root/webapp/stores
diff options
context:
space:
mode:
author=Corey Hulen <corey@hulen.com>2016-03-16 18:13:16 -0700
committer=Corey Hulen <corey@hulen.com>2016-03-16 18:13:16 -0700
commitb9d5b4e5dcc1585397f1e1d2e53c5f040ee76220 (patch)
tree85d2c293aa3456182a754fefe6646162b516eb6c /webapp/stores
parente101b2cf7c172d1c4ff20e0df63917b5b8f923ed (diff)
parentcba59d4eb6ef0f65304bc72339c676ebfd653e2b (diff)
downloadchat-b9d5b4e5dcc1585397f1e1d2e53c5f040ee76220.tar.gz
chat-b9d5b4e5dcc1585397f1e1d2e53c5f040ee76220.tar.bz2
chat-b9d5b4e5dcc1585397f1e1d2e53c5f040ee76220.zip
merging files
Diffstat (limited to 'webapp/stores')
-rw-r--r--webapp/stores/admin_store.jsx192
-rw-r--r--webapp/stores/analytics_store.jsx85
-rw-r--r--webapp/stores/browser_store.jsx225
-rw-r--r--webapp/stores/channel_store.jsx352
-rw-r--r--webapp/stores/error_store.jsx80
-rw-r--r--webapp/stores/file_store.jsx60
-rw-r--r--webapp/stores/localization_store.jsx60
-rw-r--r--webapp/stores/modal_store.jsx47
-rw-r--r--webapp/stores/post_store.jsx610
-rw-r--r--webapp/stores/preference_store.jsx178
-rw-r--r--webapp/stores/search_store.jsx137
-rw-r--r--webapp/stores/socket_store.jsx343
-rw-r--r--webapp/stores/suggestion_store.jsx261
-rw-r--r--webapp/stores/team_store.jsx142
-rw-r--r--webapp/stores/user_store.jsx305
15 files changed, 3077 insertions, 0 deletions
diff --git a/webapp/stores/admin_store.jsx b/webapp/stores/admin_store.jsx
new file mode 100644
index 000000000..0f19dd484
--- /dev/null
+++ b/webapp/stores/admin_store.jsx
@@ -0,0 +1,192 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import BrowserStore from 'stores/browser_store.jsx';
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+
+const LOG_CHANGE_EVENT = 'log_change';
+const SERVER_AUDIT_CHANGE_EVENT = 'server_audit_change';
+const CONFIG_CHANGE_EVENT = 'config_change';
+const ALL_TEAMS_EVENT = 'all_team_change';
+const SERVER_COMPLIANCE_REPORT_CHANGE_EVENT = 'server_compliance_reports_change';
+
+class AdminStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.logs = null;
+ this.audits = null;
+ this.config = null;
+ this.teams = null;
+ this.complianceReports = null;
+
+ this.emitLogChange = this.emitLogChange.bind(this);
+ this.addLogChangeListener = this.addLogChangeListener.bind(this);
+ this.removeLogChangeListener = this.removeLogChangeListener.bind(this);
+
+ this.emitAuditChange = this.emitAuditChange.bind(this);
+ this.addAuditChangeListener = this.addAuditChangeListener.bind(this);
+ this.removeAuditChangeListener = this.removeAuditChangeListener.bind(this);
+
+ this.emitComplianceReportsChange = this.emitComplianceReportsChange.bind(this);
+ this.addComplianceReportsChangeListener = this.addComplianceReportsChangeListener.bind(this);
+ this.removeComplianceReportsChangeListener = this.removeComplianceReportsChangeListener.bind(this);
+
+ this.emitConfigChange = this.emitConfigChange.bind(this);
+ this.addConfigChangeListener = this.addConfigChangeListener.bind(this);
+ this.removeConfigChangeListener = this.removeConfigChangeListener.bind(this);
+
+ this.emitAllTeamsChange = this.emitAllTeamsChange.bind(this);
+ this.addAllTeamsChangeListener = this.addAllTeamsChangeListener.bind(this);
+ this.removeAllTeamsChangeListener = this.removeAllTeamsChangeListener.bind(this);
+ }
+
+ emitLogChange() {
+ this.emit(LOG_CHANGE_EVENT);
+ }
+
+ addLogChangeListener(callback) {
+ this.on(LOG_CHANGE_EVENT, callback);
+ }
+
+ removeLogChangeListener(callback) {
+ this.removeListener(LOG_CHANGE_EVENT, callback);
+ }
+
+ emitAuditChange() {
+ this.emit(SERVER_AUDIT_CHANGE_EVENT);
+ }
+
+ addAuditChangeListener(callback) {
+ this.on(SERVER_AUDIT_CHANGE_EVENT, callback);
+ }
+
+ removeAuditChangeListener(callback) {
+ this.removeListener(SERVER_AUDIT_CHANGE_EVENT, callback);
+ }
+
+ emitComplianceReportsChange() {
+ this.emit(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT);
+ }
+
+ addComplianceReportsChangeListener(callback) {
+ this.on(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT, callback);
+ }
+
+ removeComplianceReportsChangeListener(callback) {
+ this.removeListener(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT, callback);
+ }
+
+ emitConfigChange() {
+ this.emit(CONFIG_CHANGE_EVENT);
+ }
+
+ addConfigChangeListener(callback) {
+ this.on(CONFIG_CHANGE_EVENT, callback);
+ }
+
+ removeConfigChangeListener(callback) {
+ this.removeListener(CONFIG_CHANGE_EVENT, callback);
+ }
+
+ emitAllTeamsChange() {
+ this.emit(ALL_TEAMS_EVENT);
+ }
+
+ addAllTeamsChangeListener(callback) {
+ this.on(ALL_TEAMS_EVENT, callback);
+ }
+
+ removeAllTeamsChangeListener(callback) {
+ this.removeListener(ALL_TEAMS_EVENT, callback);
+ }
+
+ getLogs() {
+ return this.logs;
+ }
+
+ saveLogs(logs) {
+ this.logs = logs;
+ }
+
+ getAudits() {
+ return this.audits;
+ }
+
+ saveAudits(audits) {
+ this.audits = audits;
+ }
+
+ getComplianceReports() {
+ return this.complianceReports;
+ }
+
+ saveComplianceReports(complianceReports) {
+ this.complianceReports = complianceReports;
+ }
+
+ getConfig() {
+ return this.config;
+ }
+
+ saveConfig(config) {
+ this.config = config;
+ }
+
+ getAllTeams() {
+ return this.teams;
+ }
+
+ saveAllTeams(teams) {
+ this.teams = teams;
+ }
+
+ getSelectedTeams() {
+ const result = BrowserStore.getItem('seleted_teams');
+ if (!result) {
+ return {};
+ }
+ return result;
+ }
+
+ saveSelectedTeams(teams) {
+ BrowserStore.setItem('seleted_teams', teams);
+ }
+}
+
+var AdminStore = new AdminStoreClass();
+
+AdminStoreClass.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_LOGS:
+ AdminStore.saveLogs(action.logs);
+ AdminStore.emitLogChange();
+ break;
+ case ActionTypes.RECEIVED_SERVER_AUDITS:
+ AdminStore.saveAudits(action.audits);
+ AdminStore.emitAuditChange();
+ break;
+ case ActionTypes.RECEIVED_SERVER_COMPLIANCE_REPORTS:
+ AdminStore.saveComplianceReports(action.complianceReports);
+ AdminStore.emitComplianceReportsChange();
+ break;
+ case ActionTypes.RECEIVED_CONFIG:
+ AdminStore.saveConfig(action.config);
+ AdminStore.emitConfigChange();
+ break;
+ case ActionTypes.RECEIVED_ALL_TEAMS:
+ AdminStore.saveAllTeams(action.teams);
+ AdminStore.emitAllTeamsChange();
+ break;
+ default:
+ }
+});
+
+export default AdminStore;
diff --git a/webapp/stores/analytics_store.jsx b/webapp/stores/analytics_store.jsx
new file mode 100644
index 000000000..565622c76
--- /dev/null
+++ b/webapp/stores/analytics_store.jsx
@@ -0,0 +1,85 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+
+const CHANGE_EVENT = 'change';
+
+class AnalyticsStoreClass extends EventEmitter {
+ constructor() {
+ super();
+ this.systemStats = {};
+ this.teamStats = {};
+ }
+
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ getAllSystem() {
+ return JSON.parse(JSON.stringify(this.systemStats));
+ }
+
+ getAllTeam(id) {
+ if (id in this.teamStats) {
+ return JSON.parse(JSON.stringify(this.teamStats[id]));
+ }
+
+ return {};
+ }
+
+ storeSystemStats(newStats) {
+ for (const stat in newStats) {
+ if (!newStats.hasOwnProperty(stat)) {
+ continue;
+ }
+ this.systemStats[stat] = newStats[stat];
+ }
+ }
+
+ storeTeamStats(id, newStats) {
+ if (!(id in this.teamStats)) {
+ this.teamStats[id] = {};
+ }
+
+ for (const stat in newStats) {
+ if (!newStats.hasOwnProperty(stat)) {
+ continue;
+ }
+ this.teamStats[id][stat] = newStats[stat];
+ }
+ }
+
+}
+
+var AnalyticsStore = new AnalyticsStoreClass();
+
+AnalyticsStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_ANALYTICS:
+ if (action.teamId == null) {
+ AnalyticsStore.storeSystemStats(action.stats);
+ } else {
+ AnalyticsStore.storeTeamStats(action.teamId, action.stats);
+ }
+ AnalyticsStore.emitChange();
+ break;
+ default:
+ }
+});
+
+export default AnalyticsStore;
diff --git a/webapp/stores/browser_store.jsx b/webapp/stores/browser_store.jsx
new file mode 100644
index 000000000..66190f6a2
--- /dev/null
+++ b/webapp/stores/browser_store.jsx
@@ -0,0 +1,225 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {generateId} from 'utils/utils.jsx';
+
+function getPrefix() {
+ if (global.window.mm_current_user_id) {
+ return global.window.mm_current_user_id + '_';
+ }
+
+ return 'unknown_';
+}
+
+class BrowserStoreClass {
+ constructor() {
+ this.getItem = this.getItem.bind(this);
+ this.setItem = this.setItem.bind(this);
+ this.removeItem = this.removeItem.bind(this);
+ this.setGlobalItem = this.setGlobalItem.bind(this);
+ this.getGlobalItem = this.getGlobalItem.bind(this);
+ this.removeGlobalItem = this.removeGlobalItem.bind(this);
+ this.actionOnItemsWithPrefix = this.actionOnItemsWithPrefix.bind(this);
+ this.actionOnGlobalItemsWithPrefix = this.actionOnGlobalItemsWithPrefix.bind(this);
+ this.isLocalStorageSupported = this.isLocalStorageSupported.bind(this);
+ this.getLastServerVersion = this.getLastServerVersion.bind(this);
+ this.setLastServerVersion = this.setLastServerVersion.bind(this);
+ this.clear = this.clear.bind(this);
+ this.clearAll = this.clearAll.bind(this);
+ this.checkedLocalStorageSupported = '';
+ this.signalLogout = this.signalLogout.bind(this);
+ this.isSignallingLogout = this.isSignallingLogout.bind(this);
+ this.signalLogin = this.signalLogin.bind(this);
+ this.isSignallingLogin = this.isSignallingLogin.bind(this);
+ }
+
+ checkVersion() {
+ var currentVersion = sessionStorage.getItem('storage_version');
+ if (currentVersion !== global.window.mm_config.Version) {
+ sessionStorage.clear();
+ try {
+ sessionStorage.setItem('storage_version', global.window.mm_config.Version);
+ } catch (e) {
+ // Do nothing
+ }
+ }
+ }
+
+ getItem(name, defaultValue) {
+ var result = null;
+ try {
+ result = JSON.parse(sessionStorage.getItem(getPrefix() + name));
+ } catch (err) {
+ result = null;
+ }
+
+ if (result === null && typeof defaultValue !== 'undefined') {
+ result = defaultValue;
+ }
+
+ return result;
+ }
+
+ setItem(name, value) {
+ sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
+ }
+
+ removeItem(name) {
+ sessionStorage.removeItem(getPrefix() + name);
+ }
+
+ setGlobalItem(name, value) {
+ try {
+ if (this.isLocalStorageSupported()) {
+ localStorage.setItem(getPrefix() + name, JSON.stringify(value));
+ } else {
+ sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
+ }
+ } catch (err) {
+ console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console
+ localStorage.clear();
+ sessionStorage.clear();
+ window.location.reload(true);
+ }
+ }
+
+ getGlobalItem(name, defaultValue) {
+ var result = null;
+ try {
+ if (this.isLocalStorageSupported()) {
+ result = JSON.parse(localStorage.getItem(getPrefix() + name));
+ } else {
+ result = JSON.parse(sessionStorage.getItem(getPrefix() + name));
+ }
+ } catch (err) {
+ result = null;
+ }
+
+ if (result === null && typeof defaultValue !== 'undefined') {
+ result = defaultValue;
+ }
+
+ return result;
+ }
+
+ removeGlobalItem(name) {
+ if (this.isLocalStorageSupported()) {
+ localStorage.removeItem(getPrefix() + name);
+ } else {
+ sessionStorage.removeItem(getPrefix() + name);
+ }
+ }
+
+ getLastServerVersion() {
+ return sessionStorage.getItem('last_server_version');
+ }
+
+ setLastServerVersion(version) {
+ sessionStorage.setItem('last_server_version', version);
+ }
+
+ signalLogout() {
+ if (this.isLocalStorageSupported()) {
+ // PLT-1285 store an identifier in session storage so we can catch if the logout came from this tab on IE11
+ const logoutId = generateId();
+
+ sessionStorage.setItem('__logout__', logoutId);
+ localStorage.setItem('__logout__', logoutId);
+ localStorage.removeItem('__logout__');
+ }
+ }
+
+ isSignallingLogout(logoutId) {
+ return logoutId === sessionStorage.getItem('__logout__');
+ }
+
+ signalLogin() {
+ if (this.isLocalStorageSupported()) {
+ // PLT-1285 store an identifier in session storage so we can catch if the logout came from this tab on IE11
+ const loginId = generateId();
+
+ sessionStorage.setItem('__login__', loginId);
+ localStorage.setItem('__login__', loginId);
+ localStorage.removeItem('__login__');
+ }
+ }
+
+ isSignallingLogin(loginId) {
+ return loginId === sessionStorage.getItem('__login__');
+ }
+
+ /**
+ * Preforms the given action on each item that has the given prefix
+ * Signature for action is action(key, value)
+ */
+ actionOnGlobalItemsWithPrefix(prefix, action) {
+ var globalPrefix = getPrefix();
+ var globalPrefixiLen = globalPrefix.length;
+
+ var storage = sessionStorage;
+ if (this.isLocalStorageSupported()) {
+ storage = localStorage;
+ }
+
+ for (var key in storage) {
+ if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) {
+ var userkey = key.substring(globalPrefixiLen);
+ action(userkey, this.getGlobalItem(key));
+ }
+ }
+ }
+
+ actionOnItemsWithPrefix(prefix, action) {
+ var globalPrefix = getPrefix();
+ var globalPrefixiLen = globalPrefix.length;
+ for (var key in sessionStorage) {
+ if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) {
+ var userkey = key.substring(globalPrefixiLen);
+ action(userkey, this.getGlobalItem(key));
+ }
+ }
+ }
+
+ clear() {
+ // don't clear the logout id so IE11 can tell which tab sent a logout request
+ const logoutId = sessionStorage.getItem('__logout__');
+
+ sessionStorage.clear();
+
+ if (logoutId) {
+ sessionStorage.setItem('__logout__', logoutId);
+ }
+ }
+
+ clearAll() {
+ sessionStorage.clear();
+ localStorage.clear();
+ }
+
+ isLocalStorageSupported() {
+ if (this.checkedLocalStorageSupported !== '') {
+ return this.checkedLocalStorageSupported;
+ }
+
+ try {
+ sessionStorage.setItem('__testSession__', '1');
+ sessionStorage.removeItem('__testSession__');
+
+ localStorage.setItem('__testLocal__', '1');
+ if (localStorage.getItem('__testLocal__') !== '1') {
+ this.checkedLocalStorageSupported = false;
+ }
+ localStorage.removeItem('__testLocal__', '1');
+
+ this.checkedLocalStorageSupported = true;
+ } catch (e) {
+ this.checkedLocalStorageSupported = false;
+ }
+
+ return this.checkedLocalStorageSupported;
+ }
+}
+
+var BrowserStore = new BrowserStoreClass();
+export default BrowserStore;
+window.BrowserStore = BrowserStore;
diff --git a/webapp/stores/channel_store.jsx b/webapp/stores/channel_store.jsx
new file mode 100644
index 000000000..b2946e326
--- /dev/null
+++ b/webapp/stores/channel_store.jsx
@@ -0,0 +1,352 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+var Utils;
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+const NotificationPrefs = Constants.NotificationPrefs;
+
+const CHANGE_EVENT = 'change';
+const LEAVE_EVENT = 'leave';
+const MORE_CHANGE_EVENT = 'change';
+const EXTRA_INFO_EVENT = 'extra_info';
+
+class ChannelStoreClass extends EventEmitter {
+ constructor(props) {
+ super(props);
+
+ 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.findFirstBy = this.findFirstBy.bind(this);
+ this.get = this.get.bind(this);
+ this.getMember = this.getMember.bind(this);
+ this.getByName = this.getByName.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.currentId = null;
+ this.postMode = this.POST_MODE_CHANNEL;
+ this.channels = [];
+ this.channelMembers = {};
+ this.moreChannels = {};
+ this.moreChannels.loading = true;
+ this.extraInfos = {};
+ this.unreadCounts = {};
+ }
+ get POST_MODE_CHANNEL() {
+ return 1;
+ }
+ get POST_MODE_FOCUS() {
+ return 2;
+ }
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+ emitMoreChange() {
+ this.emit(MORE_CHANGE_EVENT);
+ }
+ addMoreChangeListener(callback) {
+ this.on(MORE_CHANGE_EVENT, callback);
+ }
+ removeMoreChangeListener(callback) {
+ this.removeListener(MORE_CHANGE_EVENT, callback);
+ }
+ emitExtraInfoChange() {
+ this.emit(EXTRA_INFO_EVENT);
+ }
+ addExtraInfoChangeListener(callback) {
+ this.on(EXTRA_INFO_EVENT, callback);
+ }
+ removeExtraInfoChangeListener(callback) {
+ this.removeListener(EXTRA_INFO_EVENT, callback);
+ }
+ emitLeave(id) {
+ this.emit(LEAVE_EVENT, id);
+ }
+ addLeaveListener(callback) {
+ this.on(LEAVE_EVENT, callback);
+ }
+ removeLeaveListener(callback) {
+ this.removeListener(LEAVE_EVENT, callback);
+ }
+ findFirstBy(field, value) {
+ var channels = this.getChannels();
+ for (var i = 0; i < channels.length; i++) {
+ if (channels[i][field] === value) {
+ return channels[i];
+ }
+ }
+
+ return null;
+ }
+ get(id) {
+ return this.findFirstBy('id', id);
+ }
+ getMember(id) {
+ return this.getAllMembers()[id];
+ }
+ getByName(name) {
+ return this.findFirstBy('name', name);
+ }
+ getAll() {
+ return this.getChannels();
+ }
+ getAllMembers() {
+ return this.getChannelMembers();
+ }
+ getMoreAll() {
+ return this.getMoreChannels();
+ }
+ setCurrentId(id) {
+ this.currentId = id;
+ }
+ resetCounts(id) {
+ const cm = this.channelMembers;
+ for (var cmid in cm) {
+ if (cm[cmid].channel_id === id) {
+ var c = this.get(id);
+ if (c) {
+ cm[cmid].msg_count = this.get(id).total_msg_count;
+ cm[cmid].mention_count = 0;
+ this.setUnreadCount(id);
+ }
+ break;
+ }
+ }
+ }
+ getCurrentId() {
+ return this.currentId;
+ }
+ getCurrent() {
+ var currentId = this.getCurrentId();
+
+ if (currentId) {
+ return this.get(currentId);
+ }
+
+ return null;
+ }
+ getCurrentMember() {
+ var currentId = this.getCurrentId();
+
+ if (currentId) {
+ return this.getAllMembers()[currentId];
+ }
+
+ return null;
+ }
+ setChannelMember(member) {
+ var members = this.getChannelMembers();
+ members[member.channel_id] = member;
+ this.storeChannelMembers(members);
+ this.emitChange();
+ }
+ getCurrentExtraInfo() {
+ return this.getExtraInfo(this.getCurrentId());
+ }
+ getExtraInfo(channelId) {
+ var extra = null;
+
+ if (channelId) {
+ extra = this.getExtraInfos()[channelId];
+ }
+
+ if (extra) {
+ // create a defensive copy
+ extra = JSON.parse(JSON.stringify(extra));
+ } else {
+ extra = {members: []};
+ }
+
+ return extra;
+ }
+ pStoreChannel(channel) {
+ var channels = this.getChannels();
+ var found;
+
+ for (var i = 0; i < channels.length; i++) {
+ if (channels[i].id === channel.id) {
+ channels[i] = channel;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ channels.push(channel);
+ }
+
+ if (!Utils) {
+ Utils = require('utils/utils.jsx'); //eslint-disable-line global-require
+ }
+
+ channels.sort(Utils.sortByDisplayName);
+ this.storeChannels(channels);
+ }
+ storeChannels(channels) {
+ this.channels = channels;
+ }
+ getChannels() {
+ return this.channels;
+ }
+ pStoreChannelMember(channelMember) {
+ var members = this.getChannelMembers();
+ members[channelMember.channel_id] = channelMember;
+ this.storeChannelMembers(members);
+ }
+ storeChannelMembers(channelMembers) {
+ this.channelMembers = channelMembers;
+ }
+ getChannelMembers() {
+ return this.channelMembers;
+ }
+ storeMoreChannels(channels) {
+ this.moreChannels = channels;
+ }
+ getMoreChannels() {
+ return this.moreChannels;
+ }
+ storeExtraInfos(extraInfos) {
+ this.extraInfos = extraInfos;
+ }
+ getExtraInfos() {
+ return this.extraInfos;
+ }
+ isDefault(channel) {
+ return channel.name === Constants.DEFAULT_CHANNEL;
+ }
+
+ setPostMode(mode) {
+ this.postMode = mode;
+ }
+
+ getPostMode() {
+ return this.postMode;
+ }
+
+ setUnreadCount(id) {
+ const ch = this.get(id);
+ const chMember = this.getMember(id);
+
+ let chMentionCount = chMember.mention_count;
+ let chUnreadCount = ch.total_msg_count - chMember.msg_count - chMentionCount;
+
+ if (ch.type === 'D') {
+ chMentionCount = chUnreadCount;
+ chUnreadCount = 0;
+ } else if (chMember.notify_props && chMember.notify_props.mark_unread === NotificationPrefs.MENTION) {
+ chUnreadCount = 0;
+ }
+
+ this.unreadCounts[id] = {msgs: chUnreadCount, mentions: chMentionCount};
+ }
+
+ setUnreadCounts() {
+ const channels = this.getAll();
+ channels.forEach((ch) => {
+ this.setUnreadCount(ch.id);
+ });
+ }
+
+ getUnreadCount(id) {
+ return this.unreadCounts[id] || {msgs: 0, mentions: 0};
+ }
+
+ getUnreadCounts() {
+ return this.unreadCounts;
+ }
+}
+
+var ChannelStore = new ChannelStoreClass();
+
+ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+ var currentId;
+
+ switch (action.type) {
+ case ActionTypes.CLICK_CHANNEL:
+ ChannelStore.setCurrentId(action.id);
+ ChannelStore.resetCounts(action.id);
+ ChannelStore.setPostMode(ChannelStore.POST_MODE_CHANNEL);
+ ChannelStore.emitChange();
+ break;
+
+ case ActionTypes.RECEIVED_FOCUSED_POST: {
+ const post = action.post_list.posts[action.postId];
+ ChannelStore.setCurrentId(post.channel_id);
+ ChannelStore.setPostMode(ChannelStore.POST_MODE_FOCUS);
+ ChannelStore.emitChange();
+ break;
+ }
+
+ case ActionTypes.RECEIVED_CHANNELS:
+ ChannelStore.storeChannels(action.channels);
+ ChannelStore.storeChannelMembers(action.members);
+ currentId = ChannelStore.getCurrentId();
+ if (currentId && window.isActive) {
+ ChannelStore.resetCounts(currentId);
+ }
+ ChannelStore.setUnreadCounts();
+ ChannelStore.emitChange();
+ break;
+
+ case ActionTypes.RECEIVED_CHANNEL:
+ ChannelStore.pStoreChannel(action.channel);
+ if (action.member) {
+ ChannelStore.pStoreChannelMember(action.member);
+ }
+ currentId = ChannelStore.getCurrentId();
+ if (currentId && window.isActive) {
+ ChannelStore.resetCounts(currentId);
+ }
+ ChannelStore.setUnreadCount(action.channel.id);
+ ChannelStore.emitChange();
+ break;
+
+ case ActionTypes.RECEIVED_MORE_CHANNELS:
+ ChannelStore.storeMoreChannels(action.channels);
+ 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();
+ break;
+
+ case ActionTypes.LEAVE_CHANNEL:
+ ChannelStore.emitLeave(action.id);
+ break;
+
+ default:
+ break;
+ }
+});
+
+export default ChannelStore;
diff --git a/webapp/stores/error_store.jsx b/webapp/stores/error_store.jsx
new file mode 100644
index 000000000..776375a82
--- /dev/null
+++ b/webapp/stores/error_store.jsx
@@ -0,0 +1,80 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+
+import BrowserStore from 'stores/browser_store.jsx';
+
+const CHANGE_EVENT = 'change';
+
+class ErrorStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.emitChange = this.emitChange.bind(this);
+ this.addChangeListener = this.addChangeListener.bind(this);
+ this.removeChangeListener = this.removeChangeListener.bind(this);
+ this.getLastError = this.getLastError.bind(this);
+ this.storeLastError = this.storeLastError.bind(this);
+ }
+
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ getLastError() {
+ return BrowserStore.getItem('last_error');
+ }
+
+ storeLastError(error) {
+ BrowserStore.setItem('last_error', error);
+ }
+
+ getConnectionErrorCount() {
+ var count = BrowserStore.getItem('last_error_conn');
+
+ if (count == null) {
+ return 0;
+ }
+
+ return count;
+ }
+
+ setConnectionErrorCount(count) {
+ BrowserStore.setItem('last_error_conn', count);
+ }
+
+ clearLastError() {
+ BrowserStore.removeItem('last_error');
+ BrowserStore.removeItem('last_error_conn');
+ }
+}
+
+var ErrorStore = new ErrorStoreClass();
+
+ErrorStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+ switch (action.type) {
+ case ActionTypes.RECEIVED_ERROR:
+ ErrorStore.storeLastError(action.err);
+ ErrorStore.emitChange();
+ break;
+
+ default:
+ }
+});
+
+export default ErrorStore;
+window.ErrorStore = ErrorStore;
diff --git a/webapp/stores/file_store.jsx b/webapp/stores/file_store.jsx
new file mode 100644
index 000000000..2628685cc
--- /dev/null
+++ b/webapp/stores/file_store.jsx
@@ -0,0 +1,60 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import Constants from 'utils/constants.jsx';
+import EventEmitter from 'events';
+
+const ActionTypes = Constants.ActionTypes;
+
+const CHANGE_EVENT = 'changed';
+
+class FileStore extends EventEmitter {
+ constructor() {
+ super();
+
+ this.addChangeListener = this.addChangeListener.bind(this);
+ this.removeChangeListener = this.removeChangeListener.bind(this);
+ this.emitChange = this.emitChange.bind(this);
+
+ this.handleEventPayload = this.handleEventPayload.bind(this);
+ this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
+
+ this.fileInfo = new Map();
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+ emitChange(filename) {
+ this.emit(CHANGE_EVENT, filename);
+ }
+
+ hasInfo(filename) {
+ return this.fileInfo.has(filename);
+ }
+
+ getInfo(filename) {
+ return this.fileInfo.get(filename);
+ }
+
+ setInfo(filename, info) {
+ this.fileInfo.set(filename, info);
+ }
+
+ handleEventPayload(payload) {
+ const action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_FILE_INFO:
+ this.setInfo(action.filename, action.info);
+ this.emitChange(action.filename);
+ break;
+ }
+ }
+}
+
+export default new FileStore();
diff --git a/webapp/stores/localization_store.jsx b/webapp/stores/localization_store.jsx
new file mode 100644
index 000000000..64380d650
--- /dev/null
+++ b/webapp/stores/localization_store.jsx
@@ -0,0 +1,60 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+
+const CHANGE_EVENT = 'change';
+
+class LocalizationStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.currentLocale = 'en';
+ this.currentTranslations = null;
+ }
+
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ setCurrentLocale(locale, translations) {
+ this.currentLocale = locale;
+ this.currentTranslations = translations;
+ }
+
+ getLocale() {
+ return this.currentLocale;
+ }
+
+ getTranslations() {
+ return this.currentTranslations;
+ }
+}
+
+var LocalizationStore = new LocalizationStoreClass();
+LocalizationStore.setMaxListeners(0);
+
+LocalizationStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_LOCALE:
+ LocalizationStore.setCurrentLocale(action.locale, action.translations);
+ LocalizationStore.emitChange();
+ break;
+ default:
+ }
+});
+
+export default LocalizationStore;
diff --git a/webapp/stores/modal_store.jsx b/webapp/stores/modal_store.jsx
new file mode 100644
index 000000000..2a7921c40
--- /dev/null
+++ b/webapp/stores/modal_store.jsx
@@ -0,0 +1,47 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+
+class ModalStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.addModalListener = this.addModalListener.bind(this);
+ this.removeModalListener = this.removeModalListener.bind(this);
+
+ this.handleEventPayload = this.handleEventPayload.bind(this);
+ this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
+ }
+
+ addModalListener(action, callback) {
+ this.on(action, callback);
+ }
+
+ removeModalListener(action, callback) {
+ this.removeListener(action, callback);
+ }
+
+ handleEventPayload(payload) {
+ // toggle event handlers should accept a boolean show/hide value and can accept a map of arguments
+ const {type, value, ...args} = payload.action; //eslint-disable-line no-use-before-define
+
+ switch (type) {
+ case ActionTypes.TOGGLE_IMPORT_THEME_MODAL:
+ case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL:
+ case ActionTypes.TOGGLE_DELETE_POST_MODAL:
+ case ActionTypes.TOGGLE_GET_POST_LINK_MODAL:
+ case ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL:
+ case ActionTypes.TOGGLE_REGISTER_APP_MODAL:
+ this.emit(type, value, args);
+ break;
+ }
+ }
+}
+
+const ModalStore = new ModalStoreClass();
+export default ModalStore;
diff --git a/webapp/stores/post_store.jsx b/webapp/stores/post_store.jsx
new file mode 100644
index 000000000..903085760
--- /dev/null
+++ b/webapp/stores/post_store.jsx
@@ -0,0 +1,610 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import ChannelStore from 'stores/channel_store.jsx';
+import BrowserStore from 'stores/browser_store.jsx';
+import UserStore from 'stores/user_store.jsx';
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+
+const CHANGE_EVENT = 'change';
+const FOCUSED_POST_CHANGE = 'focused_post_change';
+const EDIT_POST_EVENT = 'edit_post';
+const POSTS_VIEW_JUMP_EVENT = 'post_list_jump';
+const SELECTED_POST_CHANGE_EVENT = 'selected_post_change';
+
+class PostStoreClass extends EventEmitter {
+ constructor() {
+ super();
+ this.selectedPostId = null;
+ this.postsInfo = {};
+ this.currentFocusedPostId = null;
+ }
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ emitPostFocused() {
+ this.emit(FOCUSED_POST_CHANGE);
+ }
+
+ addPostFocusedListener(callback) {
+ this.on(FOCUSED_POST_CHANGE, callback);
+ }
+
+ removePostFocusedListener(callback) {
+ this.removeListener(FOCUSED_POST_CHANGE, callback);
+ }
+
+ emitEditPost(post) {
+ this.emit(EDIT_POST_EVENT, post);
+ }
+
+ addEditPostListener(callback) {
+ this.on(EDIT_POST_EVENT, callback);
+ }
+
+ removeEditPostListner(callback) {
+ this.removeListener(EDIT_POST_EVENT, callback);
+ }
+
+ emitPostsViewJump(type, post) {
+ this.emit(POSTS_VIEW_JUMP_EVENT, type, post);
+ }
+
+ addPostsViewJumpListener(callback) {
+ this.on(POSTS_VIEW_JUMP_EVENT, callback);
+ }
+
+ removePostsViewJumpListener(callback) {
+ this.removeListener(POSTS_VIEW_JUMP_EVENT, callback);
+ }
+
+ jumpPostsViewToBottom() {
+ this.emitPostsViewJump(Constants.PostsViewJumpTypes.BOTTOM, null);
+ }
+
+ jumpPostsViewToPost(post) {
+ this.emitPostsViewJump(Constants.PostsViewJumpTypes.POST, post);
+ }
+
+ jumpPostsViewSidebarOpen() {
+ this.emitPostsViewJump(Constants.PostsViewJumpTypes.SIDEBAR_OPEN, null);
+ }
+
+ // All this does is makes sure the postsInfo is not null for the specified channel
+ makePostsInfo(id) {
+ if (!this.postsInfo.hasOwnProperty(id)) {
+ this.postsInfo[id] = {};
+ }
+ }
+
+ getPost(channelId, postId) {
+ const posts = this.postsInfo[channelId].postList;
+ let post = null;
+
+ if (posts.posts.hasOwnProperty(postId)) {
+ post = Object.assign({}, posts.posts[postId]);
+ }
+
+ return post;
+ }
+
+ getAllPosts(id) {
+ if (this.postsInfo.hasOwnProperty(id)) {
+ return Object.assign({}, this.postsInfo[id].postList);
+ }
+
+ return null;
+ }
+
+ getEarliestPost(id) {
+ if (this.postsInfo.hasOwnProperty(id)) {
+ return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[this.postsInfo[id].postList.order.length - 1]];
+ }
+
+ return null;
+ }
+
+ getLatestPost(id) {
+ if (this.postsInfo.hasOwnProperty(id)) {
+ return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[0]];
+ }
+
+ return null;
+ }
+
+ getVisiblePosts(id) {
+ if (this.postsInfo.hasOwnProperty(id) && this.postsInfo[id].hasOwnProperty('postList')) {
+ const postList = JSON.parse(JSON.stringify(this.postsInfo[id].postList));
+
+ // Only limit visibility if we are not focused on a post
+ if (this.currentFocusedPostId === null) {
+ postList.order = postList.order.slice(0, this.postsInfo[id].endVisible);
+ }
+
+ // Add pending posts
+ if (this.postsInfo[id].hasOwnProperty('pendingPosts')) {
+ Object.assign(postList.posts, this.postsInfo[id].pendingPosts.posts);
+ postList.order = this.postsInfo[id].pendingPosts.order.concat(postList.order);
+ }
+
+ return postList;
+ }
+
+ return null;
+ }
+
+ getVisibilityAtTop(id) {
+ if (this.postsInfo.hasOwnProperty(id)) {
+ return this.postsInfo[id].atTop && this.postsInfo[id].endVisible >= this.postsInfo[id].postList.order.length;
+ }
+
+ return false;
+ }
+
+ getVisibilityAtBottom(id) {
+ if (this.postsInfo.hasOwnProperty(id)) {
+ return this.postsInfo[id].atBottom;
+ }
+
+ return false;
+ }
+
+ // Returns true if posts need to be fetched
+ requestVisibilityIncrease(id, ammount) {
+ const endVisible = this.postsInfo[id].endVisible;
+ const postList = this.postsInfo[id].postList;
+ if (this.getVisibilityAtTop(id)) {
+ return false;
+ }
+ this.postsInfo[id].endVisible += ammount;
+ this.emitChange();
+ return endVisible + ammount > postList.order.length;
+ }
+
+ getFocusedPostId() {
+ return this.currentFocusedPostId;
+ }
+
+ storePosts(id, newPosts) {
+ if (isPostListNull(newPosts)) {
+ return;
+ }
+
+ const combinedPosts = makePostListNonNull(this.getAllPosts(id));
+
+ for (const pid in newPosts.posts) {
+ if (newPosts.posts.hasOwnProperty(pid)) {
+ const np = newPosts.posts[pid];
+ if (np.delete_at === 0) {
+ combinedPosts.posts[pid] = np;
+ if (combinedPosts.order.indexOf(pid) === -1 && newPosts.order.indexOf(pid) !== -1) {
+ combinedPosts.order.push(pid);
+ }
+ }
+ }
+ }
+
+ combinedPosts.order.sort((a, b) => {
+ if (combinedPosts.posts[a].create_at > combinedPosts.posts[b].create_at) {
+ return -1;
+ }
+ if (combinedPosts.posts[a].create_at < combinedPosts.posts[b].create_at) {
+ return 1;
+ }
+
+ return 0;
+ });
+
+ this.makePostsInfo(id);
+ this.postsInfo[id].postList = combinedPosts;
+ }
+
+ storePost(post) {
+ const postList = makePostListNonNull(this.getAllPosts(post.channel_id));
+
+ if (post.pending_post_id !== '') {
+ this.removePendingPost(post.channel_id, post.pending_post_id);
+ }
+
+ post.pending_post_id = '';
+
+ postList.posts[post.id] = post;
+ if (postList.order.indexOf(post.id) === -1) {
+ postList.order.unshift(post.id);
+ }
+
+ this.makePostsInfo(post.channel_id);
+ this.postsInfo[post.channel_id].postList = postList;
+ }
+
+ storeFocusedPost(postId, postList) {
+ const focusedPost = postList.posts[postId];
+ if (!focusedPost) {
+ return;
+ }
+ this.currentFocusedPostId = postId;
+ this.storePosts(postId, postList);
+ }
+
+ checkBounds(id, numRequested, postList, before) {
+ if (numRequested > postList.order.length) {
+ if (before) {
+ this.postsInfo[id].atTop = true;
+ } else {
+ this.postsInfo[id].atBottom = true;
+ }
+ }
+ }
+
+ clearFocusedPost() {
+ if (this.currentFocusedPostId != null) {
+ Reflect.deleteProperty(this.postsInfo, this.currentFocusedPostId);
+ this.currentFocusedPostId = null;
+ }
+ }
+
+ clearChannelVisibility(id, atBottom) {
+ this.makePostsInfo(id);
+ this.postsInfo[id].endVisible = Constants.POST_CHUNK_SIZE;
+ this.postsInfo[id].atTop = false;
+ this.postsInfo[id].atBottom = atBottom;
+ }
+
+ deletePost(post) {
+ const postInfo = this.postsInfo[post.channel_id];
+ if (!postInfo) {
+ // the post that has been deleted is in a channel that we haven't seen so just ignore it
+ return;
+ }
+
+ const postList = this.postsInfo[post.channel_id].postList;
+
+ if (isPostListNull(postList)) {
+ return;
+ }
+
+ if (post.id in postList.posts) {
+ // make sure to copy the post so that component state changes work properly
+ postList.posts[post.id] = Object.assign({}, post, {
+ state: Constants.POST_DELETED,
+ filenames: []
+ });
+ }
+ }
+
+ removePost(post) {
+ const channelId = post.channel_id;
+ this.makePostsInfo(channelId);
+ const postList = this.postsInfo[channelId].postList;
+ if (isPostListNull(postList)) {
+ return;
+ }
+
+ if (post.id in postList.posts) {
+ Reflect.deleteProperty(postList.posts, post.id);
+ }
+
+ const index = postList.order.indexOf(post.id);
+ if (index !== -1) {
+ postList.order.splice(index, 1);
+ }
+
+ for (const pid in postList.posts) {
+ if (!postList.posts.hasOwnProperty(pid)) {
+ continue;
+ }
+
+ if (postList.posts[pid].root_id === post.id) {
+ Reflect.deleteProperty(postList.posts, pid);
+ const commentIndex = postList.order.indexOf(pid);
+ if (commentIndex !== -1) {
+ postList.order.splice(commentIndex, 1);
+ }
+ }
+ }
+
+ this.postsInfo[channelId].postList = postList;
+ }
+
+ getPendingPosts(channelId) {
+ if (this.postsInfo.hasOwnProperty(channelId)) {
+ return this.postsInfo[channelId].pendingPosts;
+ }
+
+ return null;
+ }
+
+ storePendingPost(post) {
+ const copyPost = JSON.parse(JSON.stringify(post));
+ copyPost.state = Constants.POST_LOADING;
+
+ const postList = makePostListNonNull(this.getPendingPosts(copyPost.channel_id));
+
+ postList.posts[copyPost.pending_post_id] = copyPost;
+ postList.order.unshift(copyPost.pending_post_id);
+
+ this.makePostsInfo(copyPost.channel_id);
+ this.postsInfo[copyPost.channel_id].pendingPosts = postList;
+ this.emitChange();
+ }
+
+ removePendingPost(channelId, pendingPostId) {
+ const postList = makePostListNonNull(this.getPendingPosts(channelId));
+
+ Reflect.deleteProperty(postList.posts, pendingPostId);
+ const index = postList.order.indexOf(pendingPostId);
+ if (index === -1) {
+ return;
+ }
+
+ postList.order.splice(index, 1);
+
+ this.postsInfo[channelId].pendingPosts = postList;
+ this.emitChange();
+ }
+
+ clearPendingPosts(channelId) {
+ if (this.postsInfo.hasOwnProperty(channelId)) {
+ Reflect.deleteProperty(this.postsInfo[channelId], 'pendingPosts');
+ }
+ }
+
+ updatePendingPost(post) {
+ const copyPost = JSON.parse(JSON.stringify(post));
+ const postList = makePostListNonNull(this.getPendingPosts(copyPost.channel_id));
+
+ if (postList.order.indexOf(copyPost.pending_post_id) === -1) {
+ return;
+ }
+
+ postList.posts[copyPost.pending_post_id] = copyPost;
+ this.postsInfo[copyPost.channel_id].pendingPosts = postList;
+ this.emitChange();
+ }
+
+ storeSelectedPostId(postId) {
+ this.selectedPostId = postId;
+ }
+
+ getSelectedPostId() {
+ return this.selectedPostId;
+ }
+
+ getSelectedPost() {
+ if (this.selectedPostId == null) {
+ return null;
+ }
+
+ for (const k in this.postsInfo) {
+ if (this.postsInfo[k].postList.posts.hasOwnProperty(this.selectedPostId)) {
+ return this.postsInfo[k].postList.posts[this.selectedPostId];
+ }
+ }
+
+ return null;
+ }
+
+ getSelectedPostThread() {
+ if (this.selectedPostId == null) {
+ return null;
+ }
+
+ let posts;
+ let pendingPosts;
+ for (const k in this.postsInfo) {
+ if (this.postsInfo[k].postList.posts.hasOwnProperty(this.selectedPostId)) {
+ posts = this.postsInfo[k].postList.posts;
+ if (this.postsInfo[k].pendingPosts != null) {
+ pendingPosts = this.postsInfo[k].pendingPosts.posts;
+ }
+ }
+ }
+
+ const threadPosts = {};
+ const rootId = this.selectedPostId;
+ for (const k in posts) {
+ if (posts[k].root_id === rootId) {
+ threadPosts[k] = JSON.parse(JSON.stringify(posts[k]));
+ }
+ }
+
+ for (const k in pendingPosts) {
+ if (pendingPosts[k].root_id === rootId) {
+ threadPosts[k] = JSON.parse(JSON.stringify(pendingPosts[k]));
+ }
+ }
+
+ return threadPosts;
+ }
+
+ emitSelectedPostChange(fromSearch) {
+ this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch);
+ }
+
+ addSelectedPostChangeListener(callback) {
+ this.on(SELECTED_POST_CHANGE_EVENT, callback);
+ }
+
+ removeSelectedPostChangeListener(callback) {
+ this.removeListener(SELECTED_POST_CHANGE_EVENT, callback);
+ }
+
+ getCurrentUsersLatestPost(channelId, rootId) {
+ const userId = UserStore.getCurrentId();
+ var postList = makePostListNonNull(this.getAllPosts(channelId));
+ var i = 0;
+ var len = postList.order.length;
+ var lastPost = null;
+
+ for (i; i < len; i++) {
+ const post = postList.posts[postList.order[i]];
+ if (post.user_id === userId && (post.props && !post.props.from_webhook || !post.props)) {
+ if (rootId) {
+ if (post.root_id === rootId || post.id === rootId) {
+ lastPost = post;
+ break;
+ }
+ } else {
+ lastPost = post;
+ break;
+ }
+ }
+ }
+
+ return lastPost;
+ }
+
+ getEmptyDraft() {
+ return {message: '', uploadsInProgress: [], previews: []};
+ }
+ storeCurrentDraft(draft) {
+ var channelId = ChannelStore.getCurrentId();
+ BrowserStore.setGlobalItem('draft_' + channelId, draft);
+ }
+ getCurrentDraft() {
+ var channelId = ChannelStore.getCurrentId();
+ return this.getDraft(channelId);
+ }
+ storeDraft(channelId, draft) {
+ BrowserStore.setGlobalItem('draft_' + channelId, draft);
+ }
+ getDraft(channelId) {
+ return BrowserStore.getGlobalItem('draft_' + channelId, this.getEmptyDraft());
+ }
+ storeCommentDraft(parentPostId, draft) {
+ BrowserStore.setGlobalItem('comment_draft_' + parentPostId, draft);
+ }
+ getCommentDraft(parentPostId) {
+ return BrowserStore.getGlobalItem('comment_draft_' + parentPostId, this.getEmptyDraft());
+ }
+ clearDraftUploads() {
+ BrowserStore.actionOnGlobalItemsWithPrefix('draft_', (key, value) => {
+ if (value) {
+ value.uploadsInProgress = [];
+ BrowserStore.setItem(key, value);
+ }
+ });
+ }
+ clearCommentDraftUploads() {
+ BrowserStore.actionOnGlobalItemsWithPrefix('comment_draft_', (key, value) => {
+ if (value) {
+ value.uploadsInProgress = [];
+ BrowserStore.setItem(key, value);
+ }
+ });
+ }
+ getCommentCount(post) {
+ const posts = this.getAllPosts(post.channel_id).posts;
+
+ let commentCount = 0;
+ for (const id in posts) {
+ if (posts.hasOwnProperty(id)) {
+ if (posts[id].root_id === post.id) {
+ commentCount += 1;
+ }
+ }
+ }
+
+ return commentCount;
+ }
+}
+
+var PostStore = new PostStoreClass();
+
+PostStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_POSTS: {
+ const id = PostStore.currentFocusedPostId == null ? action.id : PostStore.currentFocusedPostId;
+ PostStore.checkBounds(id, action.numRequested, makePostListNonNull(action.post_list), action.before);
+ PostStore.storePosts(id, makePostListNonNull(action.post_list));
+ PostStore.emitChange();
+ break;
+ }
+ case ActionTypes.RECEIVED_FOCUSED_POST:
+ PostStore.clearChannelVisibility(action.postId, false);
+ PostStore.storeFocusedPost(action.postId, makePostListNonNull(action.post_list));
+ PostStore.emitChange();
+ break;
+ case ActionTypes.RECEIVED_POST:
+ PostStore.storePost(action.post);
+ PostStore.emitChange();
+ break;
+ case ActionTypes.RECEIVED_EDIT_POST:
+ PostStore.emitEditPost(action);
+ PostStore.emitChange();
+ break;
+ case ActionTypes.CLICK_CHANNEL:
+ PostStore.clearFocusedPost();
+ PostStore.clearChannelVisibility(action.id, true);
+ break;
+ case ActionTypes.CREATE_POST:
+ PostStore.storePendingPost(action.post);
+ PostStore.storeDraft(action.post.channel_id, null);
+ PostStore.jumpPostsViewToBottom();
+ break;
+ case ActionTypes.POST_DELETED:
+ PostStore.deletePost(action.post);
+ PostStore.emitChange();
+ break;
+ case ActionTypes.REMOVE_POST:
+ PostStore.removePost(action.post);
+ PostStore.emitChange();
+ break;
+ case ActionTypes.RECEIVED_POST_SELECTED:
+ PostStore.storeSelectedPostId(action.postId);
+ PostStore.emitSelectedPostChange(action.from_search);
+ break;
+ default:
+ }
+});
+
+export default PostStore;
+
+function makePostListNonNull(pl) {
+ var postList = pl;
+ if (postList == null) {
+ postList = {order: [], posts: {}};
+ }
+
+ if (postList.order == null) {
+ postList.order = [];
+ }
+
+ if (postList.posts == null) {
+ postList.posts = {};
+ }
+
+ return postList;
+}
+
+function isPostListNull(pl) {
+ if (pl == null) {
+ return true;
+ }
+
+ if (pl.posts == null) {
+ return true;
+ }
+
+ if (pl.order == null) {
+ return true;
+ }
+
+ return false;
+}
diff --git a/webapp/stores/preference_store.jsx b/webapp/stores/preference_store.jsx
new file mode 100644
index 000000000..df77f0d51
--- /dev/null
+++ b/webapp/stores/preference_store.jsx
@@ -0,0 +1,178 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import BrowserStore from './browser_store.jsx';
+import EventEmitter from 'events';
+import UserStore from 'stores/user_store.jsx';
+
+const CHANGE_EVENT = 'change';
+
+function getPreferenceKey(category, name) {
+ return `${category}-${name}`;
+}
+
+function getPreferenceKeyForModel(preference) {
+ return `${preference.category}-${preference.name}`;
+}
+
+class PreferenceStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.getAllPreferences = this.getAllPreferences.bind(this);
+ this.get = this.get.bind(this);
+ this.getBool = this.getBool.bind(this);
+ this.getInt = this.getInt.bind(this);
+ this.getPreference = this.getPreference.bind(this);
+ this.getCategory = this.getCategory.bind(this);
+ this.getPreferencesWhere = this.getPreferencesWhere.bind(this);
+ this.setAllPreferences = this.setAllPreferences.bind(this);
+ this.setPreference = this.setPreference.bind(this);
+
+ this.emitChange = this.emitChange.bind(this);
+ this.addChangeListener = this.addChangeListener.bind(this);
+ this.removeChangeListener = this.removeChangeListener.bind(this);
+
+ this.handleEventPayload = this.handleEventPayload.bind(this);
+ this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
+ }
+
+ getAllPreferences() {
+ return new Map(BrowserStore.getItem('preferences', []));
+ }
+
+ get(category, name, defaultValue = '') {
+ const preference = this.getAllPreferences().get(getPreferenceKey(category, name));
+
+ if (!preference) {
+ return defaultValue;
+ }
+
+ return preference.value || defaultValue;
+ }
+
+ getBool(category, name, defaultValue = false) {
+ const preference = this.getAllPreferences().get(getPreferenceKey(category, name));
+
+ if (!preference) {
+ return defaultValue;
+ }
+
+ // prevent a non-false default value from being returned instead of an actual false value
+ if (preference.value === 'false') {
+ return false;
+ }
+
+ return (preference.value !== 'false') || defaultValue;
+ }
+
+ getInt(category, name, defaultValue = 0) {
+ const preference = this.getAllPreferences().get(getPreferenceKey(category, name));
+
+ if (!preference) {
+ return defaultValue;
+ }
+
+ // prevent a non-zero default value from being returned instead of an actual 0 value
+ if (preference.value === '0') {
+ return 0;
+ }
+
+ return parseInt(preference.value, 10) || defaultValue;
+ }
+
+ getPreference(category, name, defaultValue = {}) {
+ return this.getAllPreferences().get(getPreferenceKey(category, name)) || defaultValue;
+ }
+
+ getCategory(category) {
+ return this.getPreferencesWhere((preference) => (preference.category === category));
+ }
+
+ getPreferencesWhere(pred) {
+ const all = this.getAllPreferences();
+ const preferences = [];
+
+ for (const [, preference] of all) {
+ if (pred(preference)) {
+ preferences.push(preference);
+ }
+ }
+
+ return preferences;
+ }
+
+ setAllPreferences(preferences) {
+ // note that we store the preferences as an array of key-value pairs so that we can deserialize
+ // it as a proper Map instead of an object
+ BrowserStore.setItem('preferences', [...preferences]);
+ }
+
+ setPreference(category, name, value) {
+ const preferences = this.getAllPreferences();
+
+ const key = getPreferenceKey(category, name);
+ let preference = preferences.get(key);
+
+ if (!preference) {
+ preference = {
+ user_id: UserStore.getCurrentId(),
+ category,
+ name
+ };
+ }
+ preference.value = value;
+
+ preferences.set(key, preference);
+
+ this.setAllPreferences(preferences);
+
+ return preference;
+ }
+
+ setPreferences(newPreferences) {
+ const preferences = this.getAllPreferences();
+
+ for (const preference of newPreferences) {
+ preferences.set(getPreferenceKeyForModel(preference), preference);
+ }
+
+ this.setAllPreferences(preferences);
+ }
+
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ handleEventPayload(payload) {
+ const action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_PREFERENCE: {
+ const preference = action.preference;
+ this.setPreference(preference.category, preference.name, preference.value);
+ this.emitChange();
+ break;
+ }
+ case ActionTypes.RECEIVED_PREFERENCES:
+ this.setPreferences(action.preferences);
+ this.emitChange();
+ break;
+ }
+ }
+}
+
+const PreferenceStore = new PreferenceStoreClass();
+export default PreferenceStore;
+window.PreferenceStore = PreferenceStore;
diff --git a/webapp/stores/search_store.jsx b/webapp/stores/search_store.jsx
new file mode 100644
index 000000000..c7818a858
--- /dev/null
+++ b/webapp/stores/search_store.jsx
@@ -0,0 +1,137 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import BrowserStore from 'stores/browser_store.jsx';
+
+import Constants from 'utils/constants.jsx';
+var ActionTypes = Constants.ActionTypes;
+
+var CHANGE_EVENT = 'change';
+var SEARCH_CHANGE_EVENT = 'search_change';
+var SEARCH_TERM_CHANGE_EVENT = 'search_term_change';
+var SHOW_SEARCH_EVENT = 'show_search';
+
+class SearchStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.emitChange = this.emitChange.bind(this);
+ this.addChangeListener = this.addChangeListener.bind(this);
+ this.removeChangeListener = this.removeChangeListener.bind(this);
+
+ this.emitSearchChange = this.emitSearchChange.bind(this);
+ this.addSearchChangeListener = this.addSearchChangeListener.bind(this);
+ this.removeSearchChangeListener = this.removeSearchChangeListener.bind(this);
+
+ this.emitSearchTermChange = this.emitSearchTermChange.bind(this);
+ this.addSearchTermChangeListener = this.addSearchTermChangeListener.bind(this);
+ this.removeSearchTermChangeListener = this.removeSearchTermChangeListener.bind(this);
+
+ this.emitShowSearch = this.emitShowSearch.bind(this);
+ this.addShowSearchListener = this.addShowSearchListener.bind(this);
+ this.removeShowSearchListener = this.removeShowSearchListener.bind(this);
+
+ this.getSearchResults = this.getSearchResults.bind(this);
+ this.getIsMentionSearch = this.getIsMentionSearch.bind(this);
+
+ this.storeSearchTerm = this.storeSearchTerm.bind(this);
+ this.getSearchTerm = this.getSearchTerm.bind(this);
+
+ this.storeSearchResults = this.storeSearchResults.bind(this);
+ }
+
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ emitSearchChange() {
+ this.emit(SEARCH_CHANGE_EVENT);
+ }
+
+ addSearchChangeListener(callback) {
+ this.on(SEARCH_CHANGE_EVENT, callback);
+ }
+
+ removeSearchChangeListener(callback) {
+ this.removeListener(SEARCH_CHANGE_EVENT, callback);
+ }
+
+ emitSearchTermChange(doSearch, isMentionSearch) {
+ this.emit(SEARCH_TERM_CHANGE_EVENT, doSearch, isMentionSearch);
+ }
+
+ addSearchTermChangeListener(callback) {
+ this.on(SEARCH_TERM_CHANGE_EVENT, callback);
+ }
+
+ removeSearchTermChangeListener(callback) {
+ this.removeListener(SEARCH_TERM_CHANGE_EVENT, callback);
+ }
+
+ emitShowSearch() {
+ this.emit(SHOW_SEARCH_EVENT);
+ }
+
+ addShowSearchListener(callback) {
+ this.on(SHOW_SEARCH_EVENT, callback);
+ }
+
+ removeShowSearchListener(callback) {
+ this.removeListener(SHOW_SEARCH_EVENT, callback);
+ }
+
+ getSearchResults() {
+ return BrowserStore.getItem('search_results');
+ }
+
+ getIsMentionSearch() {
+ return BrowserStore.getItem('is_mention_search');
+ }
+
+ storeSearchTerm(term) {
+ BrowserStore.setItem('search_term', term);
+ }
+
+ getSearchTerm() {
+ return BrowserStore.getItem('search_term');
+ }
+
+ storeSearchResults(results, isMentionSearch) {
+ BrowserStore.setItem('search_results', results);
+ BrowserStore.setItem('is_mention_search', Boolean(isMentionSearch));
+ }
+}
+
+var SearchStore = new SearchStoreClass();
+
+SearchStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_SEARCH:
+ SearchStore.storeSearchResults(action.results, action.is_mention_search);
+ SearchStore.emitSearchChange();
+ break;
+ case ActionTypes.RECEIVED_SEARCH_TERM:
+ SearchStore.storeSearchTerm(action.term);
+ SearchStore.emitSearchTermChange(action.do_search, action.is_mention_search);
+ break;
+ case ActionTypes.SHOW_SEARCH:
+ SearchStore.emitShowSearch();
+ break;
+ default:
+ }
+});
+
+export default SearchStore;
diff --git a/webapp/stores/socket_store.jsx b/webapp/stores/socket_store.jsx
new file mode 100644
index 000000000..5d6302743
--- /dev/null
+++ b/webapp/stores/socket_store.jsx
@@ -0,0 +1,343 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import $ from 'jquery';
+import UserStore from './user_store.jsx';
+import PostStore from './post_store.jsx';
+import ChannelStore from './channel_store.jsx';
+import BrowserStore from './browser_store.jsx';
+import ErrorStore from './error_store.jsx';
+import EventEmitter from 'events';
+
+import * as Utils from 'utils/utils.jsx';
+import * as AsyncClient from 'utils/async_client.jsx';
+import * as GlobalActions from 'action_creators/global_actions.jsx';
+
+import Constants from 'utils/constants.jsx';
+const SocketEvents = Constants.SocketEvents;
+
+const CHANGE_EVENT = 'change';
+
+var conn;
+
+class SocketStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.initialize = this.initialize.bind(this);
+ this.emitChange = this.emitChange.bind(this);
+ this.addChangeListener = this.addChangeListener.bind(this);
+ this.removeChangeListener = this.removeChangeListener.bind(this);
+ this.sendMessage = this.sendMessage.bind(this);
+ this.close = this.close.bind(this);
+
+ this.failCount = 0;
+ this.isInitialize = false;
+
+ this.translations = this.getDefaultTranslations();
+
+ this.initialize();
+ }
+
+ initialize() {
+ if (!UserStore.getCurrentId()) {
+ return;
+ }
+
+ this.setMaxListeners(0);
+
+ if (window.WebSocket && !conn) {
+ var protocol = 'ws://';
+ if (window.location.protocol === 'https:') {
+ protocol = 'wss://';
+ }
+
+ var connUrl = protocol + location.host + ((/:\d+/).test(location.host) ? '' : Utils.getWebsocketPort(protocol)) + '/api/v1/websocket';
+
+ if (this.failCount === 0) {
+ console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
+ }
+
+ conn = new WebSocket(connUrl);
+
+ conn.onopen = () => {
+ if (this.failCount > 0) {
+ console.log('websocket re-established connection'); //eslint-disable-line no-console
+ AsyncClient.getChannels();
+ AsyncClient.getPosts(ChannelStore.getCurrentId());
+ }
+
+ if (this.isInitialize) {
+ ErrorStore.clearLastError();
+ ErrorStore.emitChange();
+ }
+
+ this.isInitialize = true;
+ this.failCount = 0;
+ };
+
+ conn.onclose = () => {
+ conn = null;
+
+ if (this.failCount === 0) {
+ console.log('websocket closed'); //eslint-disable-line no-console
+ }
+
+ this.failCount = this.failCount + 1;
+
+ if (this.failCount > 7) {
+ ErrorStore.storeLastError({message: this.translations.socketError});
+ }
+
+ ErrorStore.setConnectionErrorCount(this.failCount);
+ ErrorStore.emitChange();
+
+ setTimeout(
+ () => {
+ this.initialize();
+ },
+ 3000
+ );
+ };
+
+ conn.onerror = (evt) => {
+ if (this.failCount <= 1) {
+ console.log('websocket error'); //eslint-disable-line no-console
+ console.log(evt); //eslint-disable-line no-console
+ }
+ };
+
+ conn.onmessage = (evt) => {
+ const msg = JSON.parse(evt.data);
+ this.handleMessage(msg);
+ this.emitChange(msg);
+ };
+ }
+ }
+
+ emitChange(msg) {
+ this.emit(CHANGE_EVENT, msg);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ handleMessage(msg) {
+ switch (msg.action) {
+ case SocketEvents.POSTED:
+ case SocketEvents.EPHEMERAL_MESSAGE:
+ handleNewPostEvent(msg, this.translations);
+ break;
+
+ case SocketEvents.POST_EDITED:
+ handlePostEditEvent(msg);
+ break;
+
+ case SocketEvents.POST_DELETED:
+ handlePostDeleteEvent(msg);
+ break;
+
+ case SocketEvents.NEW_USER:
+ handleNewUserEvent();
+ break;
+
+ case SocketEvents.USER_ADDED:
+ handleUserAddedEvent(msg);
+ break;
+
+ case SocketEvents.USER_REMOVED:
+ handleUserRemovedEvent(msg);
+ break;
+
+ case SocketEvents.CHANNEL_VIEWED:
+ handleChannelViewedEvent(msg);
+ break;
+
+ case SocketEvents.PREFERENCE_CHANGED:
+ handlePreferenceChangedEvent(msg);
+ break;
+
+ default:
+ }
+ }
+
+ sendMessage(msg) {
+ if (conn && conn.readyState === WebSocket.OPEN) {
+ conn.send(JSON.stringify(msg));
+ } else if (!conn || conn.readyState === WebSocket.Closed) {
+ conn = null;
+ this.initialize();
+ }
+ }
+
+ setTranslations(messages) {
+ this.translations = messages;
+ }
+
+ getDefaultTranslations() {
+ return ({
+ socketError: 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.',
+ someone: 'Someone',
+ posted: 'Posted',
+ uploadedImage: ' uploaded an image',
+ uploadedFile: ' uploaded a file',
+ something: ' did something new',
+ wrote: ' wrote: '
+ });
+ }
+
+ close() {
+ if (conn && conn.readyState === WebSocket.OPEN) {
+ conn.close();
+ }
+ }
+}
+
+function handleNewPostEvent(msg, translations) {
+ // Store post
+ const post = JSON.parse(msg.props.post);
+ GlobalActions.emitPostRecievedEvent(post);
+
+ // Update channel state
+ if (ChannelStore.getCurrentId() === msg.channel_id) {
+ if (window.isActive) {
+ AsyncClient.updateLastViewedAt();
+ } else {
+ AsyncClient.getChannel(msg.channel_id);
+ }
+ } else if (UserStore.getCurrentId() !== msg.user_id || post.type !== Constants.POST_TYPE_JOIN_LEAVE) {
+ AsyncClient.getChannel(msg.channel_id);
+ }
+
+ // Send desktop notification
+ if ((UserStore.getCurrentId() !== msg.user_id || post.props.from_webhook === 'true') && !Utils.isSystemMessage(post)) {
+ const msgProps = msg.props;
+
+ let mentions = [];
+ if (msgProps.mentions) {
+ mentions = JSON.parse(msg.props.mentions);
+ }
+
+ const channel = ChannelStore.get(msg.channel_id);
+ const user = UserStore.getCurrentUser();
+ const member = ChannelStore.getMember(msg.channel_id);
+
+ let notifyLevel = member && member.notify_props ? member.notify_props.desktop : 'default';
+ if (notifyLevel === 'default') {
+ notifyLevel = user.notify_props.desktop;
+ }
+
+ if (notifyLevel === 'none') {
+ return;
+ } else if (notifyLevel === 'mention' && mentions.indexOf(user.id) === -1 && channel.type !== Constants.DM_CHANNEL) {
+ return;
+ }
+
+ let username = translations.someone;
+ if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
+ username = post.props.override_username;
+ } else if (UserStore.hasProfile(msg.user_id)) {
+ username = UserStore.getProfile(msg.user_id).username;
+ }
+
+ let title = translations.posted;
+ if (channel) {
+ title = channel.display_name;
+ }
+
+ let notifyText = post.message.replace(/\n+/g, ' ');
+ if (notifyText.length > 50) {
+ notifyText = notifyText.substring(0, 49) + '...';
+ }
+
+ if (notifyText.length === 0) {
+ if (msgProps.image) {
+ Utils.notifyMe(title, username + translations.uploadedImage, channel);
+ } else if (msgProps.otherFile) {
+ Utils.notifyMe(title, username + translations.uploadedFile, channel);
+ } else {
+ Utils.notifyMe(title, username + translations.something, channel);
+ }
+ } else {
+ Utils.notifyMe(title, username + translations.wrote + notifyText, channel);
+ }
+ if (!user.notify_props || user.notify_props.desktop_sound === 'true') {
+ Utils.ding();
+ }
+ }
+}
+
+function handlePostEditEvent(msg) {
+ // Store post
+ const post = JSON.parse(msg.props.post);
+ PostStore.storePost(post);
+ PostStore.emitChange();
+
+ // Update channel state
+ if (ChannelStore.getCurrentId() === msg.channel_id) {
+ if (window.isActive) {
+ AsyncClient.updateLastViewedAt();
+ }
+ }
+}
+
+function handlePostDeleteEvent(msg) {
+ const post = JSON.parse(msg.props.post);
+ GlobalActions.emitPostDeletedEvent(post);
+}
+
+function handleNewUserEvent() {
+ AsyncClient.getProfiles();
+ AsyncClient.getChannelExtraInfo();
+}
+
+function handleUserAddedEvent(msg) {
+ if (ChannelStore.getCurrentId() === msg.channel_id) {
+ AsyncClient.getChannelExtraInfo();
+ }
+
+ if (UserStore.getCurrentId() === msg.user_id) {
+ AsyncClient.getChannel(msg.channel_id);
+ }
+}
+
+function handleUserRemovedEvent(msg) {
+ if (UserStore.getCurrentId() === msg.user_id) {
+ AsyncClient.getChannels();
+
+ if (msg.props.remover_id !== msg.user_id &&
+ msg.channel_id === ChannelStore.getCurrentId() &&
+ $('#removed_from_channel').length > 0) {
+ var sentState = {};
+ sentState.channelName = ChannelStore.getCurrent().display_name;
+ sentState.remover = UserStore.getProfile(msg.props.remover_id).username;
+
+ BrowserStore.setItem('channel-removed-state', sentState);
+ $('#removed_from_channel').modal('show');
+ }
+ } else if (ChannelStore.getCurrentId() === msg.channel_id) {
+ AsyncClient.getChannelExtraInfo();
+ }
+}
+
+function handleChannelViewedEvent(msg) {
+ // Useful for when multiple devices have the app open to different channels
+ if (ChannelStore.getCurrentId() !== msg.channel_id && UserStore.getCurrentId() === msg.user_id) {
+ AsyncClient.getChannel(msg.channel_id);
+ }
+}
+
+function handlePreferenceChangedEvent(msg) {
+ const preference = JSON.parse(msg.props.preference);
+ GlobalActions.emitPreferenceChangedEvent(preference);
+}
+
+var SocketStore = new SocketStoreClass();
+
+export default SocketStore;
+window.SocketStore = SocketStore;
diff --git a/webapp/stores/suggestion_store.jsx b/webapp/stores/suggestion_store.jsx
new file mode 100644
index 000000000..eeb09fa9e
--- /dev/null
+++ b/webapp/stores/suggestion_store.jsx
@@ -0,0 +1,261 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import Constants from 'utils/constants.jsx';
+import EventEmitter from 'events';
+
+const ActionTypes = Constants.ActionTypes;
+
+const COMPLETE_WORD_EVENT = 'complete_word';
+const PRETEXT_CHANGED_EVENT = 'pretext_changed';
+const SUGGESTIONS_CHANGED_EVENT = 'suggestions_changed';
+
+class SuggestionStore extends EventEmitter {
+ constructor() {
+ super();
+
+ this.addSuggestionsChangedListener = this.addSuggestionsChangedListener.bind(this);
+ this.removeSuggestionsChangedListener = this.removeSuggestionsChangedListener.bind(this);
+ this.emitSuggestionsChanged = this.emitSuggestionsChanged.bind(this);
+
+ this.addPretextChangedListener = this.addPretextChangedListener.bind(this);
+ this.removePretextChangedListener = this.removePretextChangedListener.bind(this);
+ this.emitPretextChanged = this.emitPretextChanged.bind(this);
+
+ this.addCompleteWordListener = this.addCompleteWordListener.bind(this);
+ this.removeCompleteWordListener = this.removeCompleteWordListener.bind(this);
+ this.emitCompleteWord = this.emitCompleteWord.bind(this);
+
+ this.handleEventPayload = this.handleEventPayload.bind(this);
+ this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
+
+ // this.suggestions stores the state of all SuggestionBoxes by mapping their unique identifier to an
+ // object with the following fields:
+ // pretext: the text before the cursor
+ // matchedPretext: the text before the cursor that will be replaced if an autocomplete term is selected
+ // terms: a list of strings which the previously typed text may be replaced by
+ // items: a list of objects backing the terms which may be used in rendering
+ // components: a list of react components that can be used to render their corresponding item
+ // selection: the term currently selected by the keyboard
+ this.suggestions = new Map();
+ }
+
+ addSuggestionsChangedListener(id, callback) {
+ this.on(SUGGESTIONS_CHANGED_EVENT + id, callback);
+ }
+ removeSuggestionsChangedListener(id, callback) {
+ this.removeListener(SUGGESTIONS_CHANGED_EVENT + id, callback);
+ }
+ emitSuggestionsChanged(id) {
+ this.emit(SUGGESTIONS_CHANGED_EVENT + id);
+ }
+
+ addPretextChangedListener(id, callback) {
+ this.on(PRETEXT_CHANGED_EVENT + id, callback);
+ }
+ removePretextChangedListener(id, callback) {
+ this.removeListener(PRETEXT_CHANGED_EVENT + id, callback);
+ }
+ emitPretextChanged(id, pretext) {
+ this.emit(PRETEXT_CHANGED_EVENT + id, pretext);
+ }
+
+ addCompleteWordListener(id, callback) {
+ this.on(COMPLETE_WORD_EVENT + id, callback);
+ }
+ removeCompleteWordListener(id, callback) {
+ this.removeListener(COMPLETE_WORD_EVENT + id, callback);
+ }
+ emitCompleteWord(id, term) {
+ this.emit(COMPLETE_WORD_EVENT + id, term);
+ }
+
+ registerSuggestionBox(id) {
+ this.suggestions.set(id, {
+ pretext: '',
+ matchedPretext: '',
+ terms: [],
+ items: [],
+ components: [],
+ selection: ''
+ });
+ }
+
+ unregisterSuggestionBox(id) {
+ this.suggestions.delete(id);
+ }
+
+ clearSuggestions(id) {
+ const suggestion = this.suggestions.get(id);
+
+ suggestion.matchedPretext = '';
+ suggestion.terms = [];
+ suggestion.items = [];
+ suggestion.components = [];
+ }
+
+ clearSelection(id) {
+ const suggestion = this.suggestions.get(id);
+
+ suggestion.selection = '';
+ }
+
+ hasSuggestions(id) {
+ return this.suggestions.get(id).terms.length > 0;
+ }
+
+ setPretext(id, pretext) {
+ const suggestion = this.suggestions.get(id);
+
+ suggestion.pretext = pretext;
+ }
+
+ setMatchedPretext(id, matchedPretext) {
+ const suggestion = this.suggestions.get(id);
+
+ suggestion.matchedPretext = matchedPretext;
+ }
+
+ addSuggestion(id, term, item, component) {
+ const suggestion = this.suggestions.get(id);
+
+ suggestion.terms.push(term);
+ suggestion.items.push(item);
+ suggestion.components.push(component);
+ }
+
+ addSuggestions(id, terms, items, component) {
+ const suggestion = this.suggestions.get(id);
+
+ suggestion.terms.push(...terms);
+ suggestion.items.push(...items);
+
+ for (let i = 0; i < terms.length; i++) {
+ suggestion.components.push(component);
+ }
+ }
+
+ // make sure that if suggestions exist, then one of them is selected. return true if the selection changes.
+ ensureSelectionExists(id) {
+ const suggestion = this.suggestions.get(id);
+
+ if (suggestion.terms.length > 0) {
+ // if the current selection is no longer in the map, select the first term in the list
+ if (!suggestion.selection || suggestion.terms.indexOf(suggestion.selection) === -1) {
+ suggestion.selection = suggestion.terms[0];
+
+ return true;
+ }
+ } else if (suggestion.selection) {
+ suggestion.selection = '';
+
+ return true;
+ }
+
+ return false;
+ }
+
+ getPretext(id) {
+ return this.suggestions.get(id).pretext;
+ }
+
+ getMatchedPretext(id) {
+ return this.suggestions.get(id).matchedPretext;
+ }
+
+ getItems(id) {
+ return this.suggestions.get(id).items;
+ }
+
+ getTerms(id) {
+ return this.suggestions.get(id).terms;
+ }
+
+ getComponents(id) {
+ return this.suggestions.get(id).components;
+ }
+
+ getSelection(id) {
+ return this.suggestions.get(id).selection;
+ }
+
+ selectNext(id) {
+ this.setSelectionByDelta(id, 1);
+ }
+
+ selectPrevious(id) {
+ this.setSelectionByDelta(id, -1);
+ }
+
+ setSelectionByDelta(id, delta) {
+ const suggestion = this.suggestions.get(id);
+
+ let selectionIndex = suggestion.terms.indexOf(suggestion.selection);
+
+ if (selectionIndex === -1) {
+ // this should never happen since selection should always be in terms
+ throw new Error('selection is not in terms');
+ }
+
+ selectionIndex += delta;
+
+ if (selectionIndex < 0) {
+ selectionIndex = 0;
+ } else if (selectionIndex > suggestion.terms.length - 1) {
+ selectionIndex = suggestion.terms.length - 1;
+ }
+
+ suggestion.selection = suggestion.terms[selectionIndex];
+ }
+
+ handleEventPayload(payload) {
+ const {type, id, ...other} = payload.action; // eslint-disable-line no-use-before-define
+
+ switch (type) {
+ case ActionTypes.SUGGESTION_PRETEXT_CHANGED:
+ this.clearSuggestions(id);
+
+ this.setPretext(id, other.pretext);
+ this.emitPretextChanged(id, other.pretext);
+
+ this.ensureSelectionExists(id);
+ this.emitSuggestionsChanged(id);
+ break;
+ case ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS:
+ if (this.getMatchedPretext(id) === '') {
+ this.setMatchedPretext(id, other.matchedPretext);
+
+ // 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);
+
+ this.ensureSelectionExists(id);
+ this.emitSuggestionsChanged(id);
+ }
+ break;
+ case ActionTypes.SUGGESTION_CLEAR_SUGGESTIONS:
+ this.clearSuggestions(id);
+ this.clearSelection(id);
+ this.emitSuggestionsChanged(id);
+ break;
+ case ActionTypes.SUGGESTION_SELECT_NEXT:
+ this.selectNext(id);
+ this.emitSuggestionsChanged(id);
+ break;
+ case ActionTypes.SUGGESTION_SELECT_PREVIOUS:
+ this.selectPrevious(id);
+ this.emitSuggestionsChanged(id);
+ break;
+ case ActionTypes.SUGGESTION_COMPLETE_WORD:
+ this.emitCompleteWord(id, other.term || this.getSelection(id), this.getMatchedPretext(id));
+
+ this.setPretext(id, '');
+ this.clearSuggestions(id);
+ this.clearSelection(id);
+ this.emitSuggestionsChanged(id);
+ break;
+ }
+ }
+}
+
+export default new SuggestionStore();
diff --git a/webapp/stores/team_store.jsx b/webapp/stores/team_store.jsx
new file mode 100644
index 000000000..e1fc9167d
--- /dev/null
+++ b/webapp/stores/team_store.jsx
@@ -0,0 +1,142 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+
+const CHANGE_EVENT = 'change';
+
+var Utils;
+function getWindowLocationOrigin() {
+ if (!Utils) {
+ Utils = require('utils/utils.jsx'); //eslint-disable-line global-require
+ }
+ return Utils.getWindowLocationOrigin();
+}
+
+class TeamStoreClass extends EventEmitter {
+ constructor() {
+ super();
+
+ this.emitChange = this.emitChange.bind(this);
+ this.addChangeListener = this.addChangeListener.bind(this);
+ this.removeChangeListener = this.removeChangeListener.bind(this);
+ this.get = this.get.bind(this);
+ this.getByName = this.getByName.bind(this);
+ this.getAll = this.getAll.bind(this);
+ this.getCurrentId = this.getCurrentId.bind(this);
+ this.getCurrent = this.getCurrent.bind(this);
+ this.getCurrentTeamUrl = this.getCurrentTeamUrl.bind(this);
+ this.getCurrentInviteLink = this.getCurrentInviteLink.bind(this);
+ this.saveTeam = this.saveTeam.bind(this);
+
+ this.teams = {};
+ this.currentTeamId = '';
+ }
+
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ get(id) {
+ var c = this.getAll();
+ return c[id];
+ }
+
+ getByName(name) {
+ var t = this.getAll();
+
+ for (var id in t) {
+ if (t[id].name === name) {
+ return t[id];
+ }
+ }
+
+ return null;
+ }
+
+ getAll() {
+ return this.teams;
+ }
+
+ getCurrentId() {
+ var team = this.get(this.currentTeamId);
+
+ if (team) {
+ return team.id;
+ }
+
+ return null;
+ }
+
+ getCurrent() {
+ const team = this.teams[this.currentTeamId];
+
+ if (team) {
+ return team;
+ }
+
+ return null;
+ }
+
+ getCurrentTeamUrl() {
+ if (this.getCurrent()) {
+ return getWindowLocationOrigin() + '/' + this.getCurrent().name;
+ }
+ return null;
+ }
+
+ getCurrentInviteLink() {
+ const current = this.getCurrent();
+
+ if (current) {
+ return getWindowLocationOrigin() + '/signup_user_complete/?id=' + current.invite_id;
+ }
+
+ return '';
+ }
+
+ saveTeam(team) {
+ this.teams[team.id] = team;
+ }
+
+ saveTeams(teams) {
+ this.teams = teams;
+ }
+
+ saveMyTeam(team) {
+ this.saveTeam(team);
+ this.currentTeamId = team.id;
+ }
+}
+
+var TeamStore = new TeamStoreClass();
+
+TeamStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_MY_TEAM:
+ TeamStore.saveMyTeam(action.team);
+ TeamStore.emitChange();
+ break;
+ case ActionTypes.RECEIVED_ALL_TEAMS:
+ TeamStore.saveTeams(action.teams);
+ TeamStore.emitChange();
+ break;
+ default:
+ }
+});
+
+export default TeamStore;
diff --git a/webapp/stores/user_store.jsx b/webapp/stores/user_store.jsx
new file mode 100644
index 000000000..98cc2f3f1
--- /dev/null
+++ b/webapp/stores/user_store.jsx
@@ -0,0 +1,305 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import EventEmitter from 'events';
+
+import Constants from 'utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
+import BrowserStore from './browser_store.jsx';
+
+const CHANGE_EVENT = 'change';
+const CHANGE_EVENT_SESSIONS = 'change_sessions';
+const CHANGE_EVENT_AUDITS = 'change_audits';
+const CHANGE_EVENT_STATUSES = 'change_statuses';
+
+class UserStoreClass extends EventEmitter {
+ constructor() {
+ super();
+ this.profileCache = null;
+ this.currentUserId = '';
+ }
+
+ emitChange(userId) {
+ this.emit(CHANGE_EVENT, userId);
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ emitSessionsChange() {
+ this.emit(CHANGE_EVENT_SESSIONS);
+ }
+
+ addSessionsChangeListener(callback) {
+ this.on(CHANGE_EVENT_SESSIONS, callback);
+ }
+
+ removeSessionsChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT_SESSIONS, callback);
+ }
+
+ emitAuditsChange() {
+ this.emit(CHANGE_EVENT_AUDITS);
+ }
+
+ addAuditsChangeListener(callback) {
+ this.on(CHANGE_EVENT_AUDITS, callback);
+ }
+
+ removeAuditsChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT_AUDITS, callback);
+ }
+
+ emitStatusesChange() {
+ this.emit(CHANGE_EVENT_STATUSES);
+ }
+
+ addStatusesChangeListener(callback) {
+ this.on(CHANGE_EVENT_STATUSES, callback);
+ }
+
+ removeStatusesChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT_STATUSES, callback);
+ }
+
+ getCurrentUser() {
+ return this.getProfiles()[this.currentUserId];
+ }
+
+ setCurrentUser(user) {
+ this.saveProfile(user);
+ this.currentUserId = user.id;
+ global.window.mm_current_user_id = this.currentUserId;
+ }
+
+ getCurrentId() {
+ var user = this.getCurrentUser();
+
+ if (user) {
+ return user.id;
+ }
+
+ return null;
+ }
+
+ getLastEmail() {
+ return BrowserStore.getGlobalItem('last_email', '');
+ }
+
+ setLastEmail(email) {
+ BrowserStore.setGlobalItem('last_email', email);
+ }
+
+ getLastUsername() {
+ return BrowserStore.getGlobalItem('last_username', '');
+ }
+
+ setLastUsername(username) {
+ BrowserStore.setGlobalItem('last_username', username);
+ }
+
+ hasProfile(userId) {
+ return this.getProfiles()[userId] != null;
+ }
+
+ getProfile(userId) {
+ if (userId === this.getCurrentId()) {
+ return this.getCurrentUser();
+ }
+
+ return this.getProfiles()[userId];
+ }
+
+ getProfileByUsername(username) {
+ return this.getProfilesUsernameMap()[username];
+ }
+
+ getProfilesUsernameMap() {
+ var profileUsernameMap = {};
+
+ var profiles = this.getProfiles();
+ for (var key in profiles) {
+ if (profiles.hasOwnProperty(key)) {
+ var profile = profiles[key];
+ profileUsernameMap[profile.username] = profile;
+ }
+ }
+
+ return profileUsernameMap;
+ }
+
+ getProfiles() {
+ if (this.profileCache !== null) {
+ return this.profileCache;
+ }
+
+ return BrowserStore.getItem('profiles', {});
+ }
+
+ getActiveOnlyProfiles(skipCurrent) {
+ const active = {};
+ const profiles = this.getProfiles();
+ const currentId = this.getCurrentId();
+
+ for (var key in profiles) {
+ if (!(profiles[key].id === currentId && skipCurrent) && profiles[key].delete_at === 0) {
+ active[key] = profiles[key];
+ }
+ }
+
+ return active;
+ }
+
+ getActiveOnlyProfileList() {
+ const profileMap = this.getActiveOnlyProfiles();
+ const profiles = [];
+ const currentId = this.getCurrentId();
+
+ for (const id in profileMap) {
+ if (profileMap.hasOwnProperty(id) && id !== currentId) {
+ profiles.push(profileMap[id]);
+ }
+ }
+
+ return profiles;
+ }
+
+ saveProfile(profile) {
+ var ps = this.getProfiles();
+ ps[profile.id] = profile;
+ this.profileCache = ps;
+ BrowserStore.setItem('profiles', ps);
+ }
+
+ saveProfiles(profiles) {
+ const currentId = this.getCurrentId();
+ if (this.profileCache) {
+ const currentUser = this.profileCache[currentId];
+ if (currentUser) {
+ if (currentId in profiles) {
+ delete profiles[currentId];
+ }
+
+ this.profileCache = profiles;
+ this.profileCache[currentId] = currentUser;
+ } else {
+ this.profileCache = profiles;
+ }
+ } else {
+ this.profileCache = profiles;
+ }
+
+ BrowserStore.setItem('profiles', profiles);
+ }
+
+ setSessions(sessions) {
+ BrowserStore.setItem('sessions', sessions);
+ }
+
+ getSessions() {
+ return BrowserStore.getItem('sessions', {loading: true});
+ }
+
+ setAudits(audits) {
+ BrowserStore.setItem('audits', audits);
+ }
+
+ getAudits() {
+ return BrowserStore.getItem('audits', {loading: true});
+ }
+
+ getCurrentMentionKeys() {
+ return this.getMentionKeys(this.getCurrentId());
+ }
+
+ getMentionKeys(id) {
+ var user = this.getProfile(id);
+
+ var keys = [];
+
+ if (!user || !user.notify_props) {
+ return keys;
+ }
+
+ if (user.notify_props.mention_keys) {
+ keys = keys.concat(user.notify_props.mention_keys.split(','));
+ }
+
+ if (user.notify_props.first_name === 'true' && user.first_name) {
+ keys.push(user.first_name);
+ }
+
+ if (user.notify_props.all === 'true') {
+ keys.push('@all');
+ }
+
+ if (user.notify_props.channel === 'true') {
+ keys.push('@channel');
+ }
+
+ return keys;
+ }
+
+ setStatuses(statuses) {
+ this.pSetStatuses(statuses);
+ this.emitStatusesChange();
+ }
+
+ pSetStatuses(statuses) {
+ BrowserStore.setItem('statuses', statuses);
+ }
+
+ setStatus(userId, status) {
+ var statuses = this.getStatuses();
+ statuses[userId] = status;
+ this.pSetStatuses(statuses);
+ this.emitStatusesChange();
+ }
+
+ getStatuses() {
+ return BrowserStore.getItem('statuses', {});
+ }
+
+ getStatus(id) {
+ return this.getStatuses()[id];
+ }
+}
+
+var UserStore = new UserStoreClass();
+UserStore.setMaxListeners(15);
+
+UserStore.dispatchToken = AppDispatcher.register((payload) => {
+ var action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_PROFILES:
+ UserStore.saveProfiles(action.profiles);
+ UserStore.emitChange();
+ break;
+ case ActionTypes.RECEIVED_ME:
+ UserStore.setCurrentUser(action.me);
+ UserStore.emitChange(action.me.id);
+ break;
+ case ActionTypes.RECEIVED_SESSIONS:
+ UserStore.setSessions(action.sessions);
+ UserStore.emitSessionsChange();
+ break;
+ case ActionTypes.RECEIVED_AUDITS:
+ UserStore.setAudits(action.audits);
+ UserStore.emitAuditsChange();
+ break;
+ case ActionTypes.RECEIVED_STATUSES:
+ UserStore.pSetStatuses(action.statuses);
+ UserStore.emitStatusesChange();
+ break;
+ default:
+ }
+});
+
+export {UserStore as default};