From f82667f3b86202dafff3a2a4ea56aec74c80316d Mon Sep 17 00:00:00 2001 From: enahum Date: Thu, 27 Oct 2016 12:24:30 -0300 Subject: PLT-4430 improve slow channel switching (#4331) * PLT-4430 improve slow channel switching * Update client side unit tests * Convert getChannelsUnread to getMyChannelMembers and address other feedback * Pull channel members on websocket reconnect --- api/channel.go | 13 ++++++++- api/channel_test.go | 56 +++++++++++++++++++++++------------- api/command_join.go | 2 +- api/command_join_test.go | 2 +- api/team.go | 8 +++--- api/team_test.go | 4 +-- manualtesting/manual_testing.go | 6 ++-- model/channel_list.go | 35 ++++------------------ model/channel_member.go | 21 ++++++++++++++ model/client.go | 16 +++++++++-- store/sql_channel_store.go | 55 ++++++++++++++++++++++++----------- store/sql_channel_store_test.go | 55 +++++++++++++++++++++++++++++++---- store/store.go | 1 + webapp/actions/global_actions.jsx | 6 ---- webapp/actions/post_actions.jsx | 2 +- webapp/actions/websocket_actions.jsx | 1 + webapp/client/client.jsx | 9 ++++++ webapp/routes/route_team.jsx | 5 ++-- webapp/stores/channel_store.jsx | 39 ++++++++++++++----------- webapp/tests/client_channel.test.jsx | 18 ++++++++++-- webapp/utils/async_client.jsx | 33 ++++++++++++++++++--- webapp/utils/constants.jsx | 1 + 22 files changed, 271 insertions(+), 117 deletions(-) diff --git a/api/channel.go b/api/channel.go index bae2a5277..2232786fd 100644 --- a/api/channel.go +++ b/api/channel.go @@ -21,6 +21,7 @@ func InitChannel() { BaseRoutes.Channels.Handle("/", ApiUserRequired(getChannels)).Methods("GET") BaseRoutes.Channels.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET") 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("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST") BaseRoutes.Channels.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST") @@ -81,7 +82,7 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } else { data := result.Data.(*model.ChannelList) - if int64(len(data.Channels)+1) > *utils.Cfg.TeamSettings.MaxChannelsPerTeam { + if int64(len(*data)+1) > *utils.Cfg.TeamSettings.MaxChannelsPerTeam { c.Err = model.NewLocAppError("createChannel", "api.channel.create_channel.max_channel_limit.app_error", map[string]interface{}{"MaxChannelsPerTeam": *utils.Cfg.TeamSettings.MaxChannelsPerTeam}, "") return } @@ -987,6 +988,16 @@ func getChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { } } +func getMyChannelMembers(c *Context, w http.ResponseWriter, r *http.Request) { + if result := <-Srv.Store.Channel().GetMembersForUser(c.TeamId, c.Session.UserId); result.Err != nil { + c.Err = result.Err + return + } else { + data := result.Data.(*model.ChannelMembers) + w.Write([]byte(data.ToJson())) + } +} + func addMember(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] diff --git a/api/channel_test.go b/api/channel_test.go index 4835ee9b7..83bb732dd 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -35,7 +35,7 @@ func TestCreateChannel(t *testing.T) { rget := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) nameMatch := false - for _, c := range rget.Channels { + for _, c := range *rget { if c.Name == channel.Name { nameMatch = true } @@ -240,8 +240,8 @@ func TestUpdateChannel(t *testing.T) { } rget := Client.Must(Client.GetChannels("")) - data := rget.Data.(*model.ChannelList) - for _, c := range data.Channels { + channels := rget.Data.(*model.ChannelList) + for _, c := range *channels { if c.Name == model.DEFAULT_CHANNEL { c.Header = "new header" c.Name = "pseudo-square" @@ -654,13 +654,13 @@ func TestGetChannel(t *testing.T) { channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) rget := Client.Must(Client.GetChannels("")) - data := rget.Data.(*model.ChannelList) + channels := rget.Data.(*model.ChannelList) - if data.Channels[0].DisplayName != channel1.DisplayName { + if (*channels)[0].DisplayName != channel1.DisplayName { t.Fatal("full name didn't match") } - if data.Channels[1].DisplayName != channel2.DisplayName { + if (*channels)[1].DisplayName != channel2.DisplayName { t.Fatal("full name didn't match") } @@ -717,13 +717,13 @@ func TestGetMoreChannel(t *testing.T) { th.LoginBasic2() rget := Client.Must(Client.GetMoreChannels("")) - data := rget.Data.(*model.ChannelList) + channels := rget.Data.(*model.ChannelList) - if data.Channels[0].DisplayName != channel1.DisplayName { + if (*channels)[0].DisplayName != channel1.DisplayName { t.Fatal("full name didn't match") } - if data.Channels[1].DisplayName != channel2.DisplayName { + if (*channels)[1].DisplayName != channel2.DisplayName { t.Fatal("full name didn't match") } @@ -770,6 +770,30 @@ func TestGetChannelCounts(t *testing.T) { } +func TestGetMyChannelMembers(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + + channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} + channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + + channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} + channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) + + if result, err := Client.GetMyChannelMembers(); err != nil { + t.Fatal(err) + } else { + members := result.Data.(*model.ChannelMembers) + + // town-square, off-topic, basic test channel, channel1, channel2 + if len(*members) != 5 { + t.Fatal("wrong number of members", len(*members)) + } + } + +} + func TestJoinChannelById(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient @@ -905,7 +929,7 @@ func TestLeaveChannel(t *testing.T) { rget := Client.Must(Client.GetChannels("")) cdata := rget.Data.(*model.ChannelList) - for _, c := range cdata.Channels { + for _, c := range *cdata { if c.Name == model.DEFAULT_CHANNEL { if _, err := Client.LeaveChannel(c.Id); err == nil { t.Fatal("should have errored on leaving default channel") @@ -969,7 +993,7 @@ func TestDeleteChannel(t *testing.T) { rget := Client.Must(Client.GetChannels("")) cdata := rget.Data.(*model.ChannelList) - for _, c := range cdata.Channels { + for _, c := range *cdata { if c.Name == model.DEFAULT_CHANNEL { if _, err := Client.DeleteChannel(c.Id); err == nil { t.Fatal("should have errored on deleting default channel") @@ -1249,7 +1273,7 @@ func TestUpdateNotifyProps(t *testing.T) { data["user_id"] = user.Id data["desktop"] = model.CHANNEL_NOTIFY_MENTION - timeBeforeUpdate := model.GetMillis() + //timeBeforeUpdate := model.GetMillis() time.Sleep(100 * time.Millisecond) // test updating desktop @@ -1261,14 +1285,6 @@ func TestUpdateNotifyProps(t *testing.T) { t.Fatalf("NotifyProps[\"mark_unread\"] changed to %v", notifyProps["mark_unread"]) } - rget := Client.Must(Client.GetChannels("")) - rdata := rget.Data.(*model.ChannelList) - if len(rdata.Members) == 0 || rdata.Members[channel1.Id].NotifyProps["desktop"] != data["desktop"] { - t.Fatal("NotifyProps[\"desktop\"] did not update properly") - } else if rdata.Members[channel1.Id].LastUpdateAt <= timeBeforeUpdate { - t.Fatal("LastUpdateAt did not update") - } - // test an empty update delete(data, "desktop") diff --git a/api/command_join.go b/api/command_join.go index b8c863425..2aba1bbd5 100644 --- a/api/command_join.go +++ b/api/command_join.go @@ -38,7 +38,7 @@ func (me *JoinProvider) DoCommand(c *Context, channelId string, message string) } else { channels := result.Data.(*model.ChannelList) - for _, v := range channels.Channels { + for _, v := range *channels { if v.Name == message { diff --git a/api/command_join_test.go b/api/command_join_test.go index 6cf474c6b..a1dbace41 100644 --- a/api/command_join_test.go +++ b/api/command_join_test.go @@ -42,7 +42,7 @@ func TestJoinCommands(t *testing.T) { c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) found := false - for _, c := range c1.Channels { + for _, c := range *c1 { if c.Id == channel2.Id { found = true } diff --git a/api/team.go b/api/team.go index 4d4795ab6..46e822504 100644 --- a/api/team.go +++ b/api/team.go @@ -325,20 +325,20 @@ func LeaveTeam(team *model.Team, user *model.User) *model.AppError { teamMember = result.Data.(model.TeamMember) } - var channelMembers *model.ChannelList + var channelList *model.ChannelList if result := <-Srv.Store.Channel().GetChannels(team.Id, user.Id); result.Err != nil { if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" { - channelMembers = &model.ChannelList{make([]*model.Channel, 0), make(map[string]*model.ChannelMember)} + channelList = &model.ChannelList{} } else { return result.Err } } else { - channelMembers = result.Data.(*model.ChannelList) + channelList = result.Data.(*model.ChannelList) } - for _, channel := range channelMembers.Channels { + for _, channel := range *channelList { if channel.Type != model.CHANNEL_DIRECT { Srv.Store.User().InvalidateProfilesInChannelCache(channel.Id) if result := <-Srv.Store.Channel().RemoveMember(channel.Id, user.Id); result.Err != nil { diff --git a/api/team_test.go b/api/team_test.go index a58710145..bac0228ad 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -63,7 +63,7 @@ func TestCreateFromSignupTeam(t *testing.T) { } c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) - if len(c1.Channels) != 2 { + if len(*c1) != 2 { t.Fatal("default channels not created") } @@ -94,7 +94,7 @@ func TestCreateTeam(t *testing.T) { Client.SetTeamId(rteam.Data.(*model.Team).Id) c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) - if len(c1.Channels) != 2 { + if len(*c1) != 2 { t.Fatal("default channels not created") } diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go index 77a5b89c8..9a2d557bc 100644 --- a/manualtesting/manual_testing.go +++ b/manualtesting/manual_testing.go @@ -159,13 +159,13 @@ func getChannelID(channelname string, teamid string, userid string) (id string, return "", false } - data := result.Data.(*model.ChannelList) + data := result.Data.(model.ChannelList) - for _, channel := range data.Channels { + for _, channel := range data { if channel.Name == channelname { return channel.Id, true } } - l4g.Debug(utils.T("manaultesting.get_channel_id.no_found.debug"), channelname, strconv.Itoa(len(data.Channels))) + l4g.Debug(utils.T("manaultesting.get_channel_id.no_found.debug"), channelname, strconv.Itoa(len(data))) return "", false } diff --git a/model/channel_list.go b/model/channel_list.go index 49ba384a2..7a46de45d 100644 --- a/model/channel_list.go +++ b/model/channel_list.go @@ -8,15 +8,11 @@ import ( "io" ) -type ChannelList struct { - Channels []*Channel `json:"channels"` - Members map[string]*ChannelMember `json:"members"` -} +type ChannelList []*Channel func (o *ChannelList) ToJson() string { - b, err := json.Marshal(o) - if err != nil { - return "" + if b, err := json.Marshal(o); err != nil { + return "[]" } else { return string(b) } @@ -28,7 +24,7 @@ func (o *ChannelList) Etag() string { var t int64 = 0 var delta int64 = 0 - for _, v := range o.Channels { + for _, v := range *o { if v.LastPostAt > t { t = v.LastPostAt id = v.Id @@ -39,30 +35,9 @@ func (o *ChannelList) Etag() string { id = v.Id } - member := o.Members[v.Id] - - if member != nil { - max := v.LastPostAt - if v.UpdateAt > max { - max = v.UpdateAt - } - - delta += max - member.LastViewedAt - - if member.LastViewedAt > t { - t = member.LastViewedAt - id = v.Id - } - - if member.LastUpdateAt > t { - t = member.LastUpdateAt - id = v.Id - } - - } } - return Etag(id, t, delta, len(o.Channels)) + return Etag(id, t, delta, len(*o)) } func ChannelListFromJson(data io.Reader) *ChannelList { diff --git a/model/channel_member.go b/model/channel_member.go index 705c6bfbd..4180bb8e6 100644 --- a/model/channel_member.go +++ b/model/channel_member.go @@ -29,6 +29,27 @@ type ChannelMember struct { LastUpdateAt int64 `json:"last_update_at"` } +type ChannelMembers []ChannelMember + +func (o *ChannelMembers) ToJson() string { + if b, err := json.Marshal(o); err != nil { + return "[]" + } else { + return string(b) + } +} + +func ChannelMembersFromJson(data io.Reader) *ChannelMembers { + decoder := json.NewDecoder(data) + var o ChannelMembers + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} + func (o *ChannelMember) ToJson() string { b, err := json.Marshal(o) if err != nil { diff --git a/model/client.go b/model/client.go index f5aeea4db..e9d6c512c 100644 --- a/model/client.go +++ b/model/client.go @@ -1124,13 +1124,13 @@ func (c *Client) UpdateNotifyProps(data map[string]string) (*Result, *AppError) } } -func (c *Client) GetChannels(etag string) (*Result, *AppError) { - if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/", "", etag); err != nil { +func (c *Client) GetMyChannelMembers() (*Result, *AppError) { + if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/members", "", ""); err != nil { return nil, err } else { defer closeBody(r) return &Result{r.Header.Get(HEADER_REQUEST_ID), - r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil + r.Header.Get(HEADER_ETAG_SERVER), ChannelMembersFromJson(r.Body)}, nil } } @@ -1164,6 +1164,16 @@ func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) { } } +func (c *Client) GetChannels(etag string) (*Result, *AppError) { + if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/", "", etag); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil + } +} + func (c *Client) JoinChannel(id string) (*Result, *AppError) { if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil { return nil, err diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index f863d57fd..f1cf7f849 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -367,23 +367,16 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string) StoreChannel go func() { result := StoreResult{} - var data []channelWithMember - _, err := s.GetReplica().Select(&data, "SELECT * FROM Channels, ChannelMembers WHERE Id = ChannelId AND UserId = :UserId AND DeleteAt = 0 AND (TeamId = :TeamId OR TeamId = '') ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId, "UserId": userId}) + data := &model.ChannelList{} + _, err := s.GetReplica().Select(data, "SELECT Channels.* FROM Channels, ChannelMembers WHERE Id = ChannelId AND UserId = :UserId AND DeleteAt = 0 AND (TeamId = :TeamId OR TeamId = '') ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId, "UserId": userId}) if err != nil { result.Err = model.NewLocAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error()) } else { - channels := &model.ChannelList{make([]*model.Channel, len(data)), make(map[string]*model.ChannelMember)} - for i := range data { - v := data[i] - channels.Channels[i] = &v.Channel - channels.Members[v.Channel.Id] = &v.ChannelMember - } - - if len(channels.Channels) == 0 { + if len(*data) == 0 { result.Err = model.NewLocAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.not_found.app_error", nil, "teamId="+teamId+", userId="+userId) } else { - result.Data = channels + result.Data = data } } @@ -400,8 +393,8 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string) StoreChan go func() { result := StoreResult{} - var data []*model.Channel - _, err := s.GetReplica().Select(&data, + data := &model.ChannelList{} + _, err := s.GetReplica().Select(data, `SELECT * FROM @@ -426,7 +419,7 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string) StoreChan if err != nil { result.Err = model.NewLocAppError("SqlChannelStore.GetMoreChannels", "store.sql_channel.get_more_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error()) } else { - result.Data = &model.ChannelList{data, make(map[string]*model.ChannelMember)} + result.Data = data } storeChannel <- result @@ -918,11 +911,12 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string) `UPDATE ChannelMembers SET - MentionCount = MentionCount + 1 + MentionCount = MentionCount + 1, + LastUpdateAt = :LastUpdateAt WHERE UserId = :UserId AND ChannelId = :ChannelId`, - map[string]interface{}{"ChannelId": channelId, "UserId": userId}) + map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": model.GetMillis()}) if err != nil { result.Err = model.NewLocAppError("SqlChannelStore.IncrementMentionCount", "store.sql_channel.increment_mention_count.app_error", nil, "channel_id="+channelId+", user_id="+userId+", "+err.Error()) } @@ -1032,3 +1026,32 @@ func (s SqlChannelStore) ExtraUpdateByUser(userId string, time int64) StoreChann return storeChannel } + +func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + result := StoreResult{} + + members := &model.ChannelMembers{} + _, err := s.GetReplica().Select(members, ` + SELECT cm.* + FROM ChannelMembers cm + INNER JOIN Channels c + ON c.Id = cm.ChannelId + AND c.TeamId = :TeamId + WHERE cm.UserId = :UserId + `, map[string]interface{}{"TeamId": teamId, "UserId": userId}) + + if err != nil { + result.Err = model.NewLocAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error()) + } else { + result.Data = members + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_channel_store_test.go b/store/sql_channel_store_test.go index d80d54d52..8a51d2ae3 100644 --- a/store/sql_channel_store_test.go +++ b/store/sql_channel_store_test.go @@ -305,14 +305,14 @@ func TestChannelStoreDelete(t *testing.T) { cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId) list := cresult.Data.(*model.ChannelList) - if len(list.Channels) != 1 { + if len(*list) != 1 { t.Fatal("invalid number of channels") } cresult = <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId) list = cresult.Data.(*model.ChannelList) - if len(list.Channels) != 1 { + if len(*list) != 1 { t.Fatal("invalid number of channels") } } @@ -514,7 +514,7 @@ func TestChannelStoreGetChannels(t *testing.T) { cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId) list := cresult.Data.(*model.ChannelList) - if list.Channels[0].Id != o1.Id { + if (*list)[0].Id != o1.Id { t.Fatal("missing channel") } @@ -614,11 +614,11 @@ func TestChannelStoreGetMoreChannels(t *testing.T) { cresult := <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId) list := cresult.Data.(*model.ChannelList) - if len(list.Channels) != 1 { + if len(*list) != 1 { t.Fatal("wrong list") } - if list.Channels[0].Name != o3.Name { + if (*list)[0].Name != o3.Name { t.Fatal("missing channel") } @@ -688,6 +688,51 @@ func TestChannelStoreGetChannelCounts(t *testing.T) { } } +func TestChannelStoreGetMembersForUser(t *testing.T) { + Setup() + + t1 := model.Team{} + t1.DisplayName = "Name" + t1.Name = model.NewId() + t1.Email = model.NewId() + "@nowhere.com" + t1.Type = model.TEAM_OPEN + Must(store.Team().Save(&t1)) + + o1 := model.Channel{} + o1.TeamId = t1.Id + o1.DisplayName = "Channel1" + o1.Name = "a" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + Must(store.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = o1.TeamId + o2.DisplayName = "Channel2" + o2.Name = "a" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + Must(store.Channel().Save(&o2)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + Must(store.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o2.Id + m2.UserId = m1.UserId + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + Must(store.Channel().SaveMember(&m2)) + + cresult := <-store.Channel().GetMembersForUser(o1.TeamId, m1.UserId) + members := cresult.Data.(*model.ChannelMembers) + + // no unread messages + if len(*members) != 2 { + t.Fatal("wrong number of members") + } +} + func TestChannelStoreUpdateLastViewedAt(t *testing.T) { Setup() diff --git a/store/store.go b/store/store.go index 51aada920..6cf216699 100644 --- a/store/store.go +++ b/store/store.go @@ -109,6 +109,7 @@ type ChannelStore interface { IncrementMentionCount(channelId string, userId string) StoreChannel AnalyticsTypeCount(teamId string, channelType string) StoreChannel ExtraUpdateByUser(userId string, time int64) StoreChannel + GetMembersForUser(teamId string, userId string) StoreChannel } type PostStore interface { diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx index 23ff5a295..123c1c392 100644 --- a/webapp/actions/global_actions.jsx +++ b/webapp/actions/global_actions.jsx @@ -42,8 +42,6 @@ export function emitChannelClickEvent(channel) { ); } function switchToChannel(chan) { - AsyncClient.getChannels(true); - AsyncClient.getMoreChannels(true); AsyncClient.getChannelStats(chan.id); AsyncClient.updateLastViewedAt(chan.id); loadPosts(chan.id); @@ -436,10 +434,6 @@ export function loadDefaultLocale() { } export function viewLoggedIn() { - AsyncClient.getChannels(); - AsyncClient.getMoreChannels(); - AsyncClient.getChannelStats(); - // Clear pending posts (shouldn't have pending posts if we are loading) PostStore.clearPendingPosts(); } diff --git a/webapp/actions/post_actions.jsx b/webapp/actions/post_actions.jsx index 462576021..1a2056a2e 100644 --- a/webapp/actions/post_actions.jsx +++ b/webapp/actions/post_actions.jsx @@ -120,7 +120,7 @@ export function setUnreadPost(channelId, postId) { member.msg_count = channel.total_msg_count - unreadPosts; member.mention_count = 0; ChannelStore.storeMyChannelMember(member); - ChannelStore.setUnreadCount(channelId); + ChannelStore.setUnreadCountByChannel(channelId); AsyncClient.setLastViewedAt(lastViewed, channelId); } diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx index c3a3010b0..e58bded0d 100644 --- a/webapp/actions/websocket_actions.jsx +++ b/webapp/actions/websocket_actions.jsx @@ -76,6 +76,7 @@ function handleFirstConnect() { function handleReconnect() { if (Client.teamId) { AsyncClient.getChannels(); + AsyncClient.getMyChannelMembers(); loadPosts(ChannelStore.getCurrentId()); } diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx index 596242e41..fd091fd69 100644 --- a/webapp/client/client.jsx +++ b/webapp/client/client.jsx @@ -1357,6 +1357,15 @@ export default class Client { end(this.handleResponse.bind(this, 'getChannelCounts', success, error)); } + getMyChannelMembers(success, error) { + request. + get(`${this.getChannelsRoute()}/members`). + set(this.defaultHeaders). + type('application/json'). + accept('application/json'). + end(this.handleResponse.bind(this, 'getMyChannelMembers', success, error)); + } + getChannelStats(channelId, success, error) { request. get(`${this.getChannelNeededRoute(channelId)}/stats`). diff --git a/webapp/routes/route_team.jsx b/webapp/routes/route_team.jsx index e63be5a5e..608052a58 100644 --- a/webapp/routes/route_team.jsx +++ b/webapp/routes/route_team.jsx @@ -75,10 +75,11 @@ function preNeedsTeam(nextState, replace, callback) { (data) => { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_CHANNELS, - channels: data.channels, - members: data.members + channels: data }); + AsyncClient.getMyChannelMembers(); + d1.resolve(); }, (err) => { diff --git a/webapp/stores/channel_store.jsx b/webapp/stores/channel_store.jsx index 20e7c966f..2ca01fc6e 100644 --- a/webapp/stores/channel_store.jsx +++ b/webapp/stores/channel_store.jsx @@ -156,7 +156,7 @@ class ChannelStoreClass extends EventEmitter { if (c) { cm[cmid].msg_count = this.get(id).total_msg_count; cm[cmid].mention_count = 0; - this.setUnreadCount(id); + this.setUnreadCountByChannel(id); } break; } @@ -250,6 +250,12 @@ class ChannelStoreClass extends EventEmitter { this.myChannelMembers = channelMembers; } + storeMyChannelMembersList(channelMembers) { + channelMembers.forEach((m) => { + this.myChannelMembers[m.channel_id] = m; + }); + } + getMyMembers() { return this.myChannelMembers; } @@ -278,7 +284,13 @@ class ChannelStoreClass extends EventEmitter { return this.postMode; } - setUnreadCount(id) { + setUnreadCountsByMembers(members) { + members.forEach((m) => { + this.setUnreadCountByChannel(m.channel_id); + }); + } + + setUnreadCountByChannel(id) { const ch = this.get(id); const chMember = this.getMyMember(id); @@ -292,13 +304,6 @@ class ChannelStoreClass extends EventEmitter { 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}; } @@ -362,12 +367,6 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => { case ActionTypes.RECEIVED_CHANNELS: ChannelStore.storeChannels(action.channels); - ChannelStore.storeMyChannelMembers(action.members); - currentId = ChannelStore.getCurrentId(); - if (currentId && window.isActive) { - ChannelStore.resetCounts(currentId); - } - ChannelStore.setUnreadCounts(); ChannelStore.emitChange(); break; @@ -380,10 +379,18 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => { if (currentId && window.isActive) { ChannelStore.resetCounts(currentId); } - ChannelStore.setUnreadCount(action.channel.id); + ChannelStore.setUnreadCountByChannel(action.channel.id); ChannelStore.emitChange(); break; + case ActionTypes.RECEIVED_MY_CHANNEL_MEMBERS: + ChannelStore.storeMyChannelMembersList(action.members); + currentId = ChannelStore.getCurrentId(); + if (currentId && window.isActive) { + ChannelStore.resetCounts(currentId); + } + ChannelStore.setUnreadCountsByMembers(action.members); + break; case ActionTypes.RECEIVED_MORE_CHANNELS: ChannelStore.storeMoreChannels(action.channels); ChannelStore.emitMoreChange(); diff --git a/webapp/tests/client_channel.test.jsx b/webapp/tests/client_channel.test.jsx index 92145f6e1..b7fa57dc8 100644 --- a/webapp/tests/client_channel.test.jsx +++ b/webapp/tests/client_channel.test.jsx @@ -232,7 +232,7 @@ describe('Client.Channels', function() { TestHelper.initBasic(() => { TestHelper.basicClient().getChannels( function(data) { - assert.equal(data.channels.length, 3); + assert.equal(data.length, 3); done(); }, function(err) { @@ -261,7 +261,7 @@ describe('Client.Channels', function() { TestHelper.initBasic(() => { TestHelper.basicClient().getMoreChannels( function(data) { - assert.equal(data.channels.length, 0); + assert.equal(data.length, 0); done(); }, function(err) { @@ -285,6 +285,20 @@ describe('Client.Channels', function() { }); }); + it('getMyChannelMembers', function(done) { + TestHelper.initBasic(() => { + TestHelper.basicClient().getMyChannelMembers( + function(data) { + assert.equal(data.length > 0, true); + done(); + }, + function(err) { + done(new Error(err.message)); + } + ); + }); + }); + it('getChannelStats', function(done) { TestHelper.initBasic(() => { TestHelper.basicClient().getChannelStats( diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx index ee9d1a4f0..67100ea3f 100644 --- a/webapp/utils/async_client.jsx +++ b/webapp/utils/async_client.jsx @@ -80,8 +80,7 @@ export function getChannels(doVersionCheck) { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_CHANNELS, - channels: data.channels, - members: data.members + channels: data }); }, (err) => { @@ -115,6 +114,33 @@ export function getChannel(id) { ); } +export function getMyChannelMembers(doVersionCheck) { + if (isCallInProgress('getMyChannelMembers')) { + return; + } + + callTracker.getMyChannelMembers = utils.getTimestamp(); + + Client.getMyChannelMembers( + (data) => { + callTracker.getMyChannelMembers = 0; + + if (doVersionCheck) { + checkVersion(); + } + + AppDispatcher.handleServerAction({ + type: ActionTypes.RECEIVED_MY_CHANNEL_MEMBERS, + members: data + }); + }, + (err) => { + callTracker.getChannelsUnread = 0; + dispatchError(err, 'getMyChannelMembers'); + } + ); +} + export function updateLastViewedAt(id, active) { let channelId; if (id) { @@ -205,8 +231,7 @@ export function getMoreChannels(force) { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_MORE_CHANNELS, - channels: data.channels, - members: data.members + channels: data }); }, (err) => { diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index f87b36fc8..6ea8d040e 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -73,6 +73,7 @@ export const ActionTypes = keyMirror({ RECEIVED_CHANNEL: null, RECEIVED_MORE_CHANNELS: null, RECEIVED_CHANNEL_STATS: null, + RECEIVED_MY_CHANNEL_MEMBERS: null, FOCUS_POST: null, RECEIVED_POSTS: null, -- cgit v1.2.3-1-g7c22