PLT-2241 Refactored statuses into a more real-time system (#3573)
* Refactored statuses into a more real-time system * Updated package.json with correct commit and fixed minor bug * Minor updates to statuses based on feedback * When setting status online, update only LastActivityAt if status already exists
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
180adc79af
Коммит
c0ab2636d6
@@ -2,6 +2,9 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import TeamStore from 'stores/team_store.jsx';
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
@@ -14,12 +17,14 @@ import Client from 'utils/web_client.jsx';
|
||||
import WebSocketClient from 'utils/websocket_client.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import * as UserActions from 'actions/user_actions.jsx';
|
||||
import {handleNewPost} from 'actions/post_actions.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const SocketEvents = Constants.SocketEvents;
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
|
||||
@@ -34,10 +39,10 @@ export function initialize() {
|
||||
|
||||
const connUrl = protocol + location.host + ((/:\d+/).test(location.host) ? '' : Utils.getWebsocketPort(protocol)) + Client.getUsersRoute() + '/websocket';
|
||||
|
||||
WebSocketClient.initialize(connUrl);
|
||||
WebSocketClient.setEventCallback(handleEvent);
|
||||
WebSocketClient.setReconnectCallback(handleReconnect);
|
||||
WebSocketClient.setCloseCallback(handleClose);
|
||||
WebSocketClient.initialize(connUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +50,21 @@ export function close() {
|
||||
WebSocketClient.close();
|
||||
}
|
||||
|
||||
export function getStatuses() {
|
||||
WebSocketClient.getStatuses(
|
||||
(resp) => {
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.RECEIVED_STATUSES,
|
||||
statuses: resp.data
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function handleReconnect() {
|
||||
AsyncClient.getChannels();
|
||||
AsyncClient.getPosts(ChannelStore.getCurrentId());
|
||||
getStatuses();
|
||||
ErrorStore.clearLastError();
|
||||
ErrorStore.emitChange();
|
||||
}
|
||||
@@ -112,6 +129,10 @@ function handleEvent(msg) {
|
||||
handleUserTypingEvent(msg);
|
||||
break;
|
||||
|
||||
case SocketEvents.STATUS_CHANGED:
|
||||
handleStatusChangedEvent(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -218,3 +239,7 @@ function handlePreferenceChangedEvent(msg) {
|
||||
function handleUserTypingEvent(msg) {
|
||||
GlobalActions.emitRemoteUserTypingEvent(msg.channel_id, msg.user_id, msg.data.parent_id);
|
||||
}
|
||||
|
||||
function handleStatusChangedEvent(msg) {
|
||||
UserStore.setStatus(msg.user_id, msg.data.status);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import * as TextFormatting from 'utils/text_formatting.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import Client from 'utils/web_client.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const UserStatuses = Constants.UserStatuses;
|
||||
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
@@ -189,6 +190,7 @@ export default class ChannelHeader extends React.Component {
|
||||
if (teammate) {
|
||||
return UserStore.getStatus(teammate.id);
|
||||
}
|
||||
return UserStatuses.OFFLINE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,12 @@ import UserStore from 'stores/user_store.jsx';
|
||||
import BrowserStore from 'stores/browser_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import * as Utils from 'utils/utils.jsx';
|
||||
import * as Websockets from 'actions/websocket_actions.jsx';
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import * as WebSocketActions from 'actions/websocket_actions.jsx';
|
||||
import Constants from 'utils/constants.jsx';
|
||||
|
||||
import {browserHistory} from 'react-router/es6';
|
||||
|
||||
const CLIENT_STATUS_INTERVAL = 30000;
|
||||
const BACKSPACE_CHAR = 8;
|
||||
|
||||
import React from 'react';
|
||||
@@ -26,8 +25,8 @@ export default class LoggedIn extends React.Component {
|
||||
this.onUserChanged = this.onUserChanged.bind(this);
|
||||
this.setupUser = this.setupUser.bind(this);
|
||||
|
||||
// Initalize websockets
|
||||
Websockets.initialize();
|
||||
// Initalize websocket
|
||||
WebSocketActions.initialize();
|
||||
|
||||
// Force logout of all tabs if one tab is logged out
|
||||
$(window).bind('storage', (e) => {
|
||||
@@ -109,10 +108,6 @@ export default class LoggedIn extends React.Component {
|
||||
// Listen for user
|
||||
UserStore.addChangeListener(this.onUserChanged);
|
||||
|
||||
// Get all statuses regularally. (Soon to be switched to websocket)
|
||||
AsyncClient.getStatuses();
|
||||
this.intervalId = setInterval(() => AsyncClient.getStatuses(), CLIENT_STATUS_INTERVAL);
|
||||
|
||||
// ???
|
||||
$('body').on('mouseenter mouseleave', '.post', function mouseOver(ev) {
|
||||
if (ev.type === 'mouseenter') {
|
||||
@@ -144,7 +139,7 @@ export default class LoggedIn extends React.Component {
|
||||
}
|
||||
});
|
||||
|
||||
// Pervent backspace from navigating back a page
|
||||
// Prevent backspace from navigating back a page
|
||||
$(window).on('keydown.preventBackspace', (e) => {
|
||||
if (e.which === BACKSPACE_CHAR && !$(e.target).is('input, textarea')) {
|
||||
e.preventDefault();
|
||||
@@ -159,9 +154,8 @@ export default class LoggedIn extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
$('#root').attr('class', '');
|
||||
clearInterval(this.intervalId);
|
||||
|
||||
Websockets.close();
|
||||
WebSocketActions.close();
|
||||
UserStore.removeChangeListener(this.onUserChanged);
|
||||
|
||||
$('body').off('click.userpopover');
|
||||
|
||||
@@ -125,7 +125,7 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
directChannel.display_name = Utils.displayUsername(teammateId);
|
||||
directChannel.teammate_id = teammateId;
|
||||
directChannel.status = UserStore.getStatus(teammateId);
|
||||
directChannel.status = UserStore.getStatus(teammateId) || 'offline';
|
||||
|
||||
if (UserStore.hasTeamProfile(teammateId)) {
|
||||
directChannels.push(directChannel);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"keymirror": "0.1.1",
|
||||
"marked": "mattermost/marked#12d2be4cdf54d4ec95fead934e18840b6a2c1a7b",
|
||||
"match-at": "0.1.0",
|
||||
"mattermost": "mattermost/mattermost-javascript#4cdaeba22ff82bf93dc417af1ab4e89e3248d624",
|
||||
"mattermost": "mattermost/mattermost-javascript#84b6f1ebf33aa4b5d8e7ddd7be97d3f5bff5ed17",
|
||||
"object-assign": "4.1.0",
|
||||
"perfect-scrollbar": "0.6.12",
|
||||
"react": "15.2.1",
|
||||
|
||||
@@ -9,6 +9,7 @@ import LocalizationStore from './localization_store.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
const UserStatuses = Constants.UserStatuses;
|
||||
|
||||
const CHANGE_EVENT_DM_LIST = 'change_dm_list';
|
||||
const CHANGE_EVENT = 'change';
|
||||
@@ -292,18 +293,11 @@ class UserStoreClass extends EventEmitter {
|
||||
}
|
||||
|
||||
setStatuses(statuses) {
|
||||
this.pSetStatuses(statuses);
|
||||
this.emitStatusesChange();
|
||||
}
|
||||
|
||||
pSetStatuses(statuses) {
|
||||
this.statuses = statuses;
|
||||
this.statuses = Object.assign(this.statuses, statuses);
|
||||
}
|
||||
|
||||
setStatus(userId, status) {
|
||||
var statuses = this.getStatuses();
|
||||
statuses[userId] = status;
|
||||
this.pSetStatuses(statuses);
|
||||
this.statuses[userId] = status;
|
||||
this.emitStatusesChange();
|
||||
}
|
||||
|
||||
@@ -312,7 +306,7 @@ class UserStoreClass extends EventEmitter {
|
||||
}
|
||||
|
||||
getStatus(id) {
|
||||
return this.getStatuses()[id];
|
||||
return this.getStatuses()[id] || UserStatuses.OFFLINE;
|
||||
}
|
||||
|
||||
getNoAccounts() {
|
||||
@@ -370,7 +364,7 @@ UserStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||
UserStore.emitAuditsChange();
|
||||
break;
|
||||
case ActionTypes.RECEIVED_STATUSES:
|
||||
UserStore.pSetStatuses(action.statuses);
|
||||
UserStore.setStatuses(action.statuses);
|
||||
UserStore.emitStatusesChange();
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||
import BrowserStore from 'stores/browser_store.jsx';
|
||||
import ChannelStore from 'stores/channel_store.jsx';
|
||||
import PreferenceStore from 'stores/preference_store.jsx';
|
||||
import PostStore from 'stores/post_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
import * as utils from './utils.jsx';
|
||||
@@ -744,21 +743,12 @@ export function getMe() {
|
||||
}
|
||||
|
||||
export function getStatuses() {
|
||||
const preferences = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
|
||||
|
||||
const teammateIds = [];
|
||||
for (const [name, value] of preferences) {
|
||||
if (value === 'true') {
|
||||
teammateIds.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCallInProgress('getStatuses') || teammateIds.length === 0) {
|
||||
if (isCallInProgress('getStatuses')) {
|
||||
return;
|
||||
}
|
||||
|
||||
callTracker.getStatuses = utils.getTimestamp();
|
||||
Client.getStatuses(teammateIds,
|
||||
Client.getStatuses(
|
||||
(data) => {
|
||||
callTracker.getStatuses = 0;
|
||||
|
||||
@@ -1535,4 +1525,4 @@ export function deleteEmoji(id) {
|
||||
dispatchError(err, 'deleteEmoji');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,8 @@ export const Constants = {
|
||||
USER_REMOVED: 'user_removed',
|
||||
TYPING: 'typing',
|
||||
PREFERENCE_CHANGED: 'preference_changed',
|
||||
EPHEMERAL_MESSAGE: 'ephemeral_message'
|
||||
EPHEMERAL_MESSAGE: 'ephemeral_message',
|
||||
STATUS_CHANGED: 'status_change'
|
||||
},
|
||||
|
||||
UserUpdateEvents: {
|
||||
@@ -210,6 +211,12 @@ export const Constants = {
|
||||
POST: 5
|
||||
},
|
||||
|
||||
UserStatuses: {
|
||||
OFFLINE: 'offline',
|
||||
AWAY: 'away',
|
||||
ONLINE: 'online'
|
||||
},
|
||||
|
||||
SPECIAL_MENTIONS: ['all', 'channel'],
|
||||
CHARACTER_LIMIT: 4000,
|
||||
IMAGE_TYPES: ['jpg', 'gif', 'bmp', 'png', 'jpeg'],
|
||||
|
||||
Ссылка в новой задаче
Block a user