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
Этот коммит содержится в:
enahum
2016-10-27 12:24:30 -03:00
коммит произвёл Harrison Healey
родитель 14ce471311
Коммит f82667f3b8
22 изменённых файлов: 271 добавлений и 117 удалений

Просмотреть файл

@@ -21,6 +21,7 @@ func InitChannel() {
BaseRoutes.Channels.Handle("/", ApiUserRequired(getChannels)).Methods("GET") BaseRoutes.Channels.Handle("/", ApiUserRequired(getChannels)).Methods("GET")
BaseRoutes.Channels.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET") BaseRoutes.Channels.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET")
BaseRoutes.Channels.Handle("/counts", ApiUserRequired(getChannelCounts)).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", ApiUserRequired(createChannel)).Methods("POST")
BaseRoutes.Channels.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST") BaseRoutes.Channels.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST")
BaseRoutes.Channels.Handle("/update", ApiUserRequired(updateChannel)).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 return
} else { } else {
data := result.Data.(*model.ChannelList) 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}, "") c.Err = model.NewLocAppError("createChannel", "api.channel.create_channel.max_channel_limit.app_error", map[string]interface{}{"MaxChannelsPerTeam": *utils.Cfg.TeamSettings.MaxChannelsPerTeam}, "")
return 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) { func addMember(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r) params := mux.Vars(r)
id := params["channel_id"] id := params["channel_id"]

Просмотреть файл

@@ -35,7 +35,7 @@ func TestCreateChannel(t *testing.T) {
rget := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) rget := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
nameMatch := false nameMatch := false
for _, c := range rget.Channels { for _, c := range *rget {
if c.Name == channel.Name { if c.Name == channel.Name {
nameMatch = true nameMatch = true
} }
@@ -240,8 +240,8 @@ func TestUpdateChannel(t *testing.T) {
} }
rget := Client.Must(Client.GetChannels("")) rget := Client.Must(Client.GetChannels(""))
data := rget.Data.(*model.ChannelList) channels := rget.Data.(*model.ChannelList)
for _, c := range data.Channels { for _, c := range *channels {
if c.Name == model.DEFAULT_CHANNEL { if c.Name == model.DEFAULT_CHANNEL {
c.Header = "new header" c.Header = "new header"
c.Name = "pseudo-square" c.Name = "pseudo-square"
@@ -654,13 +654,13 @@ func TestGetChannel(t *testing.T) {
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
rget := Client.Must(Client.GetChannels("")) 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") 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") t.Fatal("full name didn't match")
} }
@@ -717,13 +717,13 @@ func TestGetMoreChannel(t *testing.T) {
th.LoginBasic2() th.LoginBasic2()
rget := Client.Must(Client.GetMoreChannels("")) 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") 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") 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) { func TestJoinChannelById(t *testing.T) {
th := Setup().InitBasic() th := Setup().InitBasic()
Client := th.BasicClient Client := th.BasicClient
@@ -905,7 +929,7 @@ func TestLeaveChannel(t *testing.T) {
rget := Client.Must(Client.GetChannels("")) rget := Client.Must(Client.GetChannels(""))
cdata := rget.Data.(*model.ChannelList) cdata := rget.Data.(*model.ChannelList)
for _, c := range cdata.Channels { for _, c := range *cdata {
if c.Name == model.DEFAULT_CHANNEL { if c.Name == model.DEFAULT_CHANNEL {
if _, err := Client.LeaveChannel(c.Id); err == nil { if _, err := Client.LeaveChannel(c.Id); err == nil {
t.Fatal("should have errored on leaving default channel") t.Fatal("should have errored on leaving default channel")
@@ -969,7 +993,7 @@ func TestDeleteChannel(t *testing.T) {
rget := Client.Must(Client.GetChannels("")) rget := Client.Must(Client.GetChannels(""))
cdata := rget.Data.(*model.ChannelList) cdata := rget.Data.(*model.ChannelList)
for _, c := range cdata.Channels { for _, c := range *cdata {
if c.Name == model.DEFAULT_CHANNEL { if c.Name == model.DEFAULT_CHANNEL {
if _, err := Client.DeleteChannel(c.Id); err == nil { if _, err := Client.DeleteChannel(c.Id); err == nil {
t.Fatal("should have errored on deleting default channel") t.Fatal("should have errored on deleting default channel")
@@ -1249,7 +1273,7 @@ func TestUpdateNotifyProps(t *testing.T) {
data["user_id"] = user.Id data["user_id"] = user.Id
data["desktop"] = model.CHANNEL_NOTIFY_MENTION data["desktop"] = model.CHANNEL_NOTIFY_MENTION
timeBeforeUpdate := model.GetMillis() //timeBeforeUpdate := model.GetMillis()
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
// test updating desktop // test updating desktop
@@ -1261,14 +1285,6 @@ func TestUpdateNotifyProps(t *testing.T) {
t.Fatalf("NotifyProps[\"mark_unread\"] changed to %v", notifyProps["mark_unread"]) 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 // test an empty update
delete(data, "desktop") delete(data, "desktop")

Просмотреть файл

@@ -38,7 +38,7 @@ func (me *JoinProvider) DoCommand(c *Context, channelId string, message string)
} else { } else {
channels := result.Data.(*model.ChannelList) channels := result.Data.(*model.ChannelList)
for _, v := range channels.Channels { for _, v := range *channels {
if v.Name == message { if v.Name == message {

Просмотреть файл

@@ -42,7 +42,7 @@ func TestJoinCommands(t *testing.T) {
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
found := false found := false
for _, c := range c1.Channels { for _, c := range *c1 {
if c.Id == channel2.Id { if c.Id == channel2.Id {
found = true found = true
} }

Просмотреть файл

@@ -325,20 +325,20 @@ func LeaveTeam(team *model.Team, user *model.User) *model.AppError {
teamMember = result.Data.(model.TeamMember) 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 := <-Srv.Store.Channel().GetChannels(team.Id, user.Id); result.Err != nil {
if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" { 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 { } else {
return result.Err return result.Err
} }
} else { } 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 { if channel.Type != model.CHANNEL_DIRECT {
Srv.Store.User().InvalidateProfilesInChannelCache(channel.Id) Srv.Store.User().InvalidateProfilesInChannelCache(channel.Id)
if result := <-Srv.Store.Channel().RemoveMember(channel.Id, user.Id); result.Err != nil { if result := <-Srv.Store.Channel().RemoveMember(channel.Id, user.Id); result.Err != nil {

Просмотреть файл

@@ -63,7 +63,7 @@ func TestCreateFromSignupTeam(t *testing.T) {
} }
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
if len(c1.Channels) != 2 { if len(*c1) != 2 {
t.Fatal("default channels not created") t.Fatal("default channels not created")
} }
@@ -94,7 +94,7 @@ func TestCreateTeam(t *testing.T) {
Client.SetTeamId(rteam.Data.(*model.Team).Id) Client.SetTeamId(rteam.Data.(*model.Team).Id)
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
if len(c1.Channels) != 2 { if len(*c1) != 2 {
t.Fatal("default channels not created") t.Fatal("default channels not created")
} }

Просмотреть файл

@@ -159,13 +159,13 @@ func getChannelID(channelname string, teamid string, userid string) (id string,
return "", false 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 { if channel.Name == channelname {
return channel.Id, true 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 return "", false
} }

Просмотреть файл

@@ -8,15 +8,11 @@ import (
"io" "io"
) )
type ChannelList struct { type ChannelList []*Channel
Channels []*Channel `json:"channels"`
Members map[string]*ChannelMember `json:"members"`
}
func (o *ChannelList) ToJson() string { func (o *ChannelList) ToJson() string {
b, err := json.Marshal(o) if b, err := json.Marshal(o); err != nil {
if err != nil { return "[]"
return ""
} else { } else {
return string(b) return string(b)
} }
@@ -28,7 +24,7 @@ func (o *ChannelList) Etag() string {
var t int64 = 0 var t int64 = 0
var delta int64 = 0 var delta int64 = 0
for _, v := range o.Channels { for _, v := range *o {
if v.LastPostAt > t { if v.LastPostAt > t {
t = v.LastPostAt t = v.LastPostAt
id = v.Id id = v.Id
@@ -39,30 +35,9 @@ func (o *ChannelList) Etag() string {
id = v.Id 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 { func ChannelListFromJson(data io.Reader) *ChannelList {

Просмотреть файл

@@ -29,6 +29,27 @@ type ChannelMember struct {
LastUpdateAt int64 `json:"last_update_at"` 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 { func (o *ChannelMember) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {

Просмотреть файл

@@ -1124,13 +1124,13 @@ func (c *Client) UpdateNotifyProps(data map[string]string) (*Result, *AppError)
} }
} }
func (c *Client) GetChannels(etag string) (*Result, *AppError) { func (c *Client) GetMyChannelMembers() (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/", "", etag); err != nil { if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/members", "", ""); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID), 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) { func (c *Client) JoinChannel(id string) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil { if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil {
return nil, err return nil, err

Просмотреть файл

@@ -367,23 +367,16 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string) StoreChannel
go func() { go func() {
result := StoreResult{} result := StoreResult{}
var data []channelWithMember data := &model.ChannelList{}
_, 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}) _, 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 { 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()) result.Err = model.NewLocAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error())
} else { } else {
channels := &model.ChannelList{make([]*model.Channel, len(data)), make(map[string]*model.ChannelMember)} if len(*data) == 0 {
for i := range data {
v := data[i]
channels.Channels[i] = &v.Channel
channels.Members[v.Channel.Id] = &v.ChannelMember
}
if len(channels.Channels) == 0 {
result.Err = model.NewLocAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.not_found.app_error", nil, "teamId="+teamId+", userId="+userId) result.Err = model.NewLocAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.not_found.app_error", nil, "teamId="+teamId+", userId="+userId)
} else { } else {
result.Data = channels result.Data = data
} }
} }
@@ -400,8 +393,8 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string) StoreChan
go func() { go func() {
result := StoreResult{} result := StoreResult{}
var data []*model.Channel data := &model.ChannelList{}
_, err := s.GetReplica().Select(&data, _, err := s.GetReplica().Select(data,
`SELECT `SELECT
* *
FROM FROM
@@ -426,7 +419,7 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string) StoreChan
if err != nil { 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()) result.Err = model.NewLocAppError("SqlChannelStore.GetMoreChannels", "store.sql_channel.get_more_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error())
} else { } else {
result.Data = &model.ChannelList{data, make(map[string]*model.ChannelMember)} result.Data = data
} }
storeChannel <- result storeChannel <- result
@@ -918,11 +911,12 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string)
`UPDATE `UPDATE
ChannelMembers ChannelMembers
SET SET
MentionCount = MentionCount + 1 MentionCount = MentionCount + 1,
LastUpdateAt = :LastUpdateAt
WHERE WHERE
UserId = :UserId UserId = :UserId
AND ChannelId = :ChannelId`, AND ChannelId = :ChannelId`,
map[string]interface{}{"ChannelId": channelId, "UserId": userId}) map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": model.GetMillis()})
if err != nil { 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()) 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 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
}

Просмотреть файл

@@ -305,14 +305,14 @@ func TestChannelStoreDelete(t *testing.T) {
cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId) cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId)
list := cresult.Data.(*model.ChannelList) list := cresult.Data.(*model.ChannelList)
if len(list.Channels) != 1 { if len(*list) != 1 {
t.Fatal("invalid number of channels") t.Fatal("invalid number of channels")
} }
cresult = <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId) cresult = <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId)
list = cresult.Data.(*model.ChannelList) list = cresult.Data.(*model.ChannelList)
if len(list.Channels) != 1 { if len(*list) != 1 {
t.Fatal("invalid number of channels") t.Fatal("invalid number of channels")
} }
} }
@@ -514,7 +514,7 @@ func TestChannelStoreGetChannels(t *testing.T) {
cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId) cresult := <-store.Channel().GetChannels(o1.TeamId, m1.UserId)
list := cresult.Data.(*model.ChannelList) list := cresult.Data.(*model.ChannelList)
if list.Channels[0].Id != o1.Id { if (*list)[0].Id != o1.Id {
t.Fatal("missing channel") t.Fatal("missing channel")
} }
@@ -614,11 +614,11 @@ func TestChannelStoreGetMoreChannels(t *testing.T) {
cresult := <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId) cresult := <-store.Channel().GetMoreChannels(o1.TeamId, m1.UserId)
list := cresult.Data.(*model.ChannelList) list := cresult.Data.(*model.ChannelList)
if len(list.Channels) != 1 { if len(*list) != 1 {
t.Fatal("wrong list") t.Fatal("wrong list")
} }
if list.Channels[0].Name != o3.Name { if (*list)[0].Name != o3.Name {
t.Fatal("missing channel") 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) { func TestChannelStoreUpdateLastViewedAt(t *testing.T) {
Setup() Setup()

Просмотреть файл

@@ -109,6 +109,7 @@ type ChannelStore interface {
IncrementMentionCount(channelId string, userId string) StoreChannel IncrementMentionCount(channelId string, userId string) StoreChannel
AnalyticsTypeCount(teamId string, channelType string) StoreChannel AnalyticsTypeCount(teamId string, channelType string) StoreChannel
ExtraUpdateByUser(userId string, time int64) StoreChannel ExtraUpdateByUser(userId string, time int64) StoreChannel
GetMembersForUser(teamId string, userId string) StoreChannel
} }
type PostStore interface { type PostStore interface {

Просмотреть файл

@@ -42,8 +42,6 @@ export function emitChannelClickEvent(channel) {
); );
} }
function switchToChannel(chan) { function switchToChannel(chan) {
AsyncClient.getChannels(true);
AsyncClient.getMoreChannels(true);
AsyncClient.getChannelStats(chan.id); AsyncClient.getChannelStats(chan.id);
AsyncClient.updateLastViewedAt(chan.id); AsyncClient.updateLastViewedAt(chan.id);
loadPosts(chan.id); loadPosts(chan.id);
@@ -436,10 +434,6 @@ export function loadDefaultLocale() {
} }
export function viewLoggedIn() { export function viewLoggedIn() {
AsyncClient.getChannels();
AsyncClient.getMoreChannels();
AsyncClient.getChannelStats();
// Clear pending posts (shouldn't have pending posts if we are loading) // Clear pending posts (shouldn't have pending posts if we are loading)
PostStore.clearPendingPosts(); PostStore.clearPendingPosts();
} }

Просмотреть файл

@@ -120,7 +120,7 @@ export function setUnreadPost(channelId, postId) {
member.msg_count = channel.total_msg_count - unreadPosts; member.msg_count = channel.total_msg_count - unreadPosts;
member.mention_count = 0; member.mention_count = 0;
ChannelStore.storeMyChannelMember(member); ChannelStore.storeMyChannelMember(member);
ChannelStore.setUnreadCount(channelId); ChannelStore.setUnreadCountByChannel(channelId);
AsyncClient.setLastViewedAt(lastViewed, channelId); AsyncClient.setLastViewedAt(lastViewed, channelId);
} }

Просмотреть файл

@@ -76,6 +76,7 @@ function handleFirstConnect() {
function handleReconnect() { function handleReconnect() {
if (Client.teamId) { if (Client.teamId) {
AsyncClient.getChannels(); AsyncClient.getChannels();
AsyncClient.getMyChannelMembers();
loadPosts(ChannelStore.getCurrentId()); loadPosts(ChannelStore.getCurrentId());
} }

Просмотреть файл

@@ -1357,6 +1357,15 @@ export default class Client {
end(this.handleResponse.bind(this, 'getChannelCounts', success, error)); 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) { getChannelStats(channelId, success, error) {
request. request.
get(`${this.getChannelNeededRoute(channelId)}/stats`). get(`${this.getChannelNeededRoute(channelId)}/stats`).

Просмотреть файл

@@ -75,10 +75,11 @@ function preNeedsTeam(nextState, replace, callback) {
(data) => { (data) => {
AppDispatcher.handleServerAction({ AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CHANNELS, type: ActionTypes.RECEIVED_CHANNELS,
channels: data.channels, channels: data
members: data.members
}); });
AsyncClient.getMyChannelMembers();
d1.resolve(); d1.resolve();
}, },
(err) => { (err) => {

Просмотреть файл

@@ -156,7 +156,7 @@ class ChannelStoreClass extends EventEmitter {
if (c) { if (c) {
cm[cmid].msg_count = this.get(id).total_msg_count; cm[cmid].msg_count = this.get(id).total_msg_count;
cm[cmid].mention_count = 0; cm[cmid].mention_count = 0;
this.setUnreadCount(id); this.setUnreadCountByChannel(id);
} }
break; break;
} }
@@ -250,6 +250,12 @@ class ChannelStoreClass extends EventEmitter {
this.myChannelMembers = channelMembers; this.myChannelMembers = channelMembers;
} }
storeMyChannelMembersList(channelMembers) {
channelMembers.forEach((m) => {
this.myChannelMembers[m.channel_id] = m;
});
}
getMyMembers() { getMyMembers() {
return this.myChannelMembers; return this.myChannelMembers;
} }
@@ -278,7 +284,13 @@ class ChannelStoreClass extends EventEmitter {
return this.postMode; return this.postMode;
} }
setUnreadCount(id) { setUnreadCountsByMembers(members) {
members.forEach((m) => {
this.setUnreadCountByChannel(m.channel_id);
});
}
setUnreadCountByChannel(id) {
const ch = this.get(id); const ch = this.get(id);
const chMember = this.getMyMember(id); const chMember = this.getMyMember(id);
@@ -292,13 +304,6 @@ class ChannelStoreClass extends EventEmitter {
this.unreadCounts[id] = {msgs: chUnreadCount, mentions: chMentionCount}; this.unreadCounts[id] = {msgs: chUnreadCount, mentions: chMentionCount};
} }
setUnreadCounts() {
const channels = this.getAll();
channels.forEach((ch) => {
this.setUnreadCount(ch.id);
});
}
getUnreadCount(id) { getUnreadCount(id) {
return this.unreadCounts[id] || {msgs: 0, mentions: 0}; return this.unreadCounts[id] || {msgs: 0, mentions: 0};
} }
@@ -362,12 +367,6 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
case ActionTypes.RECEIVED_CHANNELS: case ActionTypes.RECEIVED_CHANNELS:
ChannelStore.storeChannels(action.channels); ChannelStore.storeChannels(action.channels);
ChannelStore.storeMyChannelMembers(action.members);
currentId = ChannelStore.getCurrentId();
if (currentId && window.isActive) {
ChannelStore.resetCounts(currentId);
}
ChannelStore.setUnreadCounts();
ChannelStore.emitChange(); ChannelStore.emitChange();
break; break;
@@ -380,10 +379,18 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
if (currentId && window.isActive) { if (currentId && window.isActive) {
ChannelStore.resetCounts(currentId); ChannelStore.resetCounts(currentId);
} }
ChannelStore.setUnreadCount(action.channel.id); ChannelStore.setUnreadCountByChannel(action.channel.id);
ChannelStore.emitChange(); ChannelStore.emitChange();
break; 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: case ActionTypes.RECEIVED_MORE_CHANNELS:
ChannelStore.storeMoreChannels(action.channels); ChannelStore.storeMoreChannels(action.channels);
ChannelStore.emitMoreChange(); ChannelStore.emitMoreChange();

Просмотреть файл

@@ -232,7 +232,7 @@ describe('Client.Channels', function() {
TestHelper.initBasic(() => { TestHelper.initBasic(() => {
TestHelper.basicClient().getChannels( TestHelper.basicClient().getChannels(
function(data) { function(data) {
assert.equal(data.channels.length, 3); assert.equal(data.length, 3);
done(); done();
}, },
function(err) { function(err) {
@@ -261,7 +261,7 @@ describe('Client.Channels', function() {
TestHelper.initBasic(() => { TestHelper.initBasic(() => {
TestHelper.basicClient().getMoreChannels( TestHelper.basicClient().getMoreChannels(
function(data) { function(data) {
assert.equal(data.channels.length, 0); assert.equal(data.length, 0);
done(); done();
}, },
function(err) { 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) { it('getChannelStats', function(done) {
TestHelper.initBasic(() => { TestHelper.initBasic(() => {
TestHelper.basicClient().getChannelStats( TestHelper.basicClient().getChannelStats(

Просмотреть файл

@@ -80,8 +80,7 @@ export function getChannels(doVersionCheck) {
AppDispatcher.handleServerAction({ AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CHANNELS, type: ActionTypes.RECEIVED_CHANNELS,
channels: data.channels, channels: data
members: data.members
}); });
}, },
(err) => { (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) { export function updateLastViewedAt(id, active) {
let channelId; let channelId;
if (id) { if (id) {
@@ -205,8 +231,7 @@ export function getMoreChannels(force) {
AppDispatcher.handleServerAction({ AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_MORE_CHANNELS, type: ActionTypes.RECEIVED_MORE_CHANNELS,
channels: data.channels, channels: data
members: data.members
}); });
}, },
(err) => { (err) => {

Просмотреть файл

@@ -73,6 +73,7 @@ export const ActionTypes = keyMirror({
RECEIVED_CHANNEL: null, RECEIVED_CHANNEL: null,
RECEIVED_MORE_CHANNELS: null, RECEIVED_MORE_CHANNELS: null,
RECEIVED_CHANNEL_STATS: null, RECEIVED_CHANNEL_STATS: null,
RECEIVED_MY_CHANNEL_MEMBERS: null,
FOCUS_POST: null, FOCUS_POST: null,
RECEIVED_POSTS: null, RECEIVED_POSTS: null,