From ba6e370ca71abacaa30234cb164427d27c86df13 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Wed, 21 Dec 2016 16:35:01 -0500 Subject: PLT-5012 Combine updateLastViewedAt, setLastViewedAt and setActiveChannel into a single API (#4840) * Combine updateLastViewedAt, setLastViewedAt and setActiveChannel into a single API * Remove preference DB writes --- api/channel.go | 168 ++++++++++-------------- api/channel_test.go | 46 ++++++- api/deprecated.go | 126 +++++++++++++++++- api/deprecated_test.go | 41 ++++++ api/status.go | 40 ------ api/status_test.go | 41 ------ model/channel_view.go | 35 +++++ model/client.go | 27 ++++ webapp/actions/channel_actions.jsx | 2 +- webapp/actions/global_actions.jsx | 2 +- webapp/actions/post_actions.jsx | 2 +- webapp/actions/websocket_actions.jsx | 2 +- webapp/client/client.jsx | 13 ++ webapp/components/needs_team.jsx | 4 +- webapp/components/post_view/post_view_cache.jsx | 2 +- webapp/tests/client_channel.test.jsx | 17 +++ webapp/utils/async_client.jsx | 94 ++----------- 17 files changed, 386 insertions(+), 276 deletions(-) create mode 100644 model/channel_view.go diff --git a/api/channel.go b/api/channel.go index 941692ac3..feebf6981 100644 --- a/api/channel.go +++ b/api/channel.go @@ -25,6 +25,7 @@ func InitChannel() { BaseRoutes.Channels.Handle("/counts", ApiUserRequired(getChannelCounts)).Methods("GET") BaseRoutes.Channels.Handle("/members", ApiUserRequired(getMyChannelMembers)).Methods("GET") BaseRoutes.Channels.Handle("/create", ApiUserRequired(createChannel)).Methods("POST") + BaseRoutes.Channels.Handle("/view", ApiUserRequired(viewChannel)).Methods("POST") BaseRoutes.Channels.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST") BaseRoutes.Channels.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST") BaseRoutes.Channels.Handle("/update_header", ApiUserRequired(updateChannelHeader)).Methods("POST") @@ -43,9 +44,6 @@ func InitChannel() { BaseRoutes.NeedChannel.Handle("/delete", ApiUserRequired(deleteChannel)).Methods("POST") BaseRoutes.NeedChannel.Handle("/add", ApiUserRequired(addMember)).Methods("POST") BaseRoutes.NeedChannel.Handle("/remove", ApiUserRequired(removeMember)).Methods("POST") - BaseRoutes.NeedChannel.Handle("/update_last_viewed_at", ApiUserRequired(updateLastViewedAt)).Methods("POST") - BaseRoutes.NeedChannel.Handle("/set_last_viewed_at", ApiUserRequired(setLastViewedAt)).Methods("POST") - } func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { @@ -873,103 +871,6 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { } } -func setLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id := params["channel_id"] - - data := model.StringInterfaceFromJson(r.Body) - newLastViewedAt := int64(data["last_viewed_at"].(float64)) - - Srv.Store.Channel().SetLastViewedAt(id, c.Session.UserId, newLastViewedAt) - - chanPref := model.Preference{ - UserId: c.Session.UserId, - Category: c.TeamId, - Name: model.PREFERENCE_NAME_LAST_CHANNEL, - Value: id, - } - - teamPref := model.Preference{ - UserId: c.Session.UserId, - Category: model.PREFERENCE_CATEGORY_LAST, - Name: model.PREFERENCE_NAME_LAST_TEAM, - Value: c.TeamId, - } - - 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 Publish(message) - - result := make(map[string]string) - result["id"] = id - w.Write([]byte(model.MapToJson(result))) -} - -func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id := params["channel_id"] - - data := model.StringInterfaceFromJson(r.Body) - - var active bool - var ok bool - if active, ok = data["active"].(bool); !ok { - active = true - } - - doClearPush := false - if *utils.Cfg.EmailSettings.SendPushNotifications && !c.Session.IsMobileApp() && active { - if result := <-Srv.Store.User().GetUnreadCountForChannel(c.Session.UserId, id); result.Err != nil { - l4g.Error(utils.T("api.channel.update_last_viewed_at.get_unread_count_for_channel.error"), c.Session.UserId, id, result.Err.Error()) - } else { - if result.Data.(int64) > 0 { - doClearPush = true - } - } - } - - go func() { - if err := SetActiveChannel(c.Session.UserId, id); err != nil { - l4g.Error(err.Error()) - } - }() - - Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId) - - // Must be after update so that unread count is correct - if doClearPush { - go clearPushNotification(c.Session.UserId, id) - } - - chanPref := model.Preference{ - UserId: c.Session.UserId, - Category: c.TeamId, - Name: model.PREFERENCE_NAME_LAST_CHANNEL, - Value: id, - } - - teamPref := model.Preference{ - UserId: c.Session.UserId, - Category: model.PREFERENCE_CATEGORY_LAST, - Name: model.PREFERENCE_NAME_LAST_TEAM, - Value: c.TeamId, - } - - 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 Publish(message) - - result := make(map[string]string) - result["id"] = id - w.Write([]byte(model.MapToJson(result))) -} - func getChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] @@ -1001,7 +902,27 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(data.ToJson())) } } +} + +func SetActiveChannel(userId string, channelId string) *model.AppError { + status, err := GetStatus(userId) + if err != nil { + status = &model.Status{userId, model.STATUS_ONLINE, false, model.GetMillis(), channelId} + } else { + status.ActiveChannel = channelId + if !status.Manual { + status.Status = model.STATUS_ONLINE + } + status.LastActivityAt = model.GetMillis() + } + + AddStatusCache(status) + + if result := <-Srv.Store.Status().SaveOrUpdate(status); result.Err != nil { + return result.Err + } + return nil } func getChannelByName(c *Context, w http.ResponseWriter, r *http.Request) { @@ -1332,3 +1253,50 @@ func autocompleteChannels(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(channels.ToJson())) } + +func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) { + view := model.ChannelViewFromJson(r.Body) + + go func() { + if err := SetActiveChannel(c.Session.UserId, view.ChannelId); err != nil { + l4g.Error(err.Error()) + } + }() + + if len(view.ChannelId) > 0 { + + if view.Time == 0 { + if result := <-Srv.Store.Channel().UpdateLastViewedAt(view.ChannelId, c.Session.UserId); result.Err != nil { + c.Err = result.Err + return + } + } else { + if result := <-Srv.Store.Channel().SetLastViewedAt(view.ChannelId, c.Session.UserId, view.Time); result.Err != nil { + c.Err = result.Err + return + } + } + + if len(view.PrevChannelId) > 0 { + Srv.Store.Channel().UpdateLastViewedAt(view.PrevChannelId, c.Session.UserId) + + // Only clear push notifications if a channel switch occured + if *utils.Cfg.EmailSettings.SendPushNotifications && !c.Session.IsMobileApp() { + go func() { + if result := <-Srv.Store.User().GetUnreadCountForChannel(c.Session.UserId, view.ChannelId); result.Err != nil { + l4g.Error(utils.T("api.channel.update_last_viewed_at.get_unread_count_for_channel.error"), c.Session.UserId, view.ChannelId, result.Err.Error()) + } else { + if result.Data.(int64) > 0 { + clearPushNotification(c.Session.UserId, view.ChannelId) + } + } + }() + } + } + + message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil) + message.Add("channel_id", view.ChannelId) + } + + ReturnStatusOK(w) +} diff --git a/api/channel_test.go b/api/channel_test.go index ae4573c9c..d24a6d603 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -724,8 +724,9 @@ func TestGetChannel(t *testing.T) { t.Fatal("cache should be empty") } - if _, err := Client.UpdateLastViewedAt(channel2.Id, true); err != nil { - t.Fatal(err) + view := model.ChannelView{ChannelId: channel2.Id, PrevChannelId: channel1.Id} + if _, resp := Client.ViewChannel(view); resp.Error != nil { + t.Fatal(resp.Error) } if resp, err := Client.GetChannel(channel1.Id, ""); err != nil { @@ -1735,3 +1736,44 @@ func TestGetChannelByName(t *testing.T) { t.Fatal("Should fail due to not enough permissions") } } + +func TestViewChannel(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + + view := model.ChannelView{ + ChannelId: th.BasicChannel.Id, + } + + if _, resp := Client.ViewChannel(view); resp.Error != nil { + t.Fatal(resp.Error) + } + + view.PrevChannelId = th.BasicChannel.Id + + if _, resp := Client.ViewChannel(view); resp.Error != nil { + t.Fatal(resp.Error) + } + + view.PrevChannelId = "" + view.Time = 1234567890 + + if _, resp := Client.ViewChannel(view); resp.Error != nil { + t.Fatal(resp.Error) + } + + view.PrevChannelId = "junk" + view.Time = 0 + + if _, resp := Client.ViewChannel(view); resp.Error != nil { + t.Fatal(resp.Error) + } + + rdata := Client.Must(Client.GetChannel(th.BasicChannel.Id, "")).Data.(*model.ChannelData) + + if rdata.Channel.TotalMsgCount != rdata.Member.MsgCount { + t.Log(rdata.Channel.TotalMsgCount) + t.Log(rdata.Member.MsgCount) + t.Fatal("message counts don't match") + } +} diff --git a/api/deprecated.go b/api/deprecated.go index 427abdedf..183552414 100644 --- a/api/deprecated.go +++ b/api/deprecated.go @@ -7,6 +7,7 @@ import ( "net/http" l4g "github.com/alecthomas/log4go" + "github.com/gorilla/mux" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" ) @@ -16,7 +17,15 @@ import ( func InitDeprecated() { l4g.Debug(utils.T("api.channel.init.debug")) - BaseRoutes.Channels.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET") // SCHEDULED FOR DEPRECATION IN 3.7 + /* start - SCHEDULED FOR DEPRECATION IN 3.7 */ + BaseRoutes.Channels.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET") + /* end - SCHEDULED FOR DEPRECATION IN 3.7 */ + + /* start - SCHEDULED FOR DEPRECATION IN 3.8 */ + BaseRoutes.NeedChannel.Handle("/update_last_viewed_at", ApiUserRequired(updateLastViewedAt)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/set_last_viewed_at", ApiUserRequired(setLastViewedAt)).Methods("POST") + BaseRoutes.Users.Handle("/status/set_active_channel", ApiUserRequired(setActiveChannel)).Methods("POST") + /* end - SCHEDULED FOR DEPRECATION IN 3.8 */ } func getMoreChannels(c *Context, w http.ResponseWriter, r *http.Request) { @@ -37,3 +46,118 @@ func getMoreChannels(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(data.ToJson())) } } + +func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + id := params["channel_id"] + + data := model.StringInterfaceFromJson(r.Body) + + var active bool + var ok bool + if active, ok = data["active"].(bool); !ok { + active = true + } + + doClearPush := false + if *utils.Cfg.EmailSettings.SendPushNotifications && !c.Session.IsMobileApp() && active { + if result := <-Srv.Store.User().GetUnreadCountForChannel(c.Session.UserId, id); result.Err != nil { + l4g.Error(utils.T("api.channel.update_last_viewed_at.get_unread_count_for_channel.error"), c.Session.UserId, id, result.Err.Error()) + } else { + if result.Data.(int64) > 0 { + doClearPush = true + } + } + } + + go func() { + if err := SetActiveChannel(c.Session.UserId, id); err != nil { + l4g.Error(err.Error()) + } + }() + + Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId) + + // Must be after update so that unread count is correct + if doClearPush { + go clearPushNotification(c.Session.UserId, id) + } + + chanPref := model.Preference{ + UserId: c.Session.UserId, + Category: c.TeamId, + Name: model.PREFERENCE_NAME_LAST_CHANNEL, + Value: id, + } + + teamPref := model.Preference{ + UserId: c.Session.UserId, + Category: model.PREFERENCE_CATEGORY_LAST, + Name: model.PREFERENCE_NAME_LAST_TEAM, + Value: c.TeamId, + } + + 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 Publish(message) + + result := make(map[string]string) + result["id"] = id + w.Write([]byte(model.MapToJson(result))) +} + +func setLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + id := params["channel_id"] + + data := model.StringInterfaceFromJson(r.Body) + newLastViewedAt := int64(data["last_viewed_at"].(float64)) + + Srv.Store.Channel().SetLastViewedAt(id, c.Session.UserId, newLastViewedAt) + + chanPref := model.Preference{ + UserId: c.Session.UserId, + Category: c.TeamId, + Name: model.PREFERENCE_NAME_LAST_CHANNEL, + Value: id, + } + + teamPref := model.Preference{ + UserId: c.Session.UserId, + Category: model.PREFERENCE_CATEGORY_LAST, + Name: model.PREFERENCE_NAME_LAST_TEAM, + Value: c.TeamId, + } + + 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 Publish(message) + + result := make(map[string]string) + result["id"] = id + w.Write([]byte(model.MapToJson(result))) +} + +func setActiveChannel(c *Context, w http.ResponseWriter, r *http.Request) { + data := model.MapFromJson(r.Body) + + var channelId string + var ok bool + if channelId, ok = data["channel_id"]; !ok || len(channelId) > 26 { + c.SetInvalidParam("setActiveChannel", "channel_id") + return + } + + if err := SetActiveChannel(c.Session.UserId, channelId); err != nil { + c.Err = err + return + } + + ReturnStatusOK(w) +} diff --git a/api/deprecated_test.go b/api/deprecated_test.go index 000b3950d..ee2ce0b2c 100644 --- a/api/deprecated_test.go +++ b/api/deprecated_test.go @@ -41,3 +41,44 @@ func TestGetMoreChannel(t *testing.T) { t.Fatal("cache should be empty") } } + +/* +func TestSetActiveChannel(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + + if _, err := Client.SetActiveChannel(th.BasicChannel.Id); err != nil { + t.Fatal(err) + } + + status, _ := GetStatus(th.BasicUser.Id) + if status.ActiveChannel != th.BasicChannel.Id { + t.Fatal("active channel should be set") + } + + if _, err := Client.SetActiveChannel(""); err != nil { + t.Fatal(err) + } + + status, _ = GetStatus(th.BasicUser.Id) + if status.ActiveChannel != "" { + t.Fatal("active channel should be blank") + } + + if _, err := Client.SetActiveChannel("123456789012345678901234567890"); err == nil { + t.Fatal("should have failed, id too long") + } + + if _, err := Client.UpdateLastViewedAt(th.BasicChannel.Id, true); err != nil { + t.Fatal(err) + } + + time.Sleep(500 * time.Millisecond) + + status, _ = GetStatus(th.BasicUser.Id) + need to check if offline to catch race + if status.Status != model.STATUS_OFFLINE && status.ActiveChannel != th.BasicChannel.Id { + t.Fatal("active channel should be set") + } +} +*/ diff --git a/api/status.go b/api/status.go index a0ad4cd76..99d5bc417 100644 --- a/api/status.go +++ b/api/status.go @@ -37,7 +37,6 @@ func InitStatus() { BaseRoutes.Users.Handle("/status", ApiUserRequired(getStatusesHttp)).Methods("GET") BaseRoutes.Users.Handle("/status/ids", ApiUserRequired(getStatusesByIdsHttp)).Methods("POST") - BaseRoutes.Users.Handle("/status/set_active_channel", ApiUserRequired(setActiveChannel)).Methods("POST") BaseRoutes.WebSocket.Handle("get_statuses", ApiWebSocketHandler(getStatusesWebSocket)) BaseRoutes.WebSocket.Handle("get_statuses_by_ids", ApiWebSocketHandler(getStatusesByIdsWebSocket)) } @@ -305,42 +304,3 @@ func DoesStatusAllowPushNotification(user *model.User, status *model.Status, cha return false } - -func setActiveChannel(c *Context, w http.ResponseWriter, r *http.Request) { - data := model.MapFromJson(r.Body) - - var channelId string - var ok bool - if channelId, ok = data["channel_id"]; !ok || len(channelId) > 26 { - c.SetInvalidParam("setActiveChannel", "channel_id") - return - } - - if err := SetActiveChannel(c.Session.UserId, channelId); err != nil { - c.Err = err - return - } - - ReturnStatusOK(w) -} - -func SetActiveChannel(userId string, channelId string) *model.AppError { - status, err := GetStatus(userId) - if err != nil { - status = &model.Status{userId, model.STATUS_ONLINE, false, model.GetMillis(), channelId} - } else { - status.ActiveChannel = channelId - if !status.Manual { - status.Status = model.STATUS_ONLINE - } - status.LastActivityAt = model.GetMillis() - } - - AddStatusCache(status) - - if result := <-Srv.Store.Status().SaveOrUpdate(status); result.Err != nil { - return result.Err - } - - return nil -} diff --git a/api/status_test.go b/api/status_test.go index 2cf8c624b..5db5a8d7c 100644 --- a/api/status_test.go +++ b/api/status_test.go @@ -229,44 +229,3 @@ func TestGetStatusesByIds(t *testing.T) { t.Fatal("should have errored") } } - -/* -func TestSetActiveChannel(t *testing.T) { - th := Setup().InitBasic() - Client := th.BasicClient - - if _, err := Client.SetActiveChannel(th.BasicChannel.Id); err != nil { - t.Fatal(err) - } - - status, _ := GetStatus(th.BasicUser.Id) - if status.ActiveChannel != th.BasicChannel.Id { - t.Fatal("active channel should be set") - } - - if _, err := Client.SetActiveChannel(""); err != nil { - t.Fatal(err) - } - - status, _ = GetStatus(th.BasicUser.Id) - if status.ActiveChannel != "" { - t.Fatal("active channel should be blank") - } - - if _, err := Client.SetActiveChannel("123456789012345678901234567890"); err == nil { - t.Fatal("should have failed, id too long") - } - - if _, err := Client.UpdateLastViewedAt(th.BasicChannel.Id, true); err != nil { - t.Fatal(err) - } - - time.Sleep(500 * time.Millisecond) - - status, _ = GetStatus(th.BasicUser.Id) - need to check if offline to catch race - if status.Status != model.STATUS_OFFLINE && status.ActiveChannel != th.BasicChannel.Id { - t.Fatal("active channel should be set") - } -} -*/ diff --git a/model/channel_view.go b/model/channel_view.go new file mode 100644 index 000000000..9803c4bbc --- /dev/null +++ b/model/channel_view.go @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type ChannelView struct { + ChannelId string `json:"channel_id"` + PrevChannelId string `json:"prev_channel_id"` + Time int64 `json:"time"` +} + +func (o *ChannelView) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func ChannelViewFromJson(data io.Reader) *ChannelView { + decoder := json.NewDecoder(data) + var o ChannelView + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/model/client.go b/model/client.go index e5f5fcea4..9ff34f5bf 100644 --- a/model/client.go +++ b/model/client.go @@ -48,6 +48,13 @@ type Result struct { Data interface{} } +type ResponseMetadata struct { + StatusCode int + Error *AppError + RequestId string + Etag string +} + type Client struct { Url string // The location of the server like "http://localhost:8065" ApiUrl string // The api location of the server like "http://localhost:8065/api/v3" @@ -1329,6 +1336,7 @@ func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) { // UpdateLastViewedAt will mark a channel as read. // The channelId indicates the channel to mark as read. If active is true, push notifications // will be cleared if there are unread messages. The default for active is true. +// SCHEDULED FOR DEPRECATION IN 3.8 - use ViewChannel instead func (c *Client) UpdateLastViewedAt(channelId string, active bool) (*Result, *AppError) { data := make(map[string]interface{}) data["active"] = active @@ -1341,6 +1349,24 @@ func (c *Client) UpdateLastViewedAt(channelId string, active bool) (*Result, *Ap } } +// ViewChannel performs all the actions related to viewing a channel. This includes marking +// the channel and the previous one as read, marking the channel as being actively viewed. +// ChannelId is required but may be blank to indicate no channel is being viewed. +// PrevChannelId is optional, populate to indicate a channel switch occurred. Optionally +// provide a non-zero Time, in Unix milliseconds, to manually set the viewing time. +func (c *Client) ViewChannel(params ChannelView) (bool, *ResponseMetadata) { + if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/view", params.ToJson()); err != nil { + return false, &ResponseMetadata{StatusCode: r.StatusCode, Error: err} + } else { + return c.CheckStatusOK(r), + &ResponseMetadata{ + StatusCode: r.StatusCode, + RequestId: r.Header.Get(HEADER_REQUEST_ID), + Etag: r.Header.Get(HEADER_ETAG_SERVER), + } + } +} + func (c *Client) GetChannelStats(id string, etag string) (*Result, *AppError) { if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/stats", "", etag); err != nil { return nil, err @@ -1718,6 +1744,7 @@ func (c *Client) GetStatusesByIds(userIds []string) (*Result, *AppError) { // SetActiveChannel sets the the channel id the user is currently viewing. // The channelId key is required but the value can be blank. Returns standard // response. +// SCHEDULED FOR DEPRECATION IN 3.8 - use ViewChannel instead func (c *Client) SetActiveChannel(channelId string) (*Result, *AppError) { data := map[string]string{} data["channel_id"] = channelId diff --git a/webapp/actions/channel_actions.jsx b/webapp/actions/channel_actions.jsx index ad2f315ee..952c8ada3 100644 --- a/webapp/actions/channel_actions.jsx +++ b/webapp/actions/channel_actions.jsx @@ -53,7 +53,7 @@ export function executeCommand(message, args, success, error) { export function setChannelAsRead(channelIdParam) { const channelId = channelIdParam || ChannelStore.getCurrentId(); - AsyncClient.updateLastViewedAt(); + AsyncClient.viewChannel(); ChannelStore.resetCounts(channelId); ChannelStore.emitChange(); if (channelId === ChannelStore.getCurrentId()) { diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx index 9d135dd26..e1009e9c2 100644 --- a/webapp/actions/global_actions.jsx +++ b/webapp/actions/global_actions.jsx @@ -48,7 +48,7 @@ export function emitChannelClickEvent(channel) { getMyChannelMembersPromise.then(() => { AsyncClient.getChannelStats(chan.id, true); - AsyncClient.updateLastViewedAt(chan.id); + AsyncClient.viewChannel(chan.id, ChannelStore.getCurrentId()); loadPosts(chan.id); trackPage(); }); diff --git a/webapp/actions/post_actions.jsx b/webapp/actions/post_actions.jsx index d1e69cda7..71b9e826e 100644 --- a/webapp/actions/post_actions.jsx +++ b/webapp/actions/post_actions.jsx @@ -22,7 +22,7 @@ export function handleNewPost(post, msg) { if (ChannelStore.getCurrentId() === post.channel_id) { if (window.isActive) { - AsyncClient.updateLastViewedAt(null, false); + AsyncClient.viewChannel(); } else { AsyncClient.getChannel(post.channel_id); } diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx index 6c81a4ac9..f24802003 100644 --- a/webapp/actions/websocket_actions.jsx +++ b/webapp/actions/websocket_actions.jsx @@ -205,7 +205,7 @@ function handlePostEditEvent(msg) { // Update channel state if (ChannelStore.getCurrentId() === msg.broadcast.channel_id) { if (window.isActive) { - AsyncClient.updateLastViewedAt(null, false); + AsyncClient.viewChannel(); } } } diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx index 3ec36644f..c5743ae7b 100644 --- a/webapp/client/client.jsx +++ b/webapp/client/client.jsx @@ -1187,6 +1187,7 @@ export default class Client { end(this.handleResponse.bind(this, 'getStatuses', success, error)); } + // SCHEDULED FOR DEPRECATION IN 3.8 - use viewChannel instead setActiveChannel(id, success, error) { request. post(`${this.getUsersRoute()}/status/set_active_channel`). @@ -1366,6 +1367,17 @@ export default class Client { this.track('api', 'api_channels_delete'); } + viewChannel(channelId, prevChannelId = '', time = 0, success, error) { + request. + post(`${this.getChannelsRoute()}/view`). + set(this.defaultHeaders). + type('application/json'). + accept('application/json'). + send({channel_id: channelId, prev_channel_id: prevChannelId, time}). + end(this.handleResponse.bind(this, 'viewChannel', success, error)); + } + + // SCHEDULED FOR DEPRECATION IN 3.8 - use viewChannel instead updateLastViewedAt(channelId, active, success, error) { request. post(`${this.getChannelNeededRoute(channelId)}/update_last_viewed_at`). @@ -1376,6 +1388,7 @@ export default class Client { end(this.handleResponse.bind(this, 'updateLastViewedAt', success, error)); } + // SCHEDULED FOR DEPRECATION IN 3.8 - use viewChannel instead setLastViewedAt(channelId, lastViewedAt, success, error) { request. post(`${this.getChannelNeededRoute(channelId)}/set_last_viewed_at`). diff --git a/webapp/components/needs_team.jsx b/webapp/components/needs_team.jsx index f90297065..0b91814c3 100644 --- a/webapp/components/needs_team.jsx +++ b/webapp/components/needs_team.jsx @@ -94,7 +94,7 @@ export default class NeedsTeam extends React.Component { // Set up tracking for whether the window is active window.isActive = true; $(window).on('focus', () => { - AsyncClient.updateLastViewedAt(); + AsyncClient.viewChannel(); ChannelStore.resetCounts(ChannelStore.getCurrentId()); ChannelStore.emitChange(); window.isActive = true; @@ -103,7 +103,7 @@ export default class NeedsTeam extends React.Component { $(window).on('blur', () => { window.isActive = false; if (UserStore.getCurrentUser()) { - AsyncClient.setActiveChannel(''); + AsyncClient.viewChannel(''); } }); diff --git a/webapp/components/post_view/post_view_cache.jsx b/webapp/components/post_view/post_view_cache.jsx index 3b6123b09..7de11d667 100644 --- a/webapp/components/post_view/post_view_cache.jsx +++ b/webapp/components/post_view/post_view_cache.jsx @@ -32,7 +32,7 @@ export default class PostViewCache extends React.Component { componentWillUnmount() { if (UserStore.getCurrentUser()) { - AsyncClient.setActiveChannel(''); + AsyncClient.viewChannel(''); } ChannelStore.removeChangeListener(this.onChannelChange); } diff --git a/webapp/tests/client_channel.test.jsx b/webapp/tests/client_channel.test.jsx index 08c821f3c..77f9f9653 100644 --- a/webapp/tests/client_channel.test.jsx +++ b/webapp/tests/client_channel.test.jsx @@ -211,6 +211,23 @@ describe('Client.Channels', function() { }); }); + it('viewChannel', function(done) { + TestHelper.initBasic(() => { + var channel = TestHelper.basicChannel(); + TestHelper.basicClient().viewChannel( + channel.id, + '', + 0, + function() { + done(); + }, + function(err) { + done(new Error(err.message)); + } + ); + }); + }); + it('updateLastViewedAt', function(done) { TestHelper.initBasic(() => { var channel = TestHelper.basicChannel(); diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx index 2d8e76fc2..66f13f04d 100644 --- a/webapp/utils/async_client.jsx +++ b/webapp/utils/async_client.jsx @@ -138,33 +138,20 @@ export function getMyChannelMembers() { }); } -export function updateLastViewedAt(id, active) { - let channelId; - if (id) { - channelId = id; - } else { - channelId = ChannelStore.getCurrentId(); - } - +export function viewChannel(channelId = ChannelStore.getCurrentId(), prevChannelId = '', time = 0) { if (channelId == null) { return; } - if (isCallInProgress(`updateLastViewed${channelId}`)) { + if (isCallInProgress(`viewChannel${channelId}`)) { return; } - let isActive; - if (active == null) { - isActive = true; - } else { - isActive = active; - } - - callTracker[`updateLastViewed${channelId}`] = utils.getTimestamp(); - Client.updateLastViewedAt( + callTracker[`viewChannel${channelId}`] = utils.getTimestamp(); + Client.viewChannel( channelId, - isActive, + prevChannelId, + time, () => { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_PREFERENCE, @@ -175,59 +162,14 @@ export function updateLastViewedAt(id, active) { } }); - callTracker[`updateLastViewed${channelId}`] = 0; + callTracker[`viewChannel${channelId}`] = 0; ErrorStore.clearLastError(); }, (err) => { - callTracker[`updateLastViewed${channelId}`] = 0; + callTracker[`viewChannel${channelId}`] = 0; const count = ErrorStore.getConnectionErrorCount(); ErrorStore.setConnectionErrorCount(count + 1); - dispatchError(err, 'updateLastViewedAt'); - } - ); -} - -export function setLastViewedAt(lastViewedAt, id) { - let channelId; - if (id) { - channelId = id; - } else { - channelId = ChannelStore.getCurrentId(); - } - - if (channelId == null) { - return; - } - - if (lastViewedAt == null) { - return; - } - - if (isCallInProgress(`setLastViewedAt${channelId}${lastViewedAt}`)) { - return; - } - - callTracker[`setLastViewedAt${channelId}${lastViewedAt}`] = utils.getTimestamp(); - Client.setLastViewedAt( - channelId, - lastViewedAt, - () => { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECEIVED_PREFERENCE, - preference: { - category: 'last', - name: TeamStore.getCurrentId(), - value: channelId - } - }); - callTracker[`setLastViewedAt${channelId}${lastViewedAt}`] = 0; - ErrorStore.clearLastError(); - }, - (err) => { - callTracker[`setLastViewedAt${channelId}${lastViewedAt}`] = 0; - var count = ErrorStore.getConnectionErrorCount(); - ErrorStore.setConnectionErrorCount(count + 1); - dispatchError(err, 'setLastViewedAt'); + dispatchError(err, 'viewChannel'); } ); } @@ -795,24 +737,6 @@ export function getStatuses() { ); } -export function setActiveChannel(channelId) { - if (isCallInProgress(`setActiveChannel${channelId}`)) { - return; - } - - callTracker[`setActiveChannel${channelId}`] = utils.getTimestamp(); - Client.setActiveChannel( - channelId, - () => { - callTracker[`setActiveChannel${channelId}`] = 0; - }, - (err) => { - callTracker[`setActiveChannel${channelId}`] = 0; - dispatchError(err, 'setActiveChannel'); - } - ); -} - export function getMyTeam() { if (isCallInProgress('getMyTeam')) { return null; -- cgit v1.2.3-1-g7c22