PLT-4167 Team Sidebar (#4569)
* PLT-4167 Team Sidebar * Address feedback from PM * change route from my_members to members * bug fixes * Updating styles for teams sidebar (#4681) * Added PM changes * Fix corner cases * Addressing feedback * use two different endpoints * Bug fixes * Rename model and client functions, using preferences to store last team and channel viewed * Fix mobile notification count and closing the team sidebar * unit test, fixed bad merge and retrieve from cached when available * bug fixes * use id for last channel in preferences, query optimization * Updating multi team css (#4830)
Этот коммит содержится в:
коммит произвёл
Joram Wilander
родитель
3ce2ce9dc8
Коммит
999d1553e1
@@ -846,14 +846,21 @@ func setLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
Srv.Store.Channel().SetLastViewedAt(id, c.Session.UserId, newLastViewedAt)
|
Srv.Store.Channel().SetLastViewedAt(id, c.Session.UserId, newLastViewedAt)
|
||||||
|
|
||||||
preference := model.Preference{
|
chanPref := model.Preference{
|
||||||
UserId: c.Session.UserId,
|
UserId: c.Session.UserId,
|
||||||
Category: model.PREFERENCE_CATEGORY_LAST,
|
Category: c.TeamId,
|
||||||
Name: model.PREFERENCE_NAME_LAST_CHANNEL,
|
Name: model.PREFERENCE_NAME_LAST_CHANNEL,
|
||||||
Value: id,
|
Value: id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Srv.Store.Preference().Save(&model.Preferences{preference})
|
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 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil)
|
||||||
message.Add("channel_id", id)
|
message.Add("channel_id", id)
|
||||||
@@ -901,14 +908,21 @@ func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
go clearPushNotification(c.Session.UserId, id)
|
go clearPushNotification(c.Session.UserId, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
preference := model.Preference{
|
chanPref := model.Preference{
|
||||||
UserId: c.Session.UserId,
|
UserId: c.Session.UserId,
|
||||||
Category: model.PREFERENCE_CATEGORY_LAST,
|
Category: c.TeamId,
|
||||||
Name: model.PREFERENCE_NAME_LAST_CHANNEL,
|
Name: model.PREFERENCE_NAME_LAST_CHANNEL,
|
||||||
Value: id,
|
Value: id,
|
||||||
}
|
}
|
||||||
|
|
||||||
Srv.Store.Preference().Save(&model.Preferences{preference})
|
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 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil)
|
||||||
message.Add("channel_id", id)
|
message.Add("channel_id", id)
|
||||||
|
|||||||
@@ -824,6 +824,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
|||||||
message.Add("post", post.ToJson())
|
message.Add("post", post.ToJson())
|
||||||
message.Add("channel_type", channel.Type)
|
message.Add("channel_type", channel.Type)
|
||||||
message.Add("channel_display_name", channel.DisplayName)
|
message.Add("channel_display_name", channel.DisplayName)
|
||||||
|
message.Add("channel_name", channel.Name)
|
||||||
message.Add("sender_name", senderUsername)
|
message.Add("sender_name", senderUsername)
|
||||||
message.Add("team_id", team.Id)
|
message.Add("team_id", team.Id)
|
||||||
|
|
||||||
|
|||||||
37
api/team.go
37
api/team.go
@@ -32,6 +32,8 @@ func InitTeam() {
|
|||||||
BaseRoutes.Teams.Handle("/get_invite_info", ApiAppHandler(getInviteInfo)).Methods("POST")
|
BaseRoutes.Teams.Handle("/get_invite_info", ApiAppHandler(getInviteInfo)).Methods("POST")
|
||||||
BaseRoutes.Teams.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST")
|
BaseRoutes.Teams.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST")
|
||||||
BaseRoutes.Teams.Handle("/name/{team_name:[A-Za-z0-9\\-]+}", ApiAppHandler(getTeamByName)).Methods("GET")
|
BaseRoutes.Teams.Handle("/name/{team_name:[A-Za-z0-9\\-]+}", ApiAppHandler(getTeamByName)).Methods("GET")
|
||||||
|
BaseRoutes.Teams.Handle("/members", ApiUserRequired(getMyTeamMembers)).Methods("GET")
|
||||||
|
BaseRoutes.Teams.Handle("/unread", ApiUserRequired(getMyTeamsUnread)).Methods("GET")
|
||||||
|
|
||||||
BaseRoutes.NeedTeam.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET")
|
BaseRoutes.NeedTeam.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET")
|
||||||
BaseRoutes.NeedTeam.Handle("/stats", ApiUserRequired(getTeamStats)).Methods("GET")
|
BaseRoutes.NeedTeam.Handle("/stats", ApiUserRequired(getTeamStats)).Methods("GET")
|
||||||
@@ -358,6 +360,11 @@ func LeaveTeam(team *model.Team, user *model.User) *model.AppError {
|
|||||||
return uua.Err
|
return uua.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete the preferences that set the last channel used in the team and other team specific preferences
|
||||||
|
if result := <-Srv.Store.Preference().DeleteCategory(user.Id, team.Id); result.Err != nil {
|
||||||
|
return result.Err
|
||||||
|
}
|
||||||
|
|
||||||
RemoveAllSessionsForUserId(user.Id)
|
RemoveAllSessionsForUserId(user.Id)
|
||||||
InvalidateCacheForUser(user.Id)
|
InvalidateCacheForUser(user.Id)
|
||||||
|
|
||||||
@@ -723,6 +730,32 @@ func getTeamByName(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMyTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if len(c.Session.TeamMembers) > 0 {
|
||||||
|
w.Write([]byte(model.TeamMembersToJson(c.Session.TeamMembers)))
|
||||||
|
} else {
|
||||||
|
if result := <-Srv.Store.Team().GetTeamsForUser(c.Session.UserId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
data := result.Data.([]*model.TeamMember)
|
||||||
|
w.Write([]byte(model.TeamMembersToJson(data)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMyTeamsUnread(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
teamId := r.URL.Query().Get("id")
|
||||||
|
|
||||||
|
if result := <-Srv.Store.Team().GetTeamsUnreadForUser(teamId, c.Session.UserId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
data := result.Data.([]*model.TeamUnread)
|
||||||
|
w.Write([]byte(model.TeamsUnreadToJson(data)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func InviteMembers(team *model.Team, senderName string, invites []string) {
|
func InviteMembers(team *model.Team, senderName string, invites []string) {
|
||||||
for _, invite := range invites {
|
for _, invite := range invites {
|
||||||
if len(invite) > 0 {
|
if len(invite) > 0 {
|
||||||
@@ -801,6 +834,10 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
oldTeam.Sanitize()
|
oldTeam.Sanitize()
|
||||||
|
|
||||||
|
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_UPDATE_TEAM, "", "", "", nil)
|
||||||
|
message.Add("team", oldTeam.ToJson())
|
||||||
|
go Publish(message)
|
||||||
|
|
||||||
w.Write([]byte(oldTeam.ToJson()))
|
w.Write([]byte(oldTeam.ToJson()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -576,6 +576,41 @@ func TestGetTeamMembers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMyTeamMembers(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
|
||||||
|
if result, err := th.BasicClient.GetMyTeamMembers(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
members := result.Data.([]*model.TeamMember)
|
||||||
|
if len(members) == 0 {
|
||||||
|
t.Fatal("should have results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMyTeamsUnread(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
|
||||||
|
if result, err := th.BasicClient.GetMyTeamsUnread(""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
members := result.Data.([]*model.TeamUnread)
|
||||||
|
if len(members) == 0 {
|
||||||
|
t.Fatal("should have results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, err := th.BasicClient.GetMyTeamsUnread(th.BasicTeam.Id); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
members := result.Data.([]*model.TeamUnread)
|
||||||
|
if len(members) != 0 {
|
||||||
|
t.Fatal("should not have results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetTeamMember(t *testing.T) {
|
func TestGetTeamMember(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
|
|
||||||
|
|||||||
@@ -4863,6 +4863,10 @@
|
|||||||
"id": "store.sql_team.get_teams_for_email.app_error",
|
"id": "store.sql_team.get_teams_for_email.app_error",
|
||||||
"translation": "We encountered a problem when looking up teams"
|
"translation": "We encountered a problem when looking up teams"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_team.get_unread.app_error",
|
||||||
|
"translation": "We couldn't get the teams unread messages"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "store.sql_team.permanent_delete.app_error",
|
"id": "store.sql_team.permanent_delete.app_error",
|
||||||
"translation": "We couldn't delete the existing team"
|
"translation": "We couldn't delete the existing team"
|
||||||
|
|||||||
@@ -1752,6 +1752,36 @@ func (c *Client) GetTeamMembers(teamId string, offset int, limit int) (*Result,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMyTeamMembers will return an array with team member objects that the current user
|
||||||
|
// is a member of. Must be authenticated.
|
||||||
|
func (c *Client) GetMyTeamMembers() (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiGet("/teams/members", "", ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyTeamsUnread will return an array with TeamUnread objects that contain the amount of
|
||||||
|
// unread messages and mentions the current user has for the teams it belongs to.
|
||||||
|
// An optional team ID can be set to exclude that team from the results. Must be authenticated.
|
||||||
|
func (c *Client) GetMyTeamsUnread(teamId string) (*Result, *AppError) {
|
||||||
|
endpoint := "/teams/unread"
|
||||||
|
|
||||||
|
if teamId != "" {
|
||||||
|
endpoint += fmt.Sprintf("?id=%s", url.QueryEscape(teamId))
|
||||||
|
}
|
||||||
|
if r, err := c.DoApiGet(endpoint, "", ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), TeamsUnreadFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetTeamMember will return a team member object based on the team id and user id provided.
|
// GetTeamMember will return a team member object based on the team id and user id provided.
|
||||||
// Must be authenticated.
|
// Must be authenticated.
|
||||||
func (c *Client) GetTeamMember(teamId string, userId string) (*Result, *AppError) {
|
func (c *Client) GetTeamMember(teamId string, userId string) (*Result, *AppError) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const (
|
|||||||
|
|
||||||
PREFERENCE_CATEGORY_LAST = "last"
|
PREFERENCE_CATEGORY_LAST = "last"
|
||||||
PREFERENCE_NAME_LAST_CHANNEL = "channel"
|
PREFERENCE_NAME_LAST_CHANNEL = "channel"
|
||||||
|
PREFERENCE_NAME_LAST_TEAM = "team"
|
||||||
|
|
||||||
PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
|
PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
|
||||||
PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval"
|
PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval"
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ type TeamMember struct {
|
|||||||
DeleteAt int64 `json:"delete_at"`
|
DeleteAt int64 `json:"delete_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TeamUnread struct {
|
||||||
|
TeamId string `json:"team_id"`
|
||||||
|
MsgCount int64 `json:"msg_count"`
|
||||||
|
MentionCount int64 `json:"mention_count"`
|
||||||
|
}
|
||||||
|
|
||||||
func (o *TeamMember) ToJson() string {
|
func (o *TeamMember) ToJson() string {
|
||||||
b, err := json.Marshal(o)
|
b, err := json.Marshal(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -55,6 +61,25 @@ func TeamMembersFromJson(data io.Reader) []*TeamMember {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TeamsUnreadToJson(o []*TeamUnread) string {
|
||||||
|
if b, err := json.Marshal(o); err != nil {
|
||||||
|
return "[]"
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TeamsUnreadFromJson(data io.Reader) []*TeamUnread {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var o []*TeamUnread
|
||||||
|
err := decoder.Decode(&o)
|
||||||
|
if err == nil {
|
||||||
|
return o
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (o *TeamMember) IsValid() *AppError {
|
func (o *TeamMember) IsValid() *AppError {
|
||||||
|
|
||||||
if len(o.TeamId) != 26 {
|
if len(o.TeamId) != 26 {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const (
|
|||||||
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
|
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
|
||||||
WEBSOCKET_EVENT_NEW_USER = "new_user"
|
WEBSOCKET_EVENT_NEW_USER = "new_user"
|
||||||
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
|
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
|
||||||
|
WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
|
||||||
WEBSOCKET_EVENT_USER_ADDED = "user_added"
|
WEBSOCKET_EVENT_USER_ADDED = "user_added"
|
||||||
WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
|
WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
|
||||||
WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
|
WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
|
||||||
|
|||||||
@@ -326,3 +326,25 @@ func (s SqlPreferenceStore) Delete(userId, category, name string) StoreChannel {
|
|||||||
|
|
||||||
return storeChannel
|
return storeChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SqlPreferenceStore) DeleteCategory(userId string, category string) StoreChannel {
|
||||||
|
storeChannel := make(StoreChannel, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if _, err := s.GetMaster().Exec(
|
||||||
|
`DELETE FROM
|
||||||
|
Preferences
|
||||||
|
WHERE
|
||||||
|
UserId = :UserId
|
||||||
|
AND Category = :Category`, map[string]interface{}{"UserId": userId, "Category": category}); err != nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlPreferenceStore.DeleteCategory", "store.sql_preference.delete.app_error", nil, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|||||||
@@ -392,3 +392,38 @@ func TestPreferenceDelete(t *testing.T) {
|
|||||||
t.Fatal("should've returned no preferences")
|
t.Fatal("should've returned no preferences")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPreferenceDeleteCategory(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
category := model.NewId()
|
||||||
|
userId := model.NewId()
|
||||||
|
|
||||||
|
preference1 := model.Preference{
|
||||||
|
UserId: userId,
|
||||||
|
Category: category,
|
||||||
|
Name: model.NewId(),
|
||||||
|
Value: "value1a",
|
||||||
|
}
|
||||||
|
|
||||||
|
preference2 := model.Preference{
|
||||||
|
UserId: userId,
|
||||||
|
Category: category,
|
||||||
|
Name: model.NewId(),
|
||||||
|
Value: "value1a",
|
||||||
|
}
|
||||||
|
|
||||||
|
Must(store.Preference().Save(&model.Preferences{preference1, preference2}))
|
||||||
|
|
||||||
|
if prefs := Must(store.Preference().GetAll(userId)).(model.Preferences); len([]model.Preference(prefs)) != 2 {
|
||||||
|
t.Fatal("should've returned 2 preferences")
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-store.Preference().DeleteCategory(userId, category); result.Err != nil {
|
||||||
|
t.Fatal(result.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefs := Must(store.Preference().GetAll(userId)).(model.Preferences); len([]model.Preference(prefs)) != 0 {
|
||||||
|
t.Fatal("should've returned no preferences")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -583,6 +583,39 @@ func (s SqlTeamStore) GetTeamsForUser(userId string) StoreChannel {
|
|||||||
return storeChannel
|
return storeChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SqlTeamStore) GetTeamsUnreadForUser(teamId, userId string) StoreChannel {
|
||||||
|
storeChannel := make(StoreChannel, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
var members []*model.TeamUnread
|
||||||
|
_, err := s.GetReplica().Select(&members,
|
||||||
|
`SELECT
|
||||||
|
Channels.TeamId,
|
||||||
|
SUM(Channels.TotalMsgCount - ChannelMembers.MsgCount) as MsgCount,
|
||||||
|
SUM(ChannelMembers.MentionCount) as MentionCount
|
||||||
|
FROM
|
||||||
|
Channels,
|
||||||
|
ChannelMembers
|
||||||
|
WHERE
|
||||||
|
Channels.Id = ChannelMembers.ChannelId AND
|
||||||
|
ChannelMembers.UserId = :UserId AND Channels.TeamId != :TeamId
|
||||||
|
GROUP BY
|
||||||
|
Channels.TeamId`, map[string]interface{}{"UserId": userId, "TeamId": teamId})
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlTeamStore.GetTeamsUnreadForUser", "store.sql_team.get_unread.app_error", nil, "userId="+userId+" "+err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = members
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
func (s SqlTeamStore) RemoveMember(teamId string, userId string) StoreChannel {
|
func (s SqlTeamStore) RemoveMember(teamId string, userId string) StoreChannel {
|
||||||
storeChannel := make(StoreChannel, 1)
|
storeChannel := make(StoreChannel, 1)
|
||||||
|
|
||||||
|
|||||||
@@ -529,3 +529,50 @@ func TestTeamStoreMemberCount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMyTeamMembersUnread(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
teamId1 := model.NewId()
|
||||||
|
teamId2 := model.NewId()
|
||||||
|
|
||||||
|
uid := model.NewId()
|
||||||
|
m1 := &model.TeamMember{TeamId: teamId1, UserId: uid}
|
||||||
|
m2 := &model.TeamMember{TeamId: teamId2, UserId: uid}
|
||||||
|
Must(store.Team().SaveMember(m1))
|
||||||
|
Must(store.Team().SaveMember(m2))
|
||||||
|
|
||||||
|
c1 := &model.Channel{TeamId: m1.TeamId, Name: model.NewId(), DisplayName: "Town Square", Type: model.CHANNEL_OPEN}
|
||||||
|
Must(store.Channel().Save(c1))
|
||||||
|
c2 := &model.Channel{TeamId: m2.TeamId, Name: model.NewId(), DisplayName: "Town Square", Type: model.CHANNEL_OPEN}
|
||||||
|
Must(store.Channel().Save(c2))
|
||||||
|
|
||||||
|
cm1 := &model.ChannelMember{ChannelId: c1.Id, UserId: m1.UserId, NotifyProps: model.GetDefaultChannelNotifyProps()}
|
||||||
|
Must(store.Channel().SaveMember(cm1))
|
||||||
|
cm2 := &model.ChannelMember{ChannelId: c2.Id, UserId: m2.UserId, NotifyProps: model.GetDefaultChannelNotifyProps()}
|
||||||
|
Must(store.Channel().SaveMember(cm2))
|
||||||
|
|
||||||
|
if r1 := <-store.Team().GetTeamsUnreadForUser("", uid); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
} else {
|
||||||
|
ms := r1.Data.([]*model.TeamUnread)
|
||||||
|
|
||||||
|
if len(ms) != 2 {
|
||||||
|
t.Fatal("Should be the unreads for all the teams")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r2 := <-store.Team().GetTeamsUnreadForUser(teamId1, uid); r2.Err != nil {
|
||||||
|
t.Fatal(r2.Err)
|
||||||
|
} else {
|
||||||
|
ms := r2.Data.([]*model.TeamUnread)
|
||||||
|
|
||||||
|
if len(ms) != 1 {
|
||||||
|
t.Fatal("Should be the unreads for just one team")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r1 := <-store.Team().RemoveAllMembersByUser(uid); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ type TeamStore interface {
|
|||||||
GetTotalMemberCount(teamId string) StoreChannel
|
GetTotalMemberCount(teamId string) StoreChannel
|
||||||
GetActiveMemberCount(teamId string) StoreChannel
|
GetActiveMemberCount(teamId string) StoreChannel
|
||||||
GetTeamsForUser(userId string) StoreChannel
|
GetTeamsForUser(userId string) StoreChannel
|
||||||
|
GetTeamsUnreadForUser(teamId, userId string) StoreChannel
|
||||||
RemoveMember(teamId string, userId string) StoreChannel
|
RemoveMember(teamId string, userId string) StoreChannel
|
||||||
RemoveAllMembersByTeam(teamId string) StoreChannel
|
RemoveAllMembersByTeam(teamId string) StoreChannel
|
||||||
RemoveAllMembersByUser(userId string) StoreChannel
|
RemoveAllMembersByUser(userId string) StoreChannel
|
||||||
@@ -270,6 +271,7 @@ type PreferenceStore interface {
|
|||||||
GetCategory(userId string, category string) StoreChannel
|
GetCategory(userId string, category string) StoreChannel
|
||||||
GetAll(userId string) StoreChannel
|
GetAll(userId string) StoreChannel
|
||||||
Delete(userId, category, name string) StoreChannel
|
Delete(userId, category, name string) StoreChannel
|
||||||
|
DeleteCategory(userId string, category string) StoreChannel
|
||||||
PermanentDeleteByUser(userId string) StoreChannel
|
PermanentDeleteByUser(userId string) StoreChannel
|
||||||
IsFeatureEnabled(feature, userId string) StoreChannel
|
IsFeatureEnabled(feature, userId string) StoreChannel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export function emitChannelClickEvent(channel) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
function switchToChannel(chan) {
|
function switchToChannel(chan) {
|
||||||
|
const channelMember = ChannelStore.getMyMember(chan.id);
|
||||||
const getMyChannelMembersPromise = AsyncClient.getChannelMember(chan.id, UserStore.getCurrentId());
|
const getMyChannelMembersPromise = AsyncClient.getChannelMember(chan.id, UserStore.getCurrentId());
|
||||||
|
|
||||||
getMyChannelMembersPromise.then(() => {
|
getMyChannelMembersPromise.then(() => {
|
||||||
@@ -56,6 +57,9 @@ export function emitChannelClickEvent(channel) {
|
|||||||
type: ActionTypes.CLICK_CHANNEL,
|
type: ActionTypes.CLICK_CHANNEL,
|
||||||
name: chan.name,
|
name: chan.name,
|
||||||
id: chan.id,
|
id: chan.id,
|
||||||
|
team_id: chan.team_id,
|
||||||
|
total_msg_count: chan.total_msg_count,
|
||||||
|
channelMember,
|
||||||
prev: ChannelStore.getCurrentId()
|
prev: ChannelStore.getCurrentId()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -443,7 +447,7 @@ export function viewLoggedIn() {
|
|||||||
PostStore.clearPendingPosts();
|
PostStore.clearPendingPosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastTimeTypingSent = 0;
|
let lastTimeTypingSent = 0;
|
||||||
export function emitLocalUserTypingEvent(channelId, parentId) {
|
export function emitLocalUserTypingEvent(channelId, parentId) {
|
||||||
const t = Date.now();
|
const t = Date.now();
|
||||||
if ((t - lastTimeTypingSent) > Constants.UPDATE_TYPING_MS) {
|
if ((t - lastTimeTypingSent) > Constants.UPDATE_TYPING_MS) {
|
||||||
@@ -534,3 +538,35 @@ export function emitBrowserFocus(focus) {
|
|||||||
focus
|
focus
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function redirectUserToDefaultTeam() {
|
||||||
|
const teams = TeamStore.getAll();
|
||||||
|
const teamMembers = TeamStore.getMyTeamMembers();
|
||||||
|
let teamId = PreferenceStore.get('last', 'team');
|
||||||
|
|
||||||
|
if (!teams[teamId] && teamMembers.length > 0) {
|
||||||
|
let myTeams = [];
|
||||||
|
for (const index in teamMembers) {
|
||||||
|
if (teamMembers.hasOwnProperty(index)) {
|
||||||
|
const teamMember = teamMembers[index];
|
||||||
|
myTeams.push(teams[teamMember.team_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myTeams.length > 0) {
|
||||||
|
myTeams = myTeams.sort((a, b) => a.display_name.localeCompare(b.display_name));
|
||||||
|
teamId = myTeams[0].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (teams[teamId]) {
|
||||||
|
const channelId = PreferenceStore.get(teamId, 'channel');
|
||||||
|
let channel = ChannelStore.getChannelById(channelId);
|
||||||
|
if (!channel) {
|
||||||
|
channel = 'town-square';
|
||||||
|
}
|
||||||
|
browserHistory.push(`/${teams[teamId].name}/channels/${channel}`);
|
||||||
|
} else {
|
||||||
|
browserHistory.push('/select_team');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,23 +18,30 @@ const ActionTypes = Constants.ActionTypes;
|
|||||||
const Preferences = Constants.Preferences;
|
const Preferences = Constants.Preferences;
|
||||||
|
|
||||||
export function handleNewPost(post, msg) {
|
export function handleNewPost(post, msg) {
|
||||||
|
const teamId = TeamStore.getCurrentId();
|
||||||
|
|
||||||
if (ChannelStore.getCurrentId() === post.channel_id) {
|
if (ChannelStore.getCurrentId() === post.channel_id) {
|
||||||
if (window.isActive) {
|
if (window.isActive) {
|
||||||
AsyncClient.updateLastViewedAt(null, false);
|
AsyncClient.updateLastViewedAt(null, false);
|
||||||
} else {
|
} else {
|
||||||
AsyncClient.getChannel(post.channel_id);
|
AsyncClient.getChannel(post.channel_id);
|
||||||
}
|
}
|
||||||
} else if (msg && (TeamStore.getCurrentId() === msg.data.team_id || msg.data.channel_type === Constants.DM_CHANNEL)) {
|
} else if (msg && (teamId === msg.data.team_id || msg.data.channel_type === Constants.DM_CHANNEL)) {
|
||||||
if (Client.teamId) {
|
if (Client.teamId) {
|
||||||
AsyncClient.getChannel(post.channel_id);
|
AsyncClient.getChannel(post.channel_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var websocketMessageProps = null;
|
let websocketMessageProps = null;
|
||||||
if (msg) {
|
if (msg) {
|
||||||
websocketMessageProps = msg.data;
|
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) {
|
if (post.root_id && PostStore.getPost(post.channel_id, post.root_id) == null) {
|
||||||
Client.getPost(
|
Client.getPost(
|
||||||
post.channel_id,
|
post.channel_id,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import $ from 'jquery';
|
|||||||
import UserStore from 'stores/user_store.jsx';
|
import UserStore from 'stores/user_store.jsx';
|
||||||
import TeamStore from 'stores/team_store.jsx';
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
import PostStore from 'stores/post_store.jsx';
|
import PostStore from 'stores/post_store.jsx';
|
||||||
|
import PreferenceStore from 'stores/preference_store.jsx';
|
||||||
import ChannelStore from 'stores/channel_store.jsx';
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
import BrowserStore from 'stores/browser_store.jsx';
|
import BrowserStore from 'stores/browser_store.jsx';
|
||||||
import ErrorStore from 'stores/error_store.jsx';
|
import ErrorStore from 'stores/error_store.jsx';
|
||||||
@@ -122,6 +123,10 @@ function handleEvent(msg) {
|
|||||||
handleLeaveTeamEvent(msg);
|
handleLeaveTeamEvent(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SocketEvents.UPDATE_TEAM:
|
||||||
|
handleUpdateTeamEvent(msg);
|
||||||
|
break;
|
||||||
|
|
||||||
case SocketEvents.USER_ADDED:
|
case SocketEvents.USER_ADDED:
|
||||||
handleUserAddedEvent(msg);
|
handleUserAddedEvent(msg);
|
||||||
break;
|
break;
|
||||||
@@ -229,21 +234,37 @@ function handleNewUserEvent(msg) {
|
|||||||
AsyncClient.getUser(msg.data.user_id);
|
AsyncClient.getUser(msg.data.user_id);
|
||||||
AsyncClient.getChannelStats();
|
AsyncClient.getChannelStats();
|
||||||
loadProfilesAndTeamMembersForDMSidebar();
|
loadProfilesAndTeamMembersForDMSidebar();
|
||||||
|
|
||||||
|
if (msg.data.user_id === UserStore.getCurrentId()) {
|
||||||
|
AsyncClient.getMyTeamMembers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLeaveTeamEvent(msg) {
|
function handleLeaveTeamEvent(msg) {
|
||||||
if (UserStore.getCurrentId() === msg.data.user_id) {
|
if (UserStore.getCurrentId() === msg.data.user_id) {
|
||||||
TeamStore.removeMyTeamMember(msg.data.team_id);
|
TeamStore.removeMyTeamMember(msg.data.team_id);
|
||||||
|
|
||||||
// if they are on the team being removed redirect them to the root
|
// if they are on the team being removed redirect them to default team
|
||||||
if (TeamStore.getCurrentId() === msg.data.team_id) {
|
if (TeamStore.getCurrentId() === msg.data.team_id) {
|
||||||
TeamStore.setCurrentId('');
|
TeamStore.setCurrentId('');
|
||||||
Client.setTeamId('');
|
Client.setTeamId('');
|
||||||
browserHistory.push('/');
|
PreferenceStore.deletePreference({
|
||||||
|
category: 'last',
|
||||||
|
name: 'team',
|
||||||
|
value: msg.data.team_id
|
||||||
|
});
|
||||||
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
UserStore.removeProfileFromTeam(msg.data.team_id, msg.data.user_id);
|
||||||
|
TeamStore.removeMemberInTeam(msg.data.team_id, msg.data.user_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleUpdateTeamEvent(msg) {
|
||||||
|
TeamStore.updateTeam(msg.data.team);
|
||||||
|
}
|
||||||
|
|
||||||
function handleDirectAddedEvent(msg) {
|
function handleDirectAddedEvent(msg) {
|
||||||
AsyncClient.getChannel(msg.broadcast.channel_id);
|
AsyncClient.getChannel(msg.broadcast.channel_id);
|
||||||
loadProfilesAndTeamMembersForDMSidebar();
|
loadProfilesAndTeamMembersForDMSidebar();
|
||||||
|
|||||||
@@ -598,6 +598,30 @@ export default class Client {
|
|||||||
end(this.handleResponse.bind(this, 'getTeamMember', success, error));
|
end(this.handleResponse.bind(this, 'getTeamMember', success, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMyTeamMembers(success, error) {
|
||||||
|
request.
|
||||||
|
get(`${this.getTeamsRoute()}/members`).
|
||||||
|
set(this.defaultHeaders).
|
||||||
|
type('application/json').
|
||||||
|
accept('application/json').
|
||||||
|
end(this.handleResponse.bind(this, 'getMyTeamMembers', success, error));
|
||||||
|
}
|
||||||
|
|
||||||
|
getMyTeamsUnread(teamId, success, error) {
|
||||||
|
let url = `${this.getTeamsRoute()}/unread`;
|
||||||
|
|
||||||
|
if (teamId) {
|
||||||
|
url += `?id=${encodeURIComponent(teamId)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.
|
||||||
|
get(url).
|
||||||
|
set(this.defaultHeaders).
|
||||||
|
type('application/json').
|
||||||
|
accept('application/json').
|
||||||
|
end(this.handleResponse.bind(this, 'getMyTeamsUnread', success, error));
|
||||||
|
}
|
||||||
|
|
||||||
getTeamMembersByIds(teamId, userIds, success, error) {
|
getTeamMembersByIds(teamId, userIds, success, error) {
|
||||||
request.
|
request.
|
||||||
post(`${this.getTeamNeededRoute(teamId)}/members/ids`).
|
post(`${this.getTeamNeededRoute(teamId)}/members/ids`).
|
||||||
|
|||||||
@@ -50,13 +50,14 @@ export default class AdminNavbarDropdown extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var teams = [];
|
const teams = [];
|
||||||
|
let switchTeams;
|
||||||
|
|
||||||
if (this.state.teamMembers && this.state.teamMembers.length > 0) {
|
if (this.state.teamMembers && this.state.teamMembers.length > 0) {
|
||||||
for (var index in this.state.teamMembers) {
|
for (const index in this.state.teamMembers) {
|
||||||
if (this.state.teamMembers.hasOwnProperty(index)) {
|
if (this.state.teamMembers.hasOwnProperty(index)) {
|
||||||
var teamMember = this.state.teamMembers[index];
|
const teamMember = this.state.teamMembers[index];
|
||||||
var team = this.state.teams[teamMember.team_id];
|
const team = this.state.teams[teamMember.team_id];
|
||||||
teams.push(
|
teams.push(
|
||||||
<li key={'team_' + team.name}>
|
<li key={'team_' + team.name}>
|
||||||
<Link
|
<Link
|
||||||
@@ -79,6 +80,20 @@ export default class AdminNavbarDropdown extends React.Component {
|
|||||||
className='divider'
|
className='divider'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
switchTeams = (
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
to={'/select_team'}
|
||||||
|
>
|
||||||
|
<i className='fa fa-exchange'/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.nav.switch'
|
||||||
|
defaultMessage='Team Selection'
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -104,17 +119,7 @@ export default class AdminNavbarDropdown extends React.Component {
|
|||||||
role='menu'
|
role='menu'
|
||||||
>
|
>
|
||||||
{teams}
|
{teams}
|
||||||
<li>
|
{switchTeams}
|
||||||
<Link
|
|
||||||
to={'/select_team'}
|
|
||||||
>
|
|
||||||
<i className='fa fa-exchange'/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='admin.nav.switch'
|
|
||||||
defaultMessage='Team Selection'
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li
|
<li
|
||||||
key='teamDiv'
|
key='teamDiv'
|
||||||
className='divider'
|
className='divider'
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import ErrorBar from 'components/error_bar.jsx';
|
import ErrorBar from 'components/error_bar.jsx';
|
||||||
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
import {browserHistory, Link} from 'react-router/es6';
|
import {browserHistory, Link} from 'react-router/es6';
|
||||||
@@ -47,11 +49,21 @@ export default class CreateTeamController extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let url = '/select_team';
|
||||||
|
const team = TeamStore.getCurrent();
|
||||||
|
const channel = ChannelStore.getCurrent();
|
||||||
|
if (team) {
|
||||||
|
url = `/${team.name}`;
|
||||||
|
if (channel) {
|
||||||
|
url += `/channels/${channel.name}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ErrorBar/>
|
<ErrorBar/>
|
||||||
<div className='signup-header'>
|
<div className='signup-header'>
|
||||||
<Link to='/select_team'>
|
<Link to={url}>
|
||||||
<span className='fa fa-chevron-left'/>
|
<span className='fa fa-chevron-left'/>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='web.header.back'
|
id='web.header.back'
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default class LoginController extends React.Component {
|
|||||||
document.title = global.window.mm_config.SiteName;
|
document.title = global.window.mm_config.SiteName;
|
||||||
|
|
||||||
if (UserStore.getCurrentUser()) {
|
if (UserStore.getCurrentUser()) {
|
||||||
browserHistory.push('/select_team');
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncClient.checkVersion();
|
AsyncClient.checkVersion();
|
||||||
@@ -205,7 +205,7 @@ export default class LoginController extends React.Component {
|
|||||||
} else if (team) {
|
} else if (team) {
|
||||||
browserHistory.push(`/${team.name}`);
|
browserHistory.push(`/${team.name}`);
|
||||||
} else {
|
} else {
|
||||||
browserHistory.push('/select_team');
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ export default class Navbar extends React.Component {
|
|||||||
if (e.target.className !== 'navbar-toggle' && e.target.className !== 'icon-bar') {
|
if (e.target.className !== 'navbar-toggle' && e.target.className !== 'icon-bar') {
|
||||||
$('.app__body .inner-wrap').removeClass('move--right move--left move--left-small');
|
$('.app__body .inner-wrap').removeClass('move--right move--left move--left-small');
|
||||||
$('.app__body .sidebar--left').removeClass('move--right');
|
$('.app__body .sidebar--left').removeClass('move--right');
|
||||||
|
$('.multi-teams .team-sidebar').removeClass('move--right');
|
||||||
$('.app__body .sidebar--right').removeClass('move--left');
|
$('.app__body .sidebar--right').removeClass('move--left');
|
||||||
$('.app__body .sidebar--menu').removeClass('move--left');
|
$('.app__body .sidebar--menu').removeClass('move--left');
|
||||||
}
|
}
|
||||||
@@ -155,6 +156,7 @@ export default class Navbar extends React.Component {
|
|||||||
toggleLeftSidebar() {
|
toggleLeftSidebar() {
|
||||||
$('.app__body .inner-wrap').toggleClass('move--right');
|
$('.app__body .inner-wrap').toggleClass('move--right');
|
||||||
$('.app__body .sidebar--left').toggleClass('move--right');
|
$('.app__body .sidebar--left').toggleClass('move--right');
|
||||||
|
$('.multi-teams .team-sidebar').toggleClass('move--right');
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleRightSidebar() {
|
toggleRightSidebar() {
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ export default class NeedsTeam extends React.Component {
|
|||||||
content.push(
|
content.push(
|
||||||
this.props.navbar
|
this.props.navbar
|
||||||
);
|
);
|
||||||
|
content.push(this.props.team_sidebar);
|
||||||
content.push(
|
content.push(
|
||||||
this.props.sidebar
|
this.props.sidebar
|
||||||
);
|
);
|
||||||
@@ -197,6 +198,7 @@ NeedsTeam.propTypes = {
|
|||||||
]),
|
]),
|
||||||
navbar: React.PropTypes.element,
|
navbar: React.PropTypes.element,
|
||||||
sidebar: React.PropTypes.element,
|
sidebar: React.PropTypes.element,
|
||||||
|
team_sidebar: React.PropTypes.element,
|
||||||
center: React.PropTypes.element,
|
center: React.PropTypes.element,
|
||||||
params: React.PropTypes.object,
|
params: React.PropTypes.object,
|
||||||
user: React.PropTypes.object
|
user: React.PropTypes.object
|
||||||
|
|||||||
@@ -3,14 +3,22 @@
|
|||||||
|
|
||||||
import * as utils from 'utils/utils.jsx';
|
import * as utils from 'utils/utils.jsx';
|
||||||
import ChannelStore from 'stores/channel_store.jsx';
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
|
|
||||||
function getCountsStateFromStores() {
|
function getCountsStateFromStores() {
|
||||||
var count = 0;
|
let count = 0;
|
||||||
var channels = ChannelStore.getAll();
|
const teamMembers = TeamStore.getMyTeamMembers();
|
||||||
var members = ChannelStore.getMyMembers();
|
const channels = ChannelStore.getAll();
|
||||||
|
const members = ChannelStore.getMyMembers();
|
||||||
|
|
||||||
|
teamMembers.forEach((member) => {
|
||||||
|
if (member.team_id !== TeamStore.getCurrentId()) {
|
||||||
|
count += ((member.msg_count || 0) + (member.mention_count || 0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
channels.forEach((channel) => {
|
channels.forEach((channel) => {
|
||||||
var channelMember = members[channel.id];
|
const channelMember = members[channel.id];
|
||||||
if (channelMember == null) {
|
if (channelMember == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -41,10 +49,12 @@ export default class NotifyCounts extends React.Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
ChannelStore.addChangeListener(this.onListenerChange);
|
ChannelStore.addChangeListener(this.onListenerChange);
|
||||||
|
TeamStore.addChangeListener(this.onListenerChange);
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
ChannelStore.removeChangeListener(this.onListenerChange);
|
ChannelStore.removeChangeListener(this.onListenerChange);
|
||||||
|
TeamStore.removeChangeListener(this.onListenerChange);
|
||||||
}
|
}
|
||||||
onListenerChange() {
|
onListenerChange() {
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default class Root extends React.Component {
|
|||||||
if (UserStore.getNoAccounts()) {
|
if (UserStore.getNoAccounts()) {
|
||||||
browserHistory.push('/signup_user_complete');
|
browserHistory.push('/signup_user_complete');
|
||||||
} else if (UserStore.getCurrentUser()) {
|
} else if (UserStore.getCurrentUser()) {
|
||||||
browserHistory.push('/select_team');
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
} else {
|
} else {
|
||||||
browserHistory.push('/login');
|
browserHistory.push('/login');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export default class SelectTeam extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.onTeamChange = this.onTeamChange.bind(this);
|
this.onTeamChange = this.onTeamChange.bind(this);
|
||||||
this.handleTeamClick = this.handleTeamClick.bind(this);
|
this.handleTeamClick = this.handleTeamClick.bind(this);
|
||||||
|
this.teamContentsCompare = this.teamContentsCompare.bind(this);
|
||||||
|
|
||||||
const state = this.getStateFromStores(false);
|
const state = this.getStateFromStores(false);
|
||||||
state.loadingTeamId = '';
|
state.loadingTeamId = '';
|
||||||
@@ -61,33 +62,15 @@ export default class SelectTeam extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let content = null;
|
let openTeamContents = [];
|
||||||
let teamContents = [];
|
|
||||||
const isAlreadyMember = new Map();
|
const isAlreadyMember = new Map();
|
||||||
const isSystemAdmin = Utils.isSystemAdmin(UserStore.getCurrentUser().roles);
|
const isSystemAdmin = Utils.isSystemAdmin(UserStore.getCurrentUser().roles);
|
||||||
let teamMembersCount = 0;
|
|
||||||
|
|
||||||
for (const teamMember of this.state.teamMembers) {
|
for (const teamMember of this.state.teamMembers) {
|
||||||
const teamId = teamMember.team_id;
|
const teamId = teamMember.team_id;
|
||||||
const team = this.state.teams[teamId];
|
|
||||||
isAlreadyMember[teamId] = true;
|
isAlreadyMember[teamId] = true;
|
||||||
teamMembersCount++;
|
|
||||||
|
|
||||||
teamContents.push(
|
|
||||||
<SelectTeamItem
|
|
||||||
key={'team_' + team.name}
|
|
||||||
team={team}
|
|
||||||
url={'/' + team.name + '/channels/town-square'}
|
|
||||||
onTeamClick={this.handleTeamClick}
|
|
||||||
loading={this.state.loadingTeamId === teamId}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
teamContents = teamContents.sort(this.teamContentsCompare);
|
|
||||||
|
|
||||||
var openTeamContents = [];
|
|
||||||
|
|
||||||
for (const id in this.state.teamListings) {
|
for (const id in this.state.teamListings) {
|
||||||
if (this.state.teamListings.hasOwnProperty(id) && !isAlreadyMember[id]) {
|
if (this.state.teamListings.hasOwnProperty(id) && !isAlreadyMember[id]) {
|
||||||
const openTeam = this.state.teamListings[id];
|
const openTeam = this.state.teamListings[id];
|
||||||
@@ -103,8 +86,8 @@ export default class SelectTeam extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teamMembersCount === 0 && teamContents.length === 0 && openTeamContents.length === 0 && (global.window.mm_config.EnableTeamCreation === 'true' || isSystemAdmin)) {
|
if (openTeamContents.length === 0 && (global.window.mm_config.EnableTeamCreation === 'true' || isSystemAdmin)) {
|
||||||
teamContents = (
|
openTeamContents = (
|
||||||
<div className='signup-team-dir-err'>
|
<div className='signup-team-dir-err'>
|
||||||
<div>
|
<div>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -114,8 +97,8 @@ export default class SelectTeam extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (teamMembersCount === 0 && teamContents.length === 0 && openTeamContents.length === 0) {
|
} else if (openTeamContents.length === 0) {
|
||||||
teamContents = (
|
openTeamContents = (
|
||||||
<div className='signup-team-dir-err'>
|
<div className='signup-team-dir-err'>
|
||||||
<div>
|
<div>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
@@ -125,42 +108,25 @@ export default class SelectTeam extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (teamContents.length === 0 && openTeamContents.length > 0) {
|
|
||||||
teamContents = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teamContents) {
|
if (Array.isArray(openTeamContents)) {
|
||||||
content = (
|
openTeamContents = openTeamContents.sort(this.teamContentsCompare);
|
||||||
<div className='signup__content'>
|
|
||||||
<h4>
|
|
||||||
<FormattedMessage
|
|
||||||
id='signup_team.choose'
|
|
||||||
defaultMessage='Your teams:'
|
|
||||||
/>
|
|
||||||
</h4>
|
|
||||||
<div className='signup-team-all'>
|
|
||||||
{teamContents}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var openContent;
|
let openContent = (
|
||||||
if (openTeamContents.length > 0) {
|
<div className='signup__content'>
|
||||||
openContent = (
|
<h4>
|
||||||
<div className='signup__content'>
|
<FormattedMessage
|
||||||
<h4>
|
id='signup_team.join_open'
|
||||||
<FormattedMessage
|
defaultMessage='Teams you can join: '
|
||||||
id='signup_team.join_open'
|
/>
|
||||||
defaultMessage='Teams you can join: '
|
</h4>
|
||||||
/>
|
<div className='signup-team-all'>
|
||||||
</h4>
|
{openTeamContents}
|
||||||
<div className='signup-team-all'>
|
|
||||||
{openTeamContents.sort(this.teamContentsCompare)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
}
|
);
|
||||||
|
|
||||||
if (!this.state.loaded) {
|
if (!this.state.loaded) {
|
||||||
openContent = <LoadingScreen/>;
|
openContent = <LoadingScreen/>;
|
||||||
@@ -177,7 +143,7 @@ export default class SelectTeam extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let teamSignUp;
|
let teamSignUp;
|
||||||
if (isSystemAdmin || (global.window.mm_config.EnableTeamCreation === 'true' && !UserAgent.isMobileApp())) {
|
if (isSystemAdmin || global.window.mm_config.EnableTeamCreation === 'true') {
|
||||||
teamSignUp = (
|
teamSignUp = (
|
||||||
<div className='margin--extra'>
|
<div className='margin--extra'>
|
||||||
<Link
|
<Link
|
||||||
@@ -197,7 +163,7 @@ export default class SelectTeam extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let adminConsoleLink;
|
let adminConsoleLink;
|
||||||
if (isSystemAdmin) {
|
if (isSystemAdmin && !UserAgent.isMobileApp()) {
|
||||||
adminConsoleLink = (
|
adminConsoleLink = (
|
||||||
<div className='margin--extra hidden-xs'>
|
<div className='margin--extra hidden-xs'>
|
||||||
<Link
|
<Link
|
||||||
@@ -225,19 +191,30 @@ export default class SelectTeam extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let headerButton;
|
||||||
|
if (this.state.teamMembers.length) {
|
||||||
|
headerButton = (
|
||||||
|
<Link to='/'>
|
||||||
|
<span className='fa fa-chevron-left'/>
|
||||||
|
<FormattedMessage id='web.header.back'/>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
headerButton = (
|
||||||
|
<a
|
||||||
|
href='#'
|
||||||
|
onClick={GlobalActions.emitUserLoggedOutEvent}
|
||||||
|
>
|
||||||
|
<span className='fa fa-chevron-left'/>
|
||||||
|
<FormattedMessage id='web.header.logout'/>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ErrorBar/>
|
<ErrorBar/>
|
||||||
<div className='signup-header'>
|
<div className='signup-header'>
|
||||||
<a
|
{headerButton}
|
||||||
href='#'
|
|
||||||
onClick={GlobalActions.emitUserLoggedOutEvent}
|
|
||||||
>
|
|
||||||
<span className='fa fa-chevron-left'/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='navbar_dropdown.logout'
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='col-sm-12'>
|
<div className='col-sm-12'>
|
||||||
<div className={'signup-team__container'}>
|
<div className={'signup-team__container'}>
|
||||||
@@ -249,7 +226,6 @@ export default class SelectTeam extends React.Component {
|
|||||||
<h4 className='color--light'>
|
<h4 className='color--light'>
|
||||||
{description}
|
{description}
|
||||||
</h4>
|
</h4>
|
||||||
{content}
|
|
||||||
{openContent}
|
{openContent}
|
||||||
{teamSignUp}
|
{teamSignUp}
|
||||||
{adminConsoleLink}
|
{adminConsoleLink}
|
||||||
|
|||||||
@@ -82,11 +82,19 @@ export default class Sidebar extends React.Component {
|
|||||||
let msgs = 0;
|
let msgs = 0;
|
||||||
let mentions = 0;
|
let mentions = 0;
|
||||||
const unreadCounts = this.state.unreadCounts;
|
const unreadCounts = this.state.unreadCounts;
|
||||||
|
const teamMembers = this.state.teamMembers;
|
||||||
|
|
||||||
|
teamMembers.forEach((member) => {
|
||||||
|
if (member.team_id !== this.state.currentTeam.id) {
|
||||||
|
msgs += member.msg_count || 0;
|
||||||
|
mentions += member.mention_count || 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Object.keys(unreadCounts).forEach((chId) => {
|
Object.keys(unreadCounts).forEach((chId) => {
|
||||||
const channel = ChannelStore.get(chId);
|
const channel = ChannelStore.get(chId);
|
||||||
|
|
||||||
if (channel && (!channel.team_id || channel.team_id === this.state.currentTeam.id)) {
|
if (channel && (channel.type === 'D' || channel.team_id === this.state.currentTeam.id)) {
|
||||||
msgs += unreadCounts[chId].msgs;
|
msgs += unreadCounts[chId].msgs;
|
||||||
mentions += unreadCounts[chId].mentions;
|
mentions += unreadCounts[chId].mentions;
|
||||||
}
|
}
|
||||||
@@ -97,6 +105,7 @@ export default class Sidebar extends React.Component {
|
|||||||
|
|
||||||
getStateFromStores() {
|
getStateFromStores() {
|
||||||
const members = ChannelStore.getMyMembers();
|
const members = ChannelStore.getMyMembers();
|
||||||
|
const teamMembers = TeamStore.getMyTeamMembers();
|
||||||
const currentChannelId = ChannelStore.getCurrentId();
|
const currentChannelId = ChannelStore.getCurrentId();
|
||||||
const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
|
const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
|
||||||
const channelList = ChannelUtils.buildDisplayableChannelList(Object.assign([], ChannelStore.getAll()));
|
const channelList = ChannelUtils.buildDisplayableChannelList(Object.assign([], ChannelStore.getAll()));
|
||||||
@@ -104,6 +113,7 @@ export default class Sidebar extends React.Component {
|
|||||||
return {
|
return {
|
||||||
activeId: currentChannelId,
|
activeId: currentChannelId,
|
||||||
members,
|
members,
|
||||||
|
teamMembers,
|
||||||
...channelList,
|
...channelList,
|
||||||
unreadCounts: JSON.parse(JSON.stringify(ChannelStore.getUnreadCounts())),
|
unreadCounts: JSON.parse(JSON.stringify(ChannelStore.getUnreadCounts())),
|
||||||
showTutorialTip: tutorialStep === TutorialSteps.CHANNEL_POPOVER,
|
showTutorialTip: tutorialStep === TutorialSteps.CHANNEL_POPOVER,
|
||||||
@@ -153,6 +163,7 @@ export default class Sidebar extends React.Component {
|
|||||||
if (this.state.activeId !== prevState.activeId) {
|
if (this.state.activeId !== prevState.activeId) {
|
||||||
$('.app__body .inner-wrap').removeClass('move--right');
|
$('.app__body .inner-wrap').removeClass('move--right');
|
||||||
$('.app__body .sidebar--left').removeClass('move--right');
|
$('.app__body .sidebar--left').removeClass('move--right');
|
||||||
|
$('.multi-teams .team-sidebar').removeClass('move--right');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +180,7 @@ export default class Sidebar extends React.Component {
|
|||||||
|
|
||||||
onChange() {
|
onChange() {
|
||||||
this.setState(this.getStateFromStores());
|
this.setState(this.getStateFromStores());
|
||||||
|
this.updateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTitle() {
|
updateTitle() {
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
|
this.showGetTeamInviteLinkModal = this.showGetTeamInviteLinkModal.bind(this);
|
||||||
this.showTeamMembersModal = this.showTeamMembersModal.bind(this);
|
this.showTeamMembersModal = this.showTeamMembersModal.bind(this);
|
||||||
this.hideTeamMembersModal = this.hideTeamMembersModal.bind(this);
|
this.hideTeamMembersModal = this.hideTeamMembersModal.bind(this);
|
||||||
this.handleSwitchTeams = this.handleSwitchTeams.bind(this);
|
|
||||||
|
|
||||||
this.onTeamChange = this.onTeamChange.bind(this);
|
this.onTeamChange = this.onTeamChange.bind(this);
|
||||||
this.openAccountSettings = this.openAccountSettings.bind(this);
|
this.openAccountSettings = this.openAccountSettings.bind(this);
|
||||||
@@ -55,8 +54,8 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
this.handleClick = this.handleClick.bind(this);
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
teams: TeamStore.getAll(),
|
|
||||||
teamMembers: TeamStore.getMyTeamMembers(),
|
teamMembers: TeamStore.getMyTeamMembers(),
|
||||||
|
teamListings: TeamStore.getTeamListings(),
|
||||||
showAboutModal: false,
|
showAboutModal: false,
|
||||||
showDropdown: false,
|
showDropdown: false,
|
||||||
showTeamMembersModal: false,
|
showTeamMembersModal: false,
|
||||||
@@ -131,11 +130,6 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSwitchTeams() {
|
|
||||||
// The actual switching of teams is handled by the react-router Link
|
|
||||||
this.setState({showDropdown: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
TeamStore.addChangeListener(this.onTeamChange);
|
TeamStore.addChangeListener(this.onTeamChange);
|
||||||
document.addEventListener('keydown', this.openAccountSettings);
|
document.addEventListener('keydown', this.openAccountSettings);
|
||||||
@@ -143,8 +137,8 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
|
|
||||||
onTeamChange() {
|
onTeamChange() {
|
||||||
this.setState({
|
this.setState({
|
||||||
teams: TeamStore.getAll(),
|
teamMembers: TeamStore.getMyTeamMembers(),
|
||||||
teamMembers: TeamStore.getMyTeamMembers()
|
teamListings: TeamStore.getTeamListings()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,14 +176,14 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const config = global.mm_config;
|
const config = global.mm_config;
|
||||||
var teamLink = '';
|
const currentUser = this.props.currentUser;
|
||||||
var inviteLink = '';
|
let teamLink = '';
|
||||||
var manageLink = '';
|
let inviteLink = '';
|
||||||
var sysAdminLink = '';
|
let manageLink = '';
|
||||||
var currentUser = this.props.currentUser;
|
let sysAdminLink = '';
|
||||||
var isAdmin = false;
|
let isAdmin = false;
|
||||||
var isSystemAdmin = false;
|
let isSystemAdmin = false;
|
||||||
var teamSettings = null;
|
let teamSettings = null;
|
||||||
let integrationsLink = null;
|
let integrationsLink = null;
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
@@ -322,6 +316,7 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const teams = [];
|
const teams = [];
|
||||||
|
let moreTeams = false;
|
||||||
|
|
||||||
if (config.EnableTeamCreation === 'true') {
|
if (config.EnableTeamCreation === 'true') {
|
||||||
teams.push(
|
teams.push(
|
||||||
@@ -340,6 +335,31 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isAlreadyMember = this.state.teamMembers.reduce((result, item) => {
|
||||||
|
result[item.team_id] = true;
|
||||||
|
return result;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
for (const id in this.state.teamListings) {
|
||||||
|
if (this.state.teamListings.hasOwnProperty(id) && !isAlreadyMember[id]) {
|
||||||
|
moreTeams = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moreTeams) {
|
||||||
|
teams.push(
|
||||||
|
<li key='joinTeam_li'>
|
||||||
|
<Link to='/select_team'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='navbar_dropdown.join'
|
||||||
|
defaultMessage='Join Another Team'
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
teams.push(
|
teams.push(
|
||||||
<li key='leaveTeam_li'>
|
<li key='leaveTeam_li'>
|
||||||
<a
|
<a
|
||||||
@@ -354,39 +374,6 @@ export default class SidebarHeaderDropdown extends React.Component {
|
|||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.state.teamMembers && this.state.teamMembers.length > 1) {
|
|
||||||
teams.push(
|
|
||||||
<li
|
|
||||||
key='teamDiv'
|
|
||||||
className='divider'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
for (var index in this.state.teamMembers) {
|
|
||||||
if (this.state.teamMembers.hasOwnProperty(index)) {
|
|
||||||
var teamMember = this.state.teamMembers[index];
|
|
||||||
var team = this.state.teams[teamMember.team_id];
|
|
||||||
|
|
||||||
if (team.name !== this.props.teamName) {
|
|
||||||
teams.push(
|
|
||||||
<li key={'team_' + team.name}>
|
|
||||||
<Link
|
|
||||||
to={'/' + team.name + '/channels/town-square'}
|
|
||||||
onClick={this.handleSwitchTeams}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='navbar_dropdown.switchTo'
|
|
||||||
defaultMessage='Switch to '
|
|
||||||
/>
|
|
||||||
{team.display_name}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let helpLink = null;
|
let helpLink = null;
|
||||||
if (config.HelpLink) {
|
if (config.HelpLink) {
|
||||||
helpLink = (
|
helpLink = (
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export default class SidebarRight extends React.Component {
|
|||||||
$('.app__body .inner-wrap').removeClass('.move--right');
|
$('.app__body .inner-wrap').removeClass('.move--right');
|
||||||
$('.app__body .inner-wrap').addClass('move--left');
|
$('.app__body .inner-wrap').addClass('move--left');
|
||||||
$('.app__body .sidebar--left').removeClass('move--right');
|
$('.app__body .sidebar--left').removeClass('move--right');
|
||||||
|
$('.multi-teams .team-sidebar').removeClass('move--right');
|
||||||
$('.app__body .sidebar--right').addClass('move--left');
|
$('.app__body .sidebar--right').addClass('move--left');
|
||||||
|
|
||||||
//$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
|
//$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
|
||||||
|
|||||||
@@ -391,15 +391,6 @@ export default class SidebarRightMenu extends React.Component {
|
|||||||
{teamSettingsLink}
|
{teamSettingsLink}
|
||||||
{manageLink}
|
{manageLink}
|
||||||
{consoleLink}
|
{consoleLink}
|
||||||
<li>
|
|
||||||
<Link to='/select_team'>
|
|
||||||
<i className='icon fa fa-exchange'/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='sidebar_right_menu.switch_team'
|
|
||||||
defaultMessage='Team Selection'
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li className='divider'/>
|
<li className='divider'/>
|
||||||
{helpLink}
|
{helpLink}
|
||||||
{reportLink}
|
{reportLink}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export default class SignupEmail extends React.Component {
|
|||||||
if (query.redirect_to) {
|
if (query.redirect_to) {
|
||||||
browserHistory.push(query.redirect_to);
|
browserHistory.push(query.redirect_to);
|
||||||
} else {
|
} else {
|
||||||
browserHistory.push('/select_team');
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -133,7 +133,7 @@ export default class SignupEmail extends React.Component {
|
|||||||
if (query.redirect_to) {
|
if (query.redirect_to) {
|
||||||
browserHistory.push(query.redirect_to);
|
browserHistory.push(query.redirect_to);
|
||||||
} else {
|
} else {
|
||||||
browserHistory.push('/select_team');
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -90,8 +90,13 @@ export default class SignupLdap extends React.Component {
|
|||||||
finishSignup() {
|
finishSignup() {
|
||||||
GlobalActions.emitInitialLoad(
|
GlobalActions.emitInitialLoad(
|
||||||
() => {
|
() => {
|
||||||
|
const query = this.props.location.query;
|
||||||
GlobalActions.loadDefaultLocale();
|
GlobalActions.loadDefaultLocale();
|
||||||
browserHistory.push('/select_team');
|
if (query.redirect_to) {
|
||||||
|
browserHistory.push(query.redirect_to);
|
||||||
|
} else {
|
||||||
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export default class SignupController extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (userLoggedIn) {
|
if (userLoggedIn) {
|
||||||
browserHistory.push('/select_team');
|
GlobalActions.redirectUserToDefaultTeam();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
webapp/components/team_sidebar/components/team_button.jsx
Обычный файл
84
webapp/components/team_sidebar/components/team_button.jsx
Обычный файл
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import Constants from 'utils/constants.jsx';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {Link} from 'react-router/es6';
|
||||||
|
import {Tooltip, OverlayTrigger} from 'react-bootstrap';
|
||||||
|
|
||||||
|
export default class TeamButton extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleDisabled = this.handleDisabled.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisabled(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let teamClass = this.props.active ? 'active' : '';
|
||||||
|
const disabled = this.props.disabled ? 'team-disabled' : '';
|
||||||
|
const handleClick = (this.props.active || this.props.disabled) ? this.handleDisabled : null;
|
||||||
|
let badge;
|
||||||
|
|
||||||
|
if (!teamClass) {
|
||||||
|
teamClass = this.props.unread ? 'unread' : '';
|
||||||
|
|
||||||
|
if (this.props.mentions) {
|
||||||
|
badge = (
|
||||||
|
<span className='badge pull-right small'>{this.props.mentions}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`team-container ${teamClass}`}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
className={disabled}
|
||||||
|
to={this.props.url}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<OverlayTrigger
|
||||||
|
delayShow={Constants.OVERLAY_TIME_DELAY}
|
||||||
|
placement={this.props.placement}
|
||||||
|
overlay={
|
||||||
|
<Tooltip id={`tooltip-${this.props.url}`}>
|
||||||
|
{this.props.tip}
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className='team-btn'>
|
||||||
|
{badge}
|
||||||
|
{this.props.contents}
|
||||||
|
</div>
|
||||||
|
</OverlayTrigger>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamButton.defaultProps = {
|
||||||
|
tip: '',
|
||||||
|
placement: 'right',
|
||||||
|
active: false,
|
||||||
|
disabled: false,
|
||||||
|
unread: false,
|
||||||
|
mentions: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
TeamButton.propTypes = {
|
||||||
|
url: React.PropTypes.string.isRequired,
|
||||||
|
contents: React.PropTypes.node.isRequired,
|
||||||
|
tip: React.PropTypes.node,
|
||||||
|
active: React.PropTypes.bool,
|
||||||
|
disabled: React.PropTypes.bool,
|
||||||
|
unread: React.PropTypes.bool,
|
||||||
|
mentions: React.PropTypes.number,
|
||||||
|
placement: React.PropTypes.oneOf(['left', 'right', 'top', 'bottom'])
|
||||||
|
};
|
||||||
169
webapp/components/team_sidebar/team_sidebar_controller.jsx
Обычный файл
169
webapp/components/team_sidebar/team_sidebar_controller.jsx
Обычный файл
@@ -0,0 +1,169 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import TeamButton from './components/team_button.jsx';
|
||||||
|
|
||||||
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
|
import UserStore from 'stores/user_store.jsx';
|
||||||
|
|
||||||
|
import * as AsyncClient from 'utils/async_client.jsx';
|
||||||
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
|
import React from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
export default class TeamSidebar extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||||
|
this.onChange = this.onChange.bind(this);
|
||||||
|
this.handleResize = this.handleResize.bind(this);
|
||||||
|
this.setStyles = this.setStyles.bind(this);
|
||||||
|
|
||||||
|
this.state = this.getStateFromStores();
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateFromStores() {
|
||||||
|
const teamMembers = TeamStore.getMyTeamMembers();
|
||||||
|
const currentTeamId = TeamStore.getCurrentId();
|
||||||
|
|
||||||
|
return {
|
||||||
|
teams: TeamStore.getAll(),
|
||||||
|
teamListings: TeamStore.getTeamListings(),
|
||||||
|
teamMembers,
|
||||||
|
currentTeamId,
|
||||||
|
show: teamMembers && teamMembers.length > 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
window.addEventListener('resize', this.handleResize);
|
||||||
|
TeamStore.addChangeListener(this.onChange);
|
||||||
|
TeamStore.addUnreadChangeListener(this.onChange);
|
||||||
|
AsyncClient.getAllTeamListings();
|
||||||
|
this.setStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('resize', this.handleResize);
|
||||||
|
TeamStore.removeChangeListener(this.onChange);
|
||||||
|
TeamStore.removeUnreadChangeListener(this.onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
$('.team-wrapper').perfectScrollbar();
|
||||||
|
|
||||||
|
// reset the scrollbar upon switching teams
|
||||||
|
if (this.state.currentTeam !== prevState.currentTeam) {
|
||||||
|
this.refs.container.scrollTop = 0;
|
||||||
|
$('.team-wrapper').perfectScrollbar('update');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange() {
|
||||||
|
this.setState(this.getStateFromStores());
|
||||||
|
this.setStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResize() {
|
||||||
|
const teamMembers = this.state.teamMembers;
|
||||||
|
this.setState({show: teamMembers && teamMembers.length > 1});
|
||||||
|
this.setStyles();
|
||||||
|
}
|
||||||
|
|
||||||
|
setStyles() {
|
||||||
|
const root = document.querySelector('#root');
|
||||||
|
|
||||||
|
if (this.state.show) {
|
||||||
|
root.classList.add('multi-teams');
|
||||||
|
} else {
|
||||||
|
root.classList.remove('multi-teams');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (!this.state.show) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const myTeams = [];
|
||||||
|
const isSystemAdmin = Utils.isSystemAdmin(UserStore.getCurrentUser().roles);
|
||||||
|
const isAlreadyMember = new Map();
|
||||||
|
let moreTeams = false;
|
||||||
|
|
||||||
|
for (const index in this.state.teamMembers) {
|
||||||
|
if (this.state.teamMembers.hasOwnProperty(index)) {
|
||||||
|
const teamMember = this.state.teamMembers[index];
|
||||||
|
const teamId = teamMember.team_id;
|
||||||
|
myTeams.push(Object.assign({
|
||||||
|
unread: teamMember.msg_count > 0,
|
||||||
|
mentions: teamMember.mention_count
|
||||||
|
}, this.state.teams[teamId]));
|
||||||
|
isAlreadyMember[teamId] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id in this.state.teamListings) {
|
||||||
|
if (this.state.teamListings.hasOwnProperty(id) && !isAlreadyMember[id]) {
|
||||||
|
moreTeams = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const teams = myTeams.
|
||||||
|
sort((a, b) => a.display_name.localeCompare(b.display_name)).
|
||||||
|
map((team) => {
|
||||||
|
return (
|
||||||
|
<TeamButton
|
||||||
|
key={'switch_team_' + team.name}
|
||||||
|
url={`/${team.name}`}
|
||||||
|
tip={team.display_name}
|
||||||
|
active={team.id === this.state.currentTeamId}
|
||||||
|
contents={team.display_name.substring(0, 1).toUpperCase()}
|
||||||
|
unread={team.unread}
|
||||||
|
mentions={team.mentions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (moreTeams) {
|
||||||
|
teams.push(
|
||||||
|
<TeamButton
|
||||||
|
key='more_teams'
|
||||||
|
url='/select_team'
|
||||||
|
tip={
|
||||||
|
<FormattedMessage
|
||||||
|
id='team_sidebar.join'
|
||||||
|
defaultMessage='Other teams you can join.'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
contents={<i className='fa fa-plus'/>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (global.window.mm_config.EnableTeamCreation === 'true' || isSystemAdmin) {
|
||||||
|
teams.push(
|
||||||
|
<TeamButton
|
||||||
|
key='more_teams'
|
||||||
|
url='/create_team'
|
||||||
|
tip={
|
||||||
|
<FormattedMessage
|
||||||
|
id='navbar_dropdown.create'
|
||||||
|
defaultMessage='Create a New Team'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
contents={<i className='fa fa-plus'/>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='team-sidebar'>
|
||||||
|
<div className='team-wrapper'>
|
||||||
|
{teams}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@ export default class SidebarRight extends React.Component {
|
|||||||
$('.app__body .inner-wrap').removeClass('move--right');
|
$('.app__body .inner-wrap').removeClass('move--right');
|
||||||
$('.app__body .inner-wrap').addClass('webrtc--show');
|
$('.app__body .inner-wrap').addClass('webrtc--show');
|
||||||
$('.app__body .sidebar--left').removeClass('move--right');
|
$('.app__body .sidebar--left').removeClass('move--right');
|
||||||
|
$('.multi-teams .team-sidebar').removeClass('move--right');
|
||||||
$('.app__body .webrtc').addClass('webrtc--show');
|
$('.app__body .webrtc').addClass('webrtc--show');
|
||||||
|
|
||||||
//$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
|
//$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
|
||||||
|
|||||||
@@ -1602,6 +1602,7 @@
|
|||||||
"navbar_dropdown.help": "Help",
|
"navbar_dropdown.help": "Help",
|
||||||
"navbar_dropdown.integrations": "Integrations",
|
"navbar_dropdown.integrations": "Integrations",
|
||||||
"navbar_dropdown.inviteMember": "Invite New Member",
|
"navbar_dropdown.inviteMember": "Invite New Member",
|
||||||
|
"navbar_dropdown.join": "Join Another Team",
|
||||||
"navbar_dropdown.leave": "Leave Team",
|
"navbar_dropdown.leave": "Leave Team",
|
||||||
"navbar_dropdown.logout": "Logout",
|
"navbar_dropdown.logout": "Logout",
|
||||||
"navbar_dropdown.manageMembers": "Manage Members",
|
"navbar_dropdown.manageMembers": "Manage Members",
|
||||||
@@ -1750,7 +1751,6 @@
|
|||||||
"sidebar_right_menu.nativeApps": "Download Apps",
|
"sidebar_right_menu.nativeApps": "Download Apps",
|
||||||
"sidebar_right_menu.recentMentions": "Recent Mentions",
|
"sidebar_right_menu.recentMentions": "Recent Mentions",
|
||||||
"sidebar_right_menu.report": "Report a Problem",
|
"sidebar_right_menu.report": "Report a Problem",
|
||||||
"sidebar_right_menu.switch_team": "Team Selection",
|
|
||||||
"sidebar_right_menu.teamLink": "Get Team Invite Link",
|
"sidebar_right_menu.teamLink": "Get Team Invite Link",
|
||||||
"sidebar_right_menu.teamSettings": "Team Settings",
|
"sidebar_right_menu.teamSettings": "Team Settings",
|
||||||
"sidebar_right_menu.viewMembers": "View Members",
|
"sidebar_right_menu.viewMembers": "View Members",
|
||||||
@@ -1760,7 +1760,6 @@
|
|||||||
"signup.ldap": "AD/LDAP Credentials",
|
"signup.ldap": "AD/LDAP Credentials",
|
||||||
"signup.office365": "Office 365",
|
"signup.office365": "Office 365",
|
||||||
"signup.title": "Create an account with:",
|
"signup.title": "Create an account with:",
|
||||||
"signup_team.choose": "Your teams: ",
|
|
||||||
"signup_team.createTeam": "Or Create a Team",
|
"signup_team.createTeam": "Or Create a Team",
|
||||||
"signup_team.disabled": "Team creation has been disabled. Please contact an administrator for access.",
|
"signup_team.disabled": "Team creation has been disabled. Please contact an administrator for access.",
|
||||||
"signup_team.join_open": "Teams you can join: ",
|
"signup_team.join_open": "Teams you can join: ",
|
||||||
@@ -1853,6 +1852,7 @@
|
|||||||
"team_settings_modal.generalTab": "General",
|
"team_settings_modal.generalTab": "General",
|
||||||
"team_settings_modal.importTab": "Import",
|
"team_settings_modal.importTab": "Import",
|
||||||
"team_settings_modal.title": "Team Settings",
|
"team_settings_modal.title": "Team Settings",
|
||||||
|
"team_sidebar.join": "Other teams you can join.",
|
||||||
"textbox.bold": "**bold**",
|
"textbox.bold": "**bold**",
|
||||||
"textbox.edit": "Edit message",
|
"textbox.edit": "Edit message",
|
||||||
"textbox.help": "Help",
|
"textbox.help": "Help",
|
||||||
@@ -2170,6 +2170,7 @@
|
|||||||
"web.footer.privacy": "Privacy",
|
"web.footer.privacy": "Privacy",
|
||||||
"web.footer.terms": "Terms",
|
"web.footer.terms": "Terms",
|
||||||
"web.header.back": "Back",
|
"web.header.back": "Back",
|
||||||
|
"web.header.logout": "Logout",
|
||||||
"web.root.signup_info": "All team communication in one place, searchable and accessible anywhere",
|
"web.root.signup_info": "All team communication in one place, searchable and accessible anywhere",
|
||||||
"webrtc.busy": "{username} is busy.",
|
"webrtc.busy": "{username} is busy.",
|
||||||
"webrtc.call": "Call",
|
"webrtc.call": "Call",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const ActionTypes = Constants.ActionTypes;
|
|||||||
import * as AsyncClient from 'utils/async_client.jsx';
|
import * as AsyncClient from 'utils/async_client.jsx';
|
||||||
import Client from 'client/web_client.jsx';
|
import Client from 'client/web_client.jsx';
|
||||||
import ChannelStore from 'stores/channel_store.jsx';
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
|
import PreferenceStore from 'stores/preference_store.jsx';
|
||||||
|
|
||||||
import emojiRoute from 'routes/route_emoji.jsx';
|
import emojiRoute from 'routes/route_emoji.jsx';
|
||||||
import integrationsRoute from 'routes/route_integrations.jsx';
|
import integrationsRoute from 'routes/route_integrations.jsx';
|
||||||
@@ -63,7 +64,7 @@ function preNeedsTeam(nextState, replace, callback) {
|
|||||||
// First check to make sure you're in the current team
|
// First check to make sure you're in the current team
|
||||||
// for the current url.
|
// for the current url.
|
||||||
const teamName = nextState.params.team;
|
const teamName = nextState.params.team;
|
||||||
var team = TeamStore.getByName(teamName);
|
const team = TeamStore.getByName(teamName);
|
||||||
|
|
||||||
if (!team) {
|
if (!team) {
|
||||||
browserHistory.push('/');
|
browserHistory.push('/');
|
||||||
@@ -75,9 +76,10 @@ function preNeedsTeam(nextState, replace, callback) {
|
|||||||
TeamStore.saveMyTeam(team);
|
TeamStore.saveMyTeam(team);
|
||||||
TeamStore.emitChange();
|
TeamStore.emitChange();
|
||||||
loadProfilesAndTeamMembersForDMSidebar();
|
loadProfilesAndTeamMembersForDMSidebar();
|
||||||
|
AsyncClient.getMyTeamsUnread();
|
||||||
AsyncClient.getMyChannelMembers();
|
AsyncClient.getMyChannelMembers();
|
||||||
|
|
||||||
var d1 = $.Deferred(); //eslint-disable-line new-cap
|
const d1 = $.Deferred(); //eslint-disable-line new-cap
|
||||||
|
|
||||||
Client.getChannels(
|
Client.getChannels(
|
||||||
(data) => {
|
(data) => {
|
||||||
@@ -101,6 +103,20 @@ function preNeedsTeam(nextState, replace, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectLastChannel(nextState, replace, callback) {
|
||||||
|
const team = TeamStore.getByName(nextState.params.team);
|
||||||
|
const channelId = PreferenceStore.get(team.id, 'channel');
|
||||||
|
const channel = ChannelStore.getChannelById(channelId);
|
||||||
|
|
||||||
|
let channelName = 'town-square';
|
||||||
|
if (channel) {
|
||||||
|
channelName = channel.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
replace(`/${team.name}/channels/${channelName}`);
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
function onPermalinkEnter(nextState, replace, callback) {
|
function onPermalinkEnter(nextState, replace, callback) {
|
||||||
const postId = nextState.params.postid;
|
const postId = nextState.params.postid;
|
||||||
GlobalActions.emitPostFocusEvent(
|
GlobalActions.emitPostFocusEvent(
|
||||||
@@ -112,7 +128,7 @@ function onPermalinkEnter(nextState, replace, callback) {
|
|||||||
export default {
|
export default {
|
||||||
path: ':team',
|
path: ':team',
|
||||||
onEnter: preNeedsTeam,
|
onEnter: preNeedsTeam,
|
||||||
indexRoute: {onEnter: (nextState, replace) => replace('/' + nextState.params.team + '/channels/town-square')},
|
indexRoute: {onEnter: selectLastChannel},
|
||||||
childRoutes: [
|
childRoutes: [
|
||||||
integrationsRoute,
|
integrationsRoute,
|
||||||
emojiRoute,
|
emojiRoute,
|
||||||
@@ -126,10 +142,11 @@ export default {
|
|||||||
onEnter: onChannelEnter,
|
onEnter: onChannelEnter,
|
||||||
getComponents: (location, callback) => {
|
getComponents: (location, callback) => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
System.import('components/team_sidebar/team_sidebar_controller.jsx'),
|
||||||
System.import('components/sidebar.jsx'),
|
System.import('components/sidebar.jsx'),
|
||||||
System.import('components/channel_view.jsx')
|
System.import('components/channel_view.jsx')
|
||||||
]).then(
|
]).then(
|
||||||
(comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
|
(comarr) => callback(null, {team_sidebar: comarr[0].default, sidebar: comarr[1].default, center: comarr[2].default})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -138,10 +155,11 @@ export default {
|
|||||||
onEnter: onPermalinkEnter,
|
onEnter: onPermalinkEnter,
|
||||||
getComponents: (location, callback) => {
|
getComponents: (location, callback) => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
System.import('components/team_sidebar/team_sidebar_controller.jsx'),
|
||||||
System.import('components/sidebar.jsx'),
|
System.import('components/sidebar.jsx'),
|
||||||
System.import('components/permalink_view.jsx')
|
System.import('components/permalink_view.jsx')
|
||||||
]).then(
|
]).then(
|
||||||
(comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
|
(comarr) => callback(null, {team_sidebar: comarr[0].default, sidebar: comarr[1].default, center: comarr[2].default})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -149,10 +167,11 @@ export default {
|
|||||||
path: 'tutorial',
|
path: 'tutorial',
|
||||||
getComponents: (location, callback) => {
|
getComponents: (location, callback) => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
|
System.import('components/team_sidebar/team_sidebar_controller.jsx'),
|
||||||
System.import('components/sidebar.jsx'),
|
System.import('components/sidebar.jsx'),
|
||||||
System.import('components/tutorial/tutorial_view.jsx')
|
System.import('components/tutorial/tutorial_view.jsx')
|
||||||
]).then(
|
]).then(
|
||||||
(comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
|
(comarr) => callback(null, {team_sidebar: comarr[0].default, sidebar: comarr[1].default, center: comarr[2].default})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,5 @@
|
|||||||
@import 'sidebar-left';
|
@import 'sidebar-left';
|
||||||
@import 'sidebar-menu';
|
@import 'sidebar-menu';
|
||||||
@import 'sidebar-right';
|
@import 'sidebar-right';
|
||||||
|
@import 'team-sidebar';
|
||||||
@import 'webhooks';
|
@import 'webhooks';
|
||||||
|
|||||||
90
webapp/sass/layout/_team-sidebar.scss
Обычный файл
90
webapp/sass/layout/_team-sidebar.scss
Обычный файл
@@ -0,0 +1,90 @@
|
|||||||
|
.multi-teams {
|
||||||
|
.team-sidebar {
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
text-align: center;
|
||||||
|
width: 75px;
|
||||||
|
z-index: 5;
|
||||||
|
|
||||||
|
.team-wrapper {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
padding-top: 15px;
|
||||||
|
|
||||||
|
.team-container {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
display: table;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.team-btn {
|
||||||
|
border: none;
|
||||||
|
height: 42px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
width: 42px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -3px;
|
||||||
|
right: -6px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.team-btn {
|
||||||
|
border: 1px solid #fff;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active:before {
|
||||||
|
background: black;
|
||||||
|
content: '';
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.unread:before {
|
||||||
|
background: black;
|
||||||
|
border-radius: 50%;
|
||||||
|
content: '';
|
||||||
|
height: 5px;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.team-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-container a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar--left {
|
||||||
|
left: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app__content {
|
||||||
|
margin-left: 295px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1163,6 +1163,33 @@
|
|||||||
.post-comments {
|
.post-comments {
|
||||||
padding: 9px 21px 10px 10px !important;
|
padding: 9px 21px 10px 10px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.multi-teams {
|
||||||
|
.team-sidebar {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app__content {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar--left {
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
&.move--right {
|
||||||
|
left: 75px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-sidebar {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.move--right {
|
||||||
|
display: block;
|
||||||
|
@include translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 640px) {
|
@media screen and (max-width: 640px) {
|
||||||
@@ -1493,6 +1520,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 320px) {
|
@media screen and (max-width: 320px) {
|
||||||
|
.multi-teams {
|
||||||
|
.team-sidebar {
|
||||||
|
width: 65px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar--left {
|
||||||
|
width: 220px;
|
||||||
|
|
||||||
|
&.move--right {
|
||||||
|
left: 65px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
.post__header {
|
.post__header {
|
||||||
.col__name {
|
.col__name {
|
||||||
|
|||||||
@@ -215,6 +215,10 @@ class ChannelStoreClass extends EventEmitter {
|
|||||||
return this.channels;
|
return this.channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getChannelById(id) {
|
||||||
|
return this.channels.filter((c) => c.id === id)[0];
|
||||||
|
}
|
||||||
|
|
||||||
storeMyChannelMember(channelMember) {
|
storeMyChannelMember(channelMember) {
|
||||||
const members = Object.assign({}, this.getMyMembers());
|
const members = Object.assign({}, this.getMyMembers());
|
||||||
members[channelMember.channel_id] = channelMember;
|
members[channelMember.channel_id] = channelMember;
|
||||||
@@ -348,7 +352,6 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
|
|||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.CLICK_CHANNEL:
|
case ActionTypes.CLICK_CHANNEL:
|
||||||
ChannelStore.setCurrentId(action.id);
|
ChannelStore.setCurrentId(action.id);
|
||||||
ChannelStore.resetCounts(action.id);
|
|
||||||
ChannelStore.setPostMode(ChannelStore.POST_MODE_CHANNEL);
|
ChannelStore.setPostMode(ChannelStore.POST_MODE_CHANNEL);
|
||||||
ChannelStore.emitChange();
|
ChannelStore.emitChange();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class NotificationStoreClass extends EventEmitter {
|
|||||||
}
|
}
|
||||||
const teamId = msgProps.team_id;
|
const teamId = msgProps.team_id;
|
||||||
|
|
||||||
const channel = ChannelStore.get(post.channel_id);
|
let channel = ChannelStore.get(post.channel_id);
|
||||||
const user = UserStore.getCurrentUser();
|
const user = UserStore.getCurrentUser();
|
||||||
const member = ChannelStore.getMyMember(post.channel_id);
|
const member = ChannelStore.getMyMember(post.channel_id);
|
||||||
|
|
||||||
@@ -72,6 +72,9 @@ class NotificationStoreClass extends EventEmitter {
|
|||||||
let title = Utils.localizeMessage('channel_loader.posted', 'Posted');
|
let title = Utils.localizeMessage('channel_loader.posted', 'Posted');
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
title = msgProps.channel_display_name;
|
title = msgProps.channel_display_name;
|
||||||
|
channel = {
|
||||||
|
name: msgProps.channel_name
|
||||||
|
};
|
||||||
} else if (channel.type === Constants.DM_CHANNEL) {
|
} else if (channel.type === Constants.DM_CHANNEL) {
|
||||||
title = Utils.localizeMessage('notification.dm', 'Direct Message');
|
title = Utils.localizeMessage('notification.dm', 'Direct Message');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -140,9 +140,12 @@ class PreferenceStore extends EventEmitter {
|
|||||||
}
|
}
|
||||||
this.emitChange();
|
this.emitChange();
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.CLICK_CHANNEL:
|
||||||
|
this.setPreference(action.team_id, 'channel', action.id);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.PreferenceStore = new PreferenceStore();
|
global.PreferenceStore = new PreferenceStore();
|
||||||
export default global.PreferenceStore;
|
export default global.PreferenceStore;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const ActionTypes = Constants.ActionTypes;
|
|||||||
|
|
||||||
const CHANGE_EVENT = 'change';
|
const CHANGE_EVENT = 'change';
|
||||||
const STATS_EVENT = 'stats';
|
const STATS_EVENT = 'stats';
|
||||||
|
const UNREAD_EVENT = 'unread';
|
||||||
|
|
||||||
var Utils;
|
var Utils;
|
||||||
|
|
||||||
@@ -53,17 +54,31 @@ class TeamStoreClass extends EventEmitter {
|
|||||||
this.removeListener(STATS_EVENT, callback);
|
this.removeListener(STATS_EVENT, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitUnreadChange() {
|
||||||
|
this.emit(UNREAD_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
addUnreadChangeListener(callback) {
|
||||||
|
this.on(UNREAD_EVENT, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUnreadChangeListener(callback) {
|
||||||
|
this.removeListener(UNREAD_EVENT, callback);
|
||||||
|
}
|
||||||
|
|
||||||
get(id) {
|
get(id) {
|
||||||
var c = this.getAll();
|
var c = this.getAll();
|
||||||
return c[id];
|
return c[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName(name) {
|
getByName(name) {
|
||||||
var t = this.getAll();
|
const t = this.getAll();
|
||||||
|
|
||||||
for (var id in t) {
|
for (const id in t) {
|
||||||
if (t[id].name === name) {
|
if (t.hasOwnProperty(id)) {
|
||||||
return t[id];
|
if (t[id].name === name) {
|
||||||
|
return t[id];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +173,25 @@ class TeamStoreClass extends EventEmitter {
|
|||||||
this.teams = teams;
|
this.teams = teams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateTeam(team) {
|
||||||
|
const t = JSON.parse(team);
|
||||||
|
if (this.teams && this.teams[t.id]) {
|
||||||
|
this.teams[t.id] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.teamListings && this.teamListings[t.id]) {
|
||||||
|
if (t.allow_open_invite) {
|
||||||
|
this.teamListings[t.id] = t;
|
||||||
|
} else {
|
||||||
|
Reflect.deleteProperty(this.teamListings, t.id);
|
||||||
|
}
|
||||||
|
} else if (t.allow_open_invite) {
|
||||||
|
this.teamListings[t.id] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emitChange();
|
||||||
|
}
|
||||||
|
|
||||||
saveMyTeam(team) {
|
saveMyTeam(team) {
|
||||||
this.saveTeam(team);
|
this.saveTeam(team);
|
||||||
this.currentTeamId = team.id;
|
this.currentTeamId = team.id;
|
||||||
@@ -175,12 +209,29 @@ class TeamStoreClass extends EventEmitter {
|
|||||||
this.my_team_members.push(member);
|
this.my_team_members.push(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveMyTeamMembersUnread(members) {
|
||||||
|
for (let i = 0; i < this.my_team_members.length; i++) {
|
||||||
|
const team = this.my_team_members[i];
|
||||||
|
const member = members.filter((m) => m.team_id === team.team_id)[0];
|
||||||
|
|
||||||
|
if (member) {
|
||||||
|
this.my_team_members[i] = Object.assign({},
|
||||||
|
team,
|
||||||
|
{
|
||||||
|
msg_count: member.msg_count,
|
||||||
|
mention_count: member.mention_count
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
removeMyTeamMember(teamId) {
|
removeMyTeamMember(teamId) {
|
||||||
for (let i = 0; i < this.my_team_members.length; i++) {
|
for (let i = 0; i < this.my_team_members.length; i++) {
|
||||||
if (this.my_team_members[i].team_id === teamId) {
|
if (this.my_team_members[i].team_id === teamId) {
|
||||||
this.my_team_members.splice(i, 1);
|
this.my_team_members.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.emitChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
getMyTeamMembers() {
|
getMyTeamMembers() {
|
||||||
@@ -248,6 +299,14 @@ class TeamStoreClass extends EventEmitter {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateUnreadCount(teamId, totalMsgCount, channelMember) {
|
||||||
|
const member = this.my_team_members.filter((m) => m.team_id === teamId)[0];
|
||||||
|
if (member) {
|
||||||
|
member.msg_count -= (totalMsgCount - channelMember.msg_count);
|
||||||
|
member.mention_count -= channelMember.mention_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var TeamStore = new TeamStoreClass();
|
var TeamStore = new TeamStoreClass();
|
||||||
@@ -277,6 +336,10 @@ TeamStore.dispatchToken = AppDispatcher.register((payload) => {
|
|||||||
TeamStore.saveMyTeamMembers(action.team_members);
|
TeamStore.saveMyTeamMembers(action.team_members);
|
||||||
TeamStore.emitChange();
|
TeamStore.emitChange();
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.RECEIVED_MY_TEAMS_UNREAD:
|
||||||
|
TeamStore.saveMyTeamMembersUnread(action.team_members);
|
||||||
|
TeamStore.emitChange();
|
||||||
|
break;
|
||||||
case ActionTypes.RECEIVED_ALL_TEAM_LISTINGS:
|
case ActionTypes.RECEIVED_ALL_TEAM_LISTINGS:
|
||||||
TeamStore.saveTeamListings(action.teams);
|
TeamStore.saveTeamListings(action.teams);
|
||||||
TeamStore.emitChange();
|
TeamStore.emitChange();
|
||||||
@@ -292,6 +355,12 @@ TeamStore.dispatchToken = AppDispatcher.register((payload) => {
|
|||||||
TeamStore.saveStats(action.team_id, action.stats);
|
TeamStore.saveStats(action.team_id, action.stats);
|
||||||
TeamStore.emitStatsChange();
|
TeamStore.emitStatsChange();
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.CLICK_CHANNEL:
|
||||||
|
if (action.channelMember) {
|
||||||
|
TeamStore.updateUnreadCount(action.team_id, action.total_msg_count, action.channelMember);
|
||||||
|
TeamStore.emitUnreadChange();
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -130,6 +130,20 @@ describe('Client.Team', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getMyTeamMembers', function(done) {
|
||||||
|
TestHelper.initBasic(() => {
|
||||||
|
TestHelper.basicClient().getMyTeamMembers(
|
||||||
|
function(data) {
|
||||||
|
assert.equal(data.length > 0, true);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
function(err) {
|
||||||
|
done(new Error(err.message));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('getTeamMembers', function(done) {
|
it('getTeamMembers', function(done) {
|
||||||
TestHelper.initBasic(() => {
|
TestHelper.initBasic(() => {
|
||||||
TestHelper.basicClient().getTeamMembers(
|
TestHelper.basicClient().getTeamMembers(
|
||||||
|
|||||||
@@ -166,12 +166,21 @@ export function updateLastViewedAt(id, active) {
|
|||||||
channelId,
|
channelId,
|
||||||
isActive,
|
isActive,
|
||||||
() => {
|
() => {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_PREFERENCE,
|
||||||
|
preference: {
|
||||||
|
category: 'last',
|
||||||
|
name: TeamStore.getCurrentId(),
|
||||||
|
value: channelId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
callTracker[`updateLastViewed${channelId}`] = 0;
|
callTracker[`updateLastViewed${channelId}`] = 0;
|
||||||
ErrorStore.clearLastError();
|
ErrorStore.clearLastError();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
callTracker[`updateLastViewed${channelId}`] = 0;
|
callTracker[`updateLastViewed${channelId}`] = 0;
|
||||||
var count = ErrorStore.getConnectionErrorCount();
|
const count = ErrorStore.getConnectionErrorCount();
|
||||||
ErrorStore.setConnectionErrorCount(count + 1);
|
ErrorStore.setConnectionErrorCount(count + 1);
|
||||||
dispatchError(err, 'updateLastViewedAt');
|
dispatchError(err, 'updateLastViewedAt');
|
||||||
}
|
}
|
||||||
@@ -203,6 +212,14 @@ export function setLastViewedAt(lastViewedAt, id) {
|
|||||||
channelId,
|
channelId,
|
||||||
lastViewedAt,
|
lastViewedAt,
|
||||||
() => {
|
() => {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_PREFERENCE,
|
||||||
|
preference: {
|
||||||
|
category: 'last',
|
||||||
|
name: TeamStore.getCurrentId(),
|
||||||
|
value: channelId
|
||||||
|
}
|
||||||
|
});
|
||||||
callTracker[`setLastViewedAt${channelId}${lastViewedAt}`] = 0;
|
callTracker[`setLastViewedAt${channelId}${lastViewedAt}`] = 0;
|
||||||
ErrorStore.clearLastError();
|
ErrorStore.clearLastError();
|
||||||
},
|
},
|
||||||
@@ -847,6 +864,61 @@ export function getTeamMember(teamId, userId) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMyTeamMembers() {
|
||||||
|
const callName = 'getMyTeamMembers';
|
||||||
|
if (isCallInProgress(callName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callTracker[callName] = utils.getTimestamp();
|
||||||
|
Client.getMyTeamMembers(
|
||||||
|
(data) => {
|
||||||
|
callTracker[callName] = 0;
|
||||||
|
|
||||||
|
const members = {};
|
||||||
|
for (const member of data) {
|
||||||
|
members[member.team_id] = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_MY_TEAM_MEMBERS_UNREAD,
|
||||||
|
team_members: members
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
callTracker[callName] = 0;
|
||||||
|
dispatchError(err, 'getMyTeamMembers');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMyTeamsUnread(teamId) {
|
||||||
|
const members = TeamStore.getMyTeamMembers();
|
||||||
|
if (members.length > 1) {
|
||||||
|
const callName = 'getMyTeamsUnread';
|
||||||
|
if (isCallInProgress(callName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callTracker[callName] = utils.getTimestamp();
|
||||||
|
Client.getMyTeamsUnread(
|
||||||
|
teamId,
|
||||||
|
(data) => {
|
||||||
|
callTracker[callName] = 0;
|
||||||
|
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_MY_TEAMS_UNREAD,
|
||||||
|
team_members: data
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
callTracker[callName] = 0;
|
||||||
|
dispatchError(err, 'getMyTeamsUnread');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getTeamStats(teamId) {
|
export function getTeamStats(teamId) {
|
||||||
const callName = `getTeamStats${teamId}`;
|
const callName = `getTeamStats${teamId}`;
|
||||||
if (isCallInProgress(callName)) {
|
if (isCallInProgress(callName)) {
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ export const ActionTypes = keyMirror({
|
|||||||
RECEIVED_ALL_TEAMS: null,
|
RECEIVED_ALL_TEAMS: null,
|
||||||
RECEIVED_ALL_TEAM_LISTINGS: null,
|
RECEIVED_ALL_TEAM_LISTINGS: null,
|
||||||
RECEIVED_MY_TEAM_MEMBERS: null,
|
RECEIVED_MY_TEAM_MEMBERS: null,
|
||||||
|
RECEIVED_MY_TEAMS_UNREAD: null,
|
||||||
RECEIVED_MEMBERS_IN_TEAM: null,
|
RECEIVED_MEMBERS_IN_TEAM: null,
|
||||||
RECEIVED_TEAM_STATS: null,
|
RECEIVED_TEAM_STATS: null,
|
||||||
|
|
||||||
@@ -202,6 +203,7 @@ export const SocketEvents = {
|
|||||||
DIRECT_ADDED: 'direct_added',
|
DIRECT_ADDED: 'direct_added',
|
||||||
NEW_USER: 'new_user',
|
NEW_USER: 'new_user',
|
||||||
LEAVE_TEAM: 'leave_team',
|
LEAVE_TEAM: 'leave_team',
|
||||||
|
UPDATE_TEAM: 'update_team',
|
||||||
USER_ADDED: 'user_added',
|
USER_ADDED: 'user_added',
|
||||||
USER_REMOVED: 'user_removed',
|
USER_REMOVED: 'user_removed',
|
||||||
USER_UPDATED: 'user_updated',
|
USER_UPDATED: 'user_updated',
|
||||||
|
|||||||
@@ -104,7 +104,9 @@ export function notifyMe(title, body, channel, teamId, duration, silent) {
|
|||||||
var notification = new Notification(title, {body, tag: body, icon: icon50, requireInteraction: notificationDuration === 0, silent});
|
var notification = new Notification(title, {body, tag: body, icon: icon50, requireInteraction: notificationDuration === 0, silent});
|
||||||
notification.onclick = () => {
|
notification.onclick = () => {
|
||||||
window.focus();
|
window.focus();
|
||||||
if (channel) {
|
if (channel && channel.type === Constants.DM_CHANNEL) {
|
||||||
|
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name);
|
||||||
|
} else if (channel) {
|
||||||
browserHistory.push(TeamStore.getTeamUrl(teamId) + '/channels/' + channel.name);
|
browserHistory.push(TeamStore.getTeamUrl(teamId) + '/channels/' + channel.name);
|
||||||
} else if (teamId) {
|
} else if (teamId) {
|
||||||
browserHistory.push(TeamStore.getTeamUrl(teamId) + '/channels/town-square');
|
browserHistory.push(TeamStore.getTeamUrl(teamId) + '/channels/town-square');
|
||||||
@@ -491,6 +493,8 @@ export function applyTheme(theme) {
|
|||||||
changeCss('.sidebar--left .nav li.active a:before, .app__body .modal .settings-modal .nav-pills>li.active a:before', 'background:' + theme.sidebarTextActiveBorder);
|
changeCss('.sidebar--left .nav li.active a:before, .app__body .modal .settings-modal .nav-pills>li.active a:before', 'background:' + theme.sidebarTextActiveBorder);
|
||||||
changeCss('.sidebar--left .sidebar__divider:before', 'background:' + changeOpacity(theme.sidebarTextActiveBorder, 0.5));
|
changeCss('.sidebar--left .sidebar__divider:before', 'background:' + changeOpacity(theme.sidebarTextActiveBorder, 0.5));
|
||||||
changeCss('.sidebar--left .sidebar__divider', 'color:' + theme.sidebarTextActiveBorder);
|
changeCss('.sidebar--left .sidebar__divider', 'color:' + theme.sidebarTextActiveBorder);
|
||||||
|
changeCss('.multi-teams .team-sidebar .team-wrapper .team-container.active:before', 'background:' + theme.sidebarTextActiveBorder);
|
||||||
|
changeCss('.multi-teams .team-sidebar .team-wrapper .team-container.unread:before', 'background:' + theme.sidebarTextActiveBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.sidebarTextActiveColor) {
|
if (theme.sidebarTextActiveColor) {
|
||||||
@@ -501,13 +505,13 @@ export function applyTheme(theme) {
|
|||||||
if (theme.sidebarHeaderBg) {
|
if (theme.sidebarHeaderBg) {
|
||||||
changeCss('.sidebar--left .team__header, .app__body .sidebar--menu .team__header, .app__body .post-list__timestamp > div', 'background:' + theme.sidebarHeaderBg);
|
changeCss('.sidebar--left .team__header, .app__body .sidebar--menu .team__header, .app__body .post-list__timestamp > div', 'background:' + theme.sidebarHeaderBg);
|
||||||
changeCss('.app__body .modal .modal-header', 'background:' + theme.sidebarHeaderBg);
|
changeCss('.app__body .modal .modal-header', 'background:' + theme.sidebarHeaderBg);
|
||||||
changeCss('.app__body #navbar .navbar-default', 'background:' + theme.sidebarHeaderBg);
|
changeCss('.app__body .multi-teams .team-sidebar, .app__body #navbar .navbar-default', 'background:' + theme.sidebarHeaderBg);
|
||||||
changeCss('@media(max-width: 768px){.app__body .search-bar__container', 'background:' + theme.sidebarHeaderBg);
|
changeCss('@media(max-width: 768px){.app__body .search-bar__container', 'background:' + theme.sidebarHeaderBg);
|
||||||
changeCss('.app__body .attachment .attachment__container', 'border-left-color:' + theme.sidebarHeaderBg);
|
changeCss('.app__body .attachment .attachment__container', 'border-left-color:' + theme.sidebarHeaderBg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.sidebarHeaderTextColor) {
|
if (theme.sidebarHeaderTextColor) {
|
||||||
changeCss('.sidebar--left .team__header .header__info, .app__body .sidebar--menu .team__header .header__info, .app__body .post-list__timestamp > div', 'color:' + theme.sidebarHeaderTextColor);
|
changeCss('.multi-teams .team-sidebar .team-wrapper .team-container .team-btn, .sidebar--left .team__header .header__info, .app__body .sidebar--menu .team__header .header__info, .app__body .post-list__timestamp > div', 'color:' + theme.sidebarHeaderTextColor);
|
||||||
changeCss('.app__body .sidebar-header-dropdown__icon', 'fill:' + theme.sidebarHeaderTextColor);
|
changeCss('.app__body .sidebar-header-dropdown__icon', 'fill:' + theme.sidebarHeaderTextColor);
|
||||||
changeCss('.sidebar--left .team__header .user__name, .app__body .sidebar--menu .team__header .user__name', 'color:' + changeOpacity(theme.sidebarHeaderTextColor, 0.8));
|
changeCss('.sidebar--left .team__header .user__name, .app__body .sidebar--menu .team__header .user__name', 'color:' + changeOpacity(theme.sidebarHeaderTextColor, 0.8));
|
||||||
changeCss('.sidebar--left .team__header:hover .user__name, .app__body .sidebar--menu .team__header:hover .user__name', 'color:' + theme.sidebarHeaderTextColor);
|
changeCss('.sidebar--left .team__header:hover .user__name, .app__body .sidebar--menu .team__header:hover .user__name', 'color:' + theme.sidebarHeaderTextColor);
|
||||||
@@ -534,11 +538,13 @@ export function applyTheme(theme) {
|
|||||||
if (theme.mentionBj) {
|
if (theme.mentionBj) {
|
||||||
changeCss('.sidebar--left .nav-pills__unread-indicator, .app__body .new-messages__button div', 'background:' + theme.mentionBj);
|
changeCss('.sidebar--left .nav-pills__unread-indicator, .app__body .new-messages__button div', 'background:' + theme.mentionBj);
|
||||||
changeCss('.sidebar--left .badge', 'background:' + theme.mentionBj + '!important;');
|
changeCss('.sidebar--left .badge', 'background:' + theme.mentionBj + '!important;');
|
||||||
|
changeCss('.multi-teams .team-sidebar .team-wrapper .team-container .team-btn .badge', 'background:' + theme.mentionBj + '!important;');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.mentionColor) {
|
if (theme.mentionColor) {
|
||||||
changeCss('.sidebar--left .nav-pills__unread-indicator, .app__body .new-messages__button div', 'color:' + theme.mentionColor);
|
changeCss('.sidebar--left .nav-pills__unread-indicator, .app__body .new-messages__button div', 'color:' + theme.mentionColor);
|
||||||
changeCss('.sidebar--left .badge', 'color:' + theme.mentionColor + '!important;');
|
changeCss('.sidebar--left .badge', 'color:' + theme.mentionColor + '!important;');
|
||||||
|
changeCss('.multi-teams .team-sidebar .team-wrapper .team-container .team-btn .badge', 'color:' + theme.mentionColor + '!important;');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.centerChannelBg) {
|
if (theme.centerChannelBg) {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user