diff options
Diffstat (limited to 'webapp/stores')
-rw-r--r-- | webapp/stores/browser_store.jsx | 47 | ||||
-rw-r--r-- | webapp/stores/error_store.jsx | 12 | ||||
-rw-r--r-- | webapp/stores/notificaiton_store.jsx | 98 | ||||
-rw-r--r-- | webapp/stores/preference_store.jsx | 113 | ||||
-rw-r--r-- | webapp/stores/socket_store.jsx | 343 | ||||
-rw-r--r-- | webapp/stores/user_store.jsx | 50 | ||||
-rw-r--r-- | webapp/stores/user_typing_store.jsx | 108 |
7 files changed, 276 insertions, 495 deletions
diff --git a/webapp/stores/browser_store.jsx b/webapp/stores/browser_store.jsx index 66190f6a2..bba146e38 100644 --- a/webapp/stores/browser_store.jsx +++ b/webapp/stores/browser_store.jsx @@ -8,6 +8,8 @@ function getPrefix() { return global.window.mm_current_user_id + '_'; } + console.log('BrowserStore tried to operate without user present'); //eslint-disable-line no-console + return 'unknown_'; } @@ -34,46 +36,35 @@ class BrowserStoreClass { } checkVersion() { - var currentVersion = sessionStorage.getItem('storage_version'); + var currentVersion = this.getGlobalItem('storage_version'); if (currentVersion !== global.window.mm_config.Version) { - sessionStorage.clear(); + this.clearAll(); try { - sessionStorage.setItem('storage_version', global.window.mm_config.Version); + this.setGlobalItem('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) { + this.setGlobalItem(getPrefix() + name, value); } - setItem(name, value) { - sessionStorage.setItem(getPrefix() + name, JSON.stringify(value)); + getItem(name, defaultValue) { + return this.getGlobalItem(getPrefix() + name, defaultValue); } removeItem(name) { - sessionStorage.removeItem(getPrefix() + name); + this.removeGlobalItem(getPrefix() + name); } setGlobalItem(name, value) { try { if (this.isLocalStorageSupported()) { - localStorage.setItem(getPrefix() + name, JSON.stringify(value)); + localStorage.setItem(name, JSON.stringify(value)); } else { - sessionStorage.setItem(getPrefix() + name, JSON.stringify(value)); + sessionStorage.setItem(name, JSON.stringify(value)); } } catch (err) { console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console @@ -87,9 +78,9 @@ class BrowserStoreClass { var result = null; try { if (this.isLocalStorageSupported()) { - result = JSON.parse(localStorage.getItem(getPrefix() + name)); + result = JSON.parse(localStorage.getItem(name)); } else { - result = JSON.parse(sessionStorage.getItem(getPrefix() + name)); + result = JSON.parse(sessionStorage.getItem(name)); } } catch (err) { result = null; @@ -104,18 +95,18 @@ class BrowserStoreClass { removeGlobalItem(name) { if (this.isLocalStorageSupported()) { - localStorage.removeItem(getPrefix() + name); + localStorage.removeItem(name); } else { - sessionStorage.removeItem(getPrefix() + name); + sessionStorage.removeItem(name); } } getLastServerVersion() { - return sessionStorage.getItem('last_server_version'); + return this.getGlobalItem('last_server_version'); } setLastServerVersion(version) { - sessionStorage.setItem('last_server_version', version); + this.setGlobalItem('last_server_version', version); } signalLogout() { @@ -185,6 +176,7 @@ class BrowserStoreClass { const logoutId = sessionStorage.getItem('__logout__'); sessionStorage.clear(); + localStorage.clear(); if (logoutId) { sessionStorage.setItem('__logout__', logoutId); @@ -222,4 +214,3 @@ class BrowserStoreClass { var BrowserStore = new BrowserStoreClass(); export default BrowserStore; -window.BrowserStore = BrowserStore; diff --git a/webapp/stores/error_store.jsx b/webapp/stores/error_store.jsx index 776375a82..7c695a335 100644 --- a/webapp/stores/error_store.jsx +++ b/webapp/stores/error_store.jsx @@ -35,15 +35,15 @@ class ErrorStoreClass extends EventEmitter { } getLastError() { - return BrowserStore.getItem('last_error'); + return BrowserStore.getGlobalItem('last_error'); } storeLastError(error) { - BrowserStore.setItem('last_error', error); + BrowserStore.setGlobalItem('last_error', error); } getConnectionErrorCount() { - var count = BrowserStore.getItem('last_error_conn'); + var count = BrowserStore.getGlobalItem('last_error_conn'); if (count == null) { return 0; @@ -53,12 +53,12 @@ class ErrorStoreClass extends EventEmitter { } setConnectionErrorCount(count) { - BrowserStore.setItem('last_error_conn', count); + BrowserStore.setGlobalItem('last_error_conn', count); } clearLastError() { - BrowserStore.removeItem('last_error'); - BrowserStore.removeItem('last_error_conn'); + BrowserStore.removeGlobalItem('last_error'); + BrowserStore.removeGlobalItem('last_error_conn'); } } diff --git a/webapp/stores/notificaiton_store.jsx b/webapp/stores/notificaiton_store.jsx new file mode 100644 index 000000000..70caffeb6 --- /dev/null +++ b/webapp/stores/notificaiton_store.jsx @@ -0,0 +1,98 @@ +// 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'; +import UserStore from './user_store.jsx'; +import ChannelStore from './channel_store.jsx'; +import * as Utils from 'utils/utils.jsx'; +const ActionTypes = Constants.ActionTypes; + +const CHANGE_EVENT = 'change'; + +class NotificationStoreClass extends EventEmitter { + emitChange() { + this.emit(CHANGE_EVENT); + } + + addChangeListener(callback) { + this.on(CHANGE_EVENT, callback); + } + + removeChangeListener(callback) { + this.removeListener(CHANGE_EVENT, callback); + } + + handleRecievedPost(post, msgProps) { + // Send desktop notification + if ((UserStore.getCurrentId() !== post.user_id || post.props.from_webhook === 'true') && !Utils.isSystemMessage(post)) { + let mentions = []; + if (msgProps.mentions) { + mentions = JSON.parse(msgProps.mentions); + } + + const channel = ChannelStore.get(post.channel_id); + const user = UserStore.getCurrentUser(); + const member = ChannelStore.getMember(post.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 = Utils.localizeMessage('channel_loader.someone', 'Someone'); + if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') { + username = post.props.override_username; + } else if (UserStore.hasProfile(post.user_id)) { + username = UserStore.getProfile(post.user_id).username; + } + + let title = Utils.localizeMessage('channel_loader.posted', '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 + Utils.localizeMessage('channel_loader.uploadedImage', ' uploaded an image'), channel); + } else if (msgProps.otherFile) { + Utils.notifyMe(title, username + Utils.localizeMessage('channel_loader.uploadedFile', ' uploaded a file'), channel); + } else { + Utils.notifyMe(title, username + Utils.localizeMessage('channel_loader.something', ' did something new'), channel); + } + } else { + Utils.notifyMe(title, username + Utils.localizeMessage('channel_loader.wrote', ' wrote: ') + notifyText, channel); + } + if (!user.notify_props || user.notify_props.desktop_sound === 'true') { + Utils.ding(); + } + } + } +} + +var NotificationStore = new NotificationStoreClass(); + +NotificationStore.dispatchToken = AppDispatcher.register((payload) => { + const action = payload.action; + + switch (action.type) { + case ActionTypes.RECEIVED_POST: + NotificationStore.handleRecievedPost(action.post, action.webspcketMessageProps); + NotificationStore.emitChange(); + break; + } +}); + +export default NotificationStore; diff --git a/webapp/stores/preference_store.jsx b/webapp/stores/preference_store.jsx index df77f0d51..fcfd1c426 100644 --- a/webapp/stores/preference_store.jsx +++ b/webapp/stores/preference_store.jsx @@ -4,143 +4,80 @@ 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); + + this.preferences = new Map(); } - getAllPreferences() { - return new Map(BrowserStore.getItem('preferences', [])); + getKey(category, name) { + return `${category}--${name}`; } get(category, name, defaultValue = '') { - const preference = this.getAllPreferences().get(getPreferenceKey(category, name)); + const key = this.getKey(category, name); - if (!preference) { + if (!this.preferences.has(key)) { return defaultValue; } - return preference.value || defaultValue; + return this.preferences.get(key); } getBool(category, name, defaultValue = false) { - const preference = this.getAllPreferences().get(getPreferenceKey(category, name)); + const key = this.getKey(category, name); - if (!preference) { + if (!this.preferences.has(key)) { 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; + return this.preferences.get(key) !== 'false'; } getInt(category, name, defaultValue = 0) { - const preference = this.getAllPreferences().get(getPreferenceKey(category, name)); + const key = this.getKey(category, name); - if (!preference) { + if (!this.preferences.has(key)) { 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; + return parseInt(this.preferences.get(key), 10); } getCategory(category) { - return this.getPreferencesWhere((preference) => (preference.category === category)); - } + const prefix = category + '--'; - getPreferencesWhere(pred) { - const all = this.getAllPreferences(); - const preferences = []; + const preferences = new Map(); - for (const [, preference] of all) { - if (pred(preference)) { - preferences.push(preference); + for (const [key, value] of this.preferences) { + if (key.startsWith(prefix)) { + preferences.set(key.substring(prefix.length), value); } } 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; + this.preferences.set(this.getKey(category, name), value); } - setPreferences(newPreferences) { - const preferences = this.getAllPreferences(); - + setPreferencesFromServer(newPreferences) { for (const preference of newPreferences) { - preferences.set(getPreferenceKeyForModel(preference), preference); + this.setPreference(preference.category, preference.name, preference.value); } + } - this.setAllPreferences(preferences); + clear() { + this.preferences.clear(); } emitChange() { @@ -166,7 +103,7 @@ class PreferenceStoreClass extends EventEmitter { break; } case ActionTypes.RECEIVED_PREFERENCES: - this.setPreferences(action.preferences); + this.setPreferencesFromServer(action.preferences); this.emitChange(); break; } diff --git a/webapp/stores/socket_store.jsx b/webapp/stores/socket_store.jsx deleted file mode 100644 index 5d6302743..000000000 --- a/webapp/stores/socket_store.jsx +++ /dev/null @@ -1,343 +0,0 @@ -// 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/user_store.jsx b/webapp/stores/user_store.jsx index 98cc2f3f1..4213e6e8c 100644 --- a/webapp/stores/user_store.jsx +++ b/webapp/stores/user_store.jsx @@ -16,7 +16,10 @@ const CHANGE_EVENT_STATUSES = 'change_statuses'; class UserStoreClass extends EventEmitter { constructor() { super(); - this.profileCache = null; + this.profiles = {}; + this.statuses = {}; + this.sessions = {}; + this.audits = {}; this.currentUserId = ''; } @@ -135,11 +138,7 @@ class UserStoreClass extends EventEmitter { } getProfiles() { - if (this.profileCache !== null) { - return this.profileCache; - } - - return BrowserStore.getItem('profiles', {}); + return this.profiles; } getActiveOnlyProfiles(skipCurrent) { @@ -171,47 +170,38 @@ class UserStoreClass extends EventEmitter { } saveProfile(profile) { - var ps = this.getProfiles(); - ps[profile.id] = profile; - this.profileCache = ps; - BrowserStore.setItem('profiles', ps); + this.profiles[profile.id] = profile; } 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; + const currentUser = this.profiles[currentId]; + if (currentUser) { + if (currentId in this.profiles) { + delete this.profiles[currentId]; } + + this.profiles = profiles; + this.profiles[currentId] = currentUser; } else { - this.profileCache = profiles; + this.profiles = profiles; } - - BrowserStore.setItem('profiles', profiles); } setSessions(sessions) { - BrowserStore.setItem('sessions', sessions); + this.sessions = sessions; } getSessions() { - return BrowserStore.getItem('sessions', {loading: true}); + return this.sessions; } setAudits(audits) { - BrowserStore.setItem('audits', audits); + this.audits = audits; } getAudits() { - return BrowserStore.getItem('audits', {loading: true}); + return this.audits; } getCurrentMentionKeys() { @@ -252,7 +242,7 @@ class UserStoreClass extends EventEmitter { } pSetStatuses(statuses) { - BrowserStore.setItem('statuses', statuses); + this.statuses = statuses; } setStatus(userId, status) { @@ -263,7 +253,7 @@ class UserStoreClass extends EventEmitter { } getStatuses() { - return BrowserStore.getItem('statuses', {}); + return this.statuses; } getStatus(id) { diff --git a/webapp/stores/user_typing_store.jsx b/webapp/stores/user_typing_store.jsx new file mode 100644 index 000000000..ab0a9af1d --- /dev/null +++ b/webapp/stores/user_typing_store.jsx @@ -0,0 +1,108 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; +import UserStore from 'stores/user_store.jsx'; +import EventEmitter from 'events'; +import * as Utils from 'utils/utils.jsx'; + +import Constants from 'utils/constants.jsx'; +const ActionTypes = Constants.ActionTypes; + +const CHANGE_EVENT = 'change'; + +class UserTypingStoreClass extends EventEmitter { + constructor() { + super(); + + // All typeing users by channel + // this.typingUsers.[channelId+postParentId].user if present then user us typing + // Value is timeout to remove user + this.typingUsers = {}; + } + + emitChange() { + this.emit(CHANGE_EVENT); + } + + addChangeListener(callback) { + this.on(CHANGE_EVENT, callback); + } + + removeChangeListener(callback) { + this.removeListener(CHANGE_EVENT, callback); + } + + usernameFromId(userId) { + let username = Utils.localizeMessage('msg_typing.someone', 'Someone'); + if (UserStore.hasProfile(userId)) { + username = UserStore.getProfile(userId).username; + } + return username; + } + + userTyping(channelId, userId, postParentId) { + const username = this.usernameFromId(userId); + + // Key representing a location where users can type + const loc = channelId + postParentId; + + // Create entry + if (!this.typingUsers[loc]) { + this.typingUsers[loc] = {}; + } + + // If we already have this user, clear it's timeout to be deleted + if (this.typingUsers[loc][username]) { + clearTimeout(this.typingUsers[loc][username].timeout); + } + + // Set the user and a timeout to remove it + this.typingUsers[loc][username] = setTimeout(() => { + delete this.typingUsers[loc][username]; + if (this.typingUsers[loc] === {}) { + delete this.typingUsers[loc]; + } + this.emitChange(); + }, Constants.UPDATE_TYPING_MS); + this.emitChange(); + } + + getUsersTyping(channelId, postParentId) { + // Key representing a location where users can type + const loc = channelId + postParentId; + + return this.typingUsers[loc]; + } + + userPosted(userId, channelId, postParentId) { + const username = this.usernameFromId(userId); + const loc = channelId + postParentId; + + if (this.typingUsers[loc]) { + clearTimeout(this.typingUsers[loc][username]); + delete this.typingUsers[loc][username]; + if (this.typingUsers[loc] === {}) { + delete this.typingUsers[loc]; + } + this.emitChange(); + } + } +} + +var UserTypingStore = new UserTypingStoreClass(); + +UserTypingStore.dispatchToken = AppDispatcher.register((payload) => { + var action = payload.action; + + switch (action.type) { + case ActionTypes.RECEIVED_POST: + UserTypingStore.userPosted(action.post.user_id, action.post.channel_id, action.post.parent_id); + break; + case ActionTypes.USER_TYPING: + UserTypingStore.userTyping(action.channelId, action.userId, action.postParentId); + break; + } +}); + +export default UserTypingStore; |