From ff741740eebceb43b1d69b13d97ae7eed2aa32d1 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 13 Feb 2017 09:27:28 -0500 Subject: Increase performance when receiving messages (#5375) --- api/channel.go | 10 ------ api/deprecated.go | 10 ------ app/channel.go | 18 ++++++++--- model/websocket_message.go | 1 - webapp/actions/global_actions.jsx | 7 ++-- webapp/actions/post_actions.jsx | 20 ------------ webapp/actions/team_actions.jsx | 5 +++ webapp/actions/websocket_actions.jsx | 13 -------- webapp/components/needs_team.jsx | 9 ++++++ webapp/components/post_view/post_view_cache.jsx | 2 +- .../team_sidebar/components/team_button.jsx | 10 +++++- webapp/root.jsx | 3 ++ webapp/stores/channel_store.jsx | 37 ++++++++++++++++++++++ webapp/stores/team_store.jsx | 25 +++++++++++++++ 14 files changed, 106 insertions(+), 64 deletions(-) diff --git a/api/channel.go b/api/channel.go index 33e083f14..a5d42f151 100644 --- a/api/channel.go +++ b/api/channel.go @@ -753,16 +753,6 @@ func autocompleteChannels(c *Context, w http.ResponseWriter, r *http.Request) { func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) { view := model.ChannelViewFromJson(r.Body) - if err := app.SetActiveChannel(c.Session.UserId, view.ChannelId); err != nil { - c.Err = err - return - } - - if len(view.ChannelId) == 0 { - ReturnStatusOK(w) - return - } - if err := app.ViewChannel(view, c.TeamId, c.Session.UserId, !c.Session.IsMobileApp()); err != nil { c.Err = err return diff --git a/api/deprecated.go b/api/deprecated.go index 9c1d2a4ce..46f016420 100644 --- a/api/deprecated.go +++ b/api/deprecated.go @@ -101,11 +101,6 @@ func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { app.Srv.Store.Preference().Save(&model.Preferences{teamPref, chanPref}) - message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil) - message.Add("channel_id", id) - - go app.Publish(message) - result := make(map[string]string) result["id"] = id w.Write([]byte(model.MapToJson(result))) @@ -136,11 +131,6 @@ func setLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { app.Srv.Store.Preference().Save(&model.Preferences{teamPref, chanPref}) - message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil) - message.Add("channel_id", id) - - go app.Publish(message) - result := make(map[string]string) result["id"] = id w.Write([]byte(model.MapToJson(result))) diff --git a/app/channel.go b/app/channel.go index fcd4e166d..194a43f69 100644 --- a/app/channel.go +++ b/app/channel.go @@ -844,7 +844,15 @@ func SearchChannelsUserNotIn(teamId string, userId string, term string) (*model. } func ViewChannel(view *model.ChannelView, teamId string, userId string, clearPushNotifications bool) *model.AppError { - channelIds := []string{view.ChannelId} + if err := SetActiveChannel(userId, view.ChannelId); err != nil { + return err + } + + channelIds := []string{} + + if len(view.ChannelId) > 0 { + channelIds = append(channelIds, view.ChannelId) + } var pchan store.StoreChannel if len(view.PrevChannelId) > 0 { @@ -855,6 +863,10 @@ func ViewChannel(view *model.ChannelView, teamId string, userId string, clearPus } } + if len(channelIds) == 0 { + return nil + } + uchan := Srv.Store.Channel().UpdateLastViewedAt(channelIds, userId) if pchan != nil { @@ -871,10 +883,6 @@ func ViewChannel(view *model.ChannelView, teamId string, userId string, clearPus return result.Err } - message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, teamId, "", userId, nil) - message.Add("channel_id", view.ChannelId) - go Publish(message) - return nil } diff --git a/model/websocket_message.go b/model/websocket_message.go index 5c956d576..cfbc51ed9 100644 --- a/model/websocket_message.go +++ b/model/websocket_message.go @@ -14,7 +14,6 @@ const ( WEBSOCKET_EVENT_POST_EDITED = "post_edited" WEBSOCKET_EVENT_POST_DELETED = "post_deleted" WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted" - WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed" WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" WEBSOCKET_EVENT_NEW_USER = "new_user" WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx index 2d1638060..5def48858 100644 --- a/webapp/actions/global_actions.jsx +++ b/webapp/actions/global_actions.jsx @@ -47,16 +47,17 @@ export function emitChannelClickEvent(channel) { function switchToChannel(chan) { const channelMember = ChannelStore.getMyMember(chan.id); const getMyChannelMembersPromise = AsyncClient.getChannelMember(chan.id, UserStore.getCurrentId()); + const oldChannelId = ChannelStore.getCurrentId(); getMyChannelMembersPromise.then(() => { AsyncClient.getChannelStats(chan.id, true); - AsyncClient.viewChannel(chan.id, ChannelStore.getCurrentId()); + AsyncClient.viewChannel(chan.id, oldChannelId); loadPosts(chan.id); trackPage(); }); // Mark previous and next channel as read - ChannelStore.resetCounts(ChannelStore.getCurrentId()); + ChannelStore.resetCounts(oldChannelId); ChannelStore.resetCounts(chan.id); BrowserStore.setGlobalItem(chan.team_id, chan.id); @@ -68,7 +69,7 @@ export function emitChannelClickEvent(channel) { team_id: chan.team_id, total_msg_count: chan.total_msg_count, channelMember, - prev: ChannelStore.getCurrentId() + prev: oldChannelId }); } diff --git a/webapp/actions/post_actions.jsx b/webapp/actions/post_actions.jsx index 61f193b66..633a6f66a 100644 --- a/webapp/actions/post_actions.jsx +++ b/webapp/actions/post_actions.jsx @@ -5,7 +5,6 @@ import AppDispatcher from 'dispatcher/app_dispatcher.jsx'; import ChannelStore from 'stores/channel_store.jsx'; import PostStore from 'stores/post_store.jsx'; -import TeamStore from 'stores/team_store.jsx'; import UserStore from 'stores/user_store.jsx'; import {loadStatusesForChannel} from 'actions/status_actions.jsx'; @@ -18,30 +17,11 @@ const ActionTypes = Constants.ActionTypes; const Preferences = Constants.Preferences; export function handleNewPost(post, msg) { - const teamId = TeamStore.getCurrentId(); - - if (ChannelStore.getCurrentId() === post.channel_id) { - if (window.isActive) { - AsyncClient.viewChannel(); - } else { - AsyncClient.getChannel(post.channel_id); - } - } else if (msg && (teamId === msg.data.team_id || msg.data.channel_type === Constants.DM_CHANNEL)) { - if (Client.teamId) { - AsyncClient.getChannel(post.channel_id); - } - } - let websocketMessageProps = null; if (msg) { websocketMessageProps = msg.data; } - const myTeams = TeamStore.getMyTeamMembers(); - if (msg.data.team_id !== teamId && myTeams.filter((m) => m.team_id === msg.data.team_id).length) { - AsyncClient.getMyTeamsUnread(teamId); - } - if (post.root_id && PostStore.getPost(post.channel_id, post.root_id) == null) { Client.getPost( post.channel_id, diff --git a/webapp/actions/team_actions.jsx b/webapp/actions/team_actions.jsx index e23fe1e5d..478d3dffc 100644 --- a/webapp/actions/team_actions.jsx +++ b/webapp/actions/team_actions.jsx @@ -145,3 +145,8 @@ export function inviteMembers(data, success, error) { } ); } + +export function switchTeams(url) { + AsyncClient.viewChannel(); + browserHistory.push(url); +} diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx index 3f3e81d16..2e95c712c 100644 --- a/webapp/actions/websocket_actions.jsx +++ b/webapp/actions/websocket_actions.jsx @@ -137,10 +137,6 @@ function handleEvent(msg) { handleUserUpdatedEvent(msg); break; - case SocketEvents.CHANNEL_VIEWED: - handleChannelViewedEvent(msg); - break; - case SocketEvents.CHANNEL_DELETED: handleChannelDeletedEvent(msg); break; @@ -282,15 +278,6 @@ function handleUserUpdatedEvent(msg) { } } -function handleChannelViewedEvent(msg) { - // Useful for when multiple devices have the app open to different channels - if (TeamStore.getCurrentId() === msg.broadcast.team_id && - ChannelStore.getCurrentId() !== msg.data.channel_id && - UserStore.getCurrentId() === msg.broadcast.user_id) { - AsyncClient.getChannel(msg.data.channel_id); - } -} - function handleChannelDeletedEvent(msg) { if (ChannelStore.getCurrentId() === msg.data.channel_id) { const teamUrl = TeamStore.getCurrentTeamRelativeUrl(); diff --git a/webapp/components/needs_team.jsx b/webapp/components/needs_team.jsx index 0b91814c3..fb6029c2b 100644 --- a/webapp/components/needs_team.jsx +++ b/webapp/components/needs_team.jsx @@ -42,6 +42,8 @@ import SelectTeamModal from 'components/admin_console/select_team_modal.jsx'; import iNoBounce from 'inobounce'; import * as UserAgent from 'utils/user_agent.jsx'; +const UNREAD_CHECK_TIME_MILLISECONDS = 10000; + export default class NeedsTeam extends React.Component { constructor(params) { super(params); @@ -49,6 +51,8 @@ export default class NeedsTeam extends React.Component { this.onTeamChanged = this.onTeamChanged.bind(this); this.onPreferencesChanged = this.onPreferencesChanged.bind(this); + this.blurTime = new Date().getTime(); + const team = TeamStore.getCurrent(); this.state = { @@ -97,11 +101,16 @@ export default class NeedsTeam extends React.Component { AsyncClient.viewChannel(); ChannelStore.resetCounts(ChannelStore.getCurrentId()); ChannelStore.emitChange(); + window.isActive = true; + if (new Date().getTime() - this.blurTime > UNREAD_CHECK_TIME_MILLISECONDS) { + AsyncClient.getMyChannelMembers(); + } }); $(window).on('blur', () => { window.isActive = false; + this.blurTime = new Date().getTime(); if (UserStore.getCurrentUser()) { AsyncClient.viewChannel(''); } diff --git a/webapp/components/post_view/post_view_cache.jsx b/webapp/components/post_view/post_view_cache.jsx index 9c3f1db1b..b0b35a5c0 100644 --- a/webapp/components/post_view/post_view_cache.jsx +++ b/webapp/components/post_view/post_view_cache.jsx @@ -33,7 +33,7 @@ export default class PostViewCache extends React.Component { componentWillUnmount() { if (UserStore.getCurrentUser()) { - AsyncClient.viewChannel(''); + AsyncClient.viewChannel('', this.state.currentChannelId || ''); } ChannelStore.removeChangeListener(this.onChannelChange); } diff --git a/webapp/components/team_sidebar/components/team_button.jsx b/webapp/components/team_sidebar/components/team_button.jsx index 2df21b20b..6fbf8aef9 100644 --- a/webapp/components/team_sidebar/components/team_button.jsx +++ b/webapp/components/team_sidebar/components/team_button.jsx @@ -3,6 +3,8 @@ import Constants from 'utils/constants.jsx'; +import {switchTeams} from 'actions/team_actions.jsx'; + import React from 'react'; import {Link} from 'react-router/es6'; import {Tooltip, OverlayTrigger} from 'react-bootstrap'; @@ -11,9 +13,15 @@ export default class TeamButton extends React.Component { constructor(props) { super(props); + this.handleSwitch = this.handleSwitch.bind(this); this.handleDisabled = this.handleDisabled.bind(this); } + handleSwitch(e) { + e.preventDefault(); + switchTeams(this.props.url); + } + handleDisabled(e) { e.preventDefault(); } @@ -22,7 +30,7 @@ export default class TeamButton extends React.Component { let teamClass = this.props.active ? 'active' : ''; const btnClass = this.props.btnClass; const disabled = this.props.disabled ? 'team-disabled' : ''; - const handleClick = (this.props.active || this.props.disabled) ? this.handleDisabled : null; + const handleClick = (this.props.active || this.props.disabled) ? this.handleDisabled : this.handleSwitch; let badge; if (!teamClass) { diff --git a/webapp/root.jsx b/webapp/root.jsx index b8fa4e6a2..93cfa5a38 100644 --- a/webapp/root.jsx +++ b/webapp/root.jsx @@ -11,7 +11,9 @@ import PDFJS from 'pdfjs-dist'; import * as GlobalActions from 'actions/global_actions.jsx'; import * as Websockets from 'actions/websocket_actions.jsx'; import BrowserStore from 'stores/browser_store.jsx'; +import ChannelStore from 'stores/channel_store.jsx'; import * as I18n from 'i18n/i18n.jsx'; +import * as AsyncClient from 'utils/async_client.jsx'; // Import our styles import 'bootstrap-colorpicker/dist/css/bootstrap-colorpicker.css'; @@ -58,6 +60,7 @@ function preRenderSetup(callwhendone) { $(window).on('beforeunload', () => { BrowserStore.setLastServerVersion(''); + AsyncClient.viewChannel('', ChannelStore.getCurrentId() || ''); Websockets.close(); } ); diff --git a/webapp/stores/channel_store.jsx b/webapp/stores/channel_store.jsx index 575f68f4c..488e49492 100644 --- a/webapp/stores/channel_store.jsx +++ b/webapp/stores/channel_store.jsx @@ -392,6 +392,31 @@ class ChannelStoreClass extends EventEmitter { return false; } + + incrementMessages(id) { + if (!this.unreadCounts[id]) { + return; + } + + this.unreadCounts[id].msgs++; + this.get(id).total_msg_count++; + } + + incrementMentionsIfNeeded(id, msgProps) { + let mentions = []; + if (msgProps && msgProps.mentions) { + mentions = JSON.parse(msgProps.mentions); + } + + if (!this.unreadCounts[id]) { + return; + } + + if (mentions.indexOf(UserStore.getCurrentId()) !== -1) { + this.unreadCounts[id].mentions++; + this.getMyMember(id).mention_count++; + } + } } var ChannelStore = new ChannelStoreClass(); @@ -469,6 +494,18 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => { ChannelStore.emitStatsChange(); break; + case ActionTypes.RECEIVED_POST: + var id = action.post.channel_id; + var teamId = action.websocketMessageProps ? action.websocketMessageProps.team_id : ''; + + // Current team and not current channel or the window is inactive + if (TeamStore.getCurrentId() === teamId && (ChannelStore.getCurrentId() !== id || !window.isActive)) { + ChannelStore.incrementMessages(id); + ChannelStore.incrementMentionsIfNeeded(id, action.websocketMessageProps); + ChannelStore.emitChange(); + } + break; + default: break; } diff --git a/webapp/stores/team_store.jsx b/webapp/stores/team_store.jsx index 2752c6b57..e58c79806 100644 --- a/webapp/stores/team_store.jsx +++ b/webapp/stores/team_store.jsx @@ -316,6 +316,23 @@ class TeamStoreClass extends EventEmitter { member.mention_count -= channelMember.mention_count; } } + + incrementMessages(id) { + const member = this.my_team_members.filter((m) => m.team_id === id)[0]; + member.msg_count++; + } + + incrementMentionsIfNeeded(id, msgProps) { + let mentions = []; + if (msgProps && msgProps.mentions) { + mentions = JSON.parse(msgProps.mentions); + } + + if (mentions.indexOf(UserStore.getCurrentId()) !== -1) { + const member = this.my_team_members.filter((m) => m.team_id === id)[0]; + member.mention_count++; + } + } } var TeamStore = new TeamStoreClass(); @@ -370,6 +387,14 @@ TeamStore.dispatchToken = AppDispatcher.register((payload) => { TeamStore.emitUnreadChange(); } break; + case ActionTypes.RECEIVED_POST: + var id = action.websocketMessageProps ? action.websocketMessageProps.team_id : ''; + if (TeamStore.getCurrentId() !== id && id.length > 0) { + TeamStore.incrementMessages(id); + TeamStore.incrementMentionsIfNeeded(id, action.websocketMessageProps); + TeamStore.emitChange(); + } + break; default: } }); -- cgit v1.2.3-1-g7c22