diff --git a/api4/channel.go b/api4/channel.go index 3435537548..b490623951 100644 --- a/api4/channel.go +++ b/api4/channel.go @@ -975,7 +975,7 @@ func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - times, err := c.App.ViewChannel(view, c.Params.UserId, !c.App.Session.IsMobileApp()) + times, err := c.App.ViewChannel(view, c.Params.UserId, c.App.Session.Id) if err != nil { c.Err = err return @@ -1137,7 +1137,7 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { return } - cm, err := c.App.AddChannelMember(member.UserId, channel, c.App.Session.UserId, postRootId, !c.App.Session.IsMobileApp()) + cm, err := c.App.AddChannelMember(member.UserId, channel, c.App.Session.UserId, postRootId, c.App.Session.Id) if err != nil { c.Err = err return diff --git a/api4/post.go b/api4/post.go index 6f62473604..ff2b377bb7 100644 --- a/api4/post.go +++ b/api4/post.go @@ -57,7 +57,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) { post.CreateAt = 0 } - rp, err := c.App.CreatePostAsUser(c.App.PostWithProxyRemovedFromImageURLs(post), !c.App.Session.IsMobileApp()) + rp, err := c.App.CreatePostAsUser(c.App.PostWithProxyRemovedFromImageURLs(post), c.App.Session.Id) if err != nil { c.Err = err return diff --git a/app/channel.go b/app/channel.go index f363f14cb0..72a79376c5 100644 --- a/app/channel.go +++ b/app/channel.go @@ -903,7 +903,7 @@ func (a *App) AddUserToChannel(user *model.User, channel *model.Channel) (*model return newMember, nil } -func (a *App) AddChannelMember(userId string, channel *model.Channel, userRequestorId string, postRootId string, clearPushNotifications bool) (*model.ChannelMember, *model.AppError) { +func (a *App) AddChannelMember(userId string, channel *model.Channel, userRequestorId string, postRootId string, currentSessionId string) (*model.ChannelMember, *model.AppError) { if result := <-a.Srv.Store.Channel().GetMember(channel.Id, userId); result.Err != nil { if result.Err.Id != store.MISSING_CHANNEL_MEMBER_ERROR { return nil, result.Err @@ -959,7 +959,7 @@ func (a *App) AddChannelMember(userId string, channel *model.Channel, userReques } if userRequestor != nil { - a.MarkChannelsAsViewed([]string{channel.Id}, userRequestor.Id, clearPushNotifications) + a.MarkChannelsAsViewed([]string{channel.Id}, userRequestor.Id, currentSessionId) } return cm, nil @@ -1747,10 +1747,10 @@ func (a *App) SearchChannelsUserNotIn(teamId string, userId string, term string) return result.Data.(*model.ChannelList), nil } -func (a *App) MarkChannelsAsViewed(channelIds []string, userId string, clearPushNotifications bool) (map[string]int64, *model.AppError) { +func (a *App) MarkChannelsAsViewed(channelIds []string, userId string, currentSessionId string) (map[string]int64, *model.AppError) { // I start looking for channels with notifications before I mark it as read, to clear the push notifications if needed channelsToClearPushNotifications := []string{} - if *a.Config().EmailSettings.SendPushNotifications && clearPushNotifications { + if *a.Config().EmailSettings.SendPushNotifications { for _, channelId := range channelIds { chanResult := <-a.Srv.Store.Channel().Get(channelId, true) if chanResult.Err != nil { @@ -1801,12 +1801,12 @@ func (a *App) MarkChannelsAsViewed(channelIds []string, userId string, clearPush } } for _, channelId := range channelsToClearPushNotifications { - a.ClearPushNotification(userId, channelId) + a.ClearPushNotification(currentSessionId, userId, channelId) } return times, nil } -func (a *App) ViewChannel(view *model.ChannelView, userId string, clearPushNotifications bool) (map[string]int64, *model.AppError) { +func (a *App) ViewChannel(view *model.ChannelView, userId string, currentSessionId string) (map[string]int64, *model.AppError) { if err := a.SetActiveChannel(userId, view.ChannelId); err != nil { return nil, err } @@ -1825,7 +1825,7 @@ func (a *App) ViewChannel(view *model.ChannelView, userId string, clearPushNotif return map[string]int64{}, nil } - return a.MarkChannelsAsViewed(channelIds, userId, clearPushNotifications) + return a.MarkChannelsAsViewed(channelIds, userId, currentSessionId) } func (a *App) PermanentDeleteChannel(channel *model.Channel) *model.AppError { diff --git a/app/channel_test.go b/app/channel_test.go index 1bbdac941e..9d0809eebe 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -438,7 +438,7 @@ func TestAddChannelMemberNoUserRequestor(t *testing.T) { channel := th.createChannel(th.BasicTeam, model.CHANNEL_OPEN) userRequestorId := "" postRootId := "" - if _, err := th.App.AddChannelMember(user.Id, channel, userRequestorId, postRootId, false); err != nil { + if _, err := th.App.AddChannelMember(user.Id, channel, userRequestorId, postRootId, ""); err != nil { t.Fatal("Failed to add user to channel. Error: " + err.Message) } @@ -738,7 +738,7 @@ func TestGetChannelMembersTimezones(t *testing.T) { userRequestorId := "" postRootId := "" - if _, err := th.App.AddChannelMember(th.BasicUser2.Id, th.BasicChannel, userRequestorId, postRootId, false); err != nil { + if _, err := th.App.AddChannelMember(th.BasicUser2.Id, th.BasicChannel, userRequestorId, postRootId, ""); err != nil { t.Fatal("Failed to add user to channel. Error: " + err.Message) } diff --git a/app/command_invite.go b/app/command_invite.go index c4f197374e..3183b3f52f 100644 --- a/app/command_invite.go +++ b/app/command_invite.go @@ -141,7 +141,7 @@ func (me *InviteProvider) DoCommand(a *App, args *model.CommandArgs, message str } } - if _, err := a.AddChannelMember(userProfile.Id, channelToJoin, args.Session.UserId, "", !args.Session.IsMobileApp()); err != nil { + if _, err := a.AddChannelMember(userProfile.Id, channelToJoin, args.Session.UserId, "", args.Session.Id); err != nil { return &model.CommandResponse{ Text: args.T("api.command_invite.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, diff --git a/app/integration_action_test.go b/app/integration_action_test.go index d08565a749..7b1be83ed7 100644 --- a/app/integration_action_test.go +++ b/app/integration_action_test.go @@ -55,7 +55,7 @@ func TestPostActionInvalidURL(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(&interactivePost, false) + post, err := th.App.CreatePostAsUser(&interactivePost, "") require.Nil(t, err) attachments, ok := post.Props["attachments"].([]*model.SlackAttachment) require.True(t, ok) @@ -122,7 +122,7 @@ func TestPostAction(t *testing.T) { }, } - post, err := th.App.CreatePostAsUser(&interactivePost, false) + post, err := th.App.CreatePostAsUser(&interactivePost, "") require.Nil(t, err) attachments, ok := post.Props["attachments"].([]*model.SlackAttachment) @@ -159,7 +159,7 @@ func TestPostAction(t *testing.T) { }, } - post2, err := th.App.CreatePostAsUser(&menuPost, false) + post2, err := th.App.CreatePostAsUser(&menuPost, "") require.Nil(t, err) attachments2, ok := post2.Props["attachments"].([]*model.SlackAttachment) @@ -217,7 +217,7 @@ func TestPostAction(t *testing.T) { }, } - postplugin, err := th.App.CreatePostAsUser(&interactivePostPlugin, false) + postplugin, err := th.App.CreatePostAsUser(&interactivePostPlugin, "") require.Nil(t, err) attachmentsPlugin, ok := postplugin.Props["attachments"].([]*model.SlackAttachment) @@ -258,7 +258,7 @@ func TestPostAction(t *testing.T) { }, } - postSiteURL, err := th.App.CreatePostAsUser(&interactivePostSiteURL, false) + postSiteURL, err := th.App.CreatePostAsUser(&interactivePostSiteURL, "") require.Nil(t, err) attachmentsSiteURL, ok := postSiteURL.Props["attachments"].([]*model.SlackAttachment) @@ -300,7 +300,7 @@ func TestPostAction(t *testing.T) { }, } - postSubpath, err := th.App.CreatePostAsUser(&interactivePostSubpath, false) + postSubpath, err := th.App.CreatePostAsUser(&interactivePostSubpath, "") require.Nil(t, err) attachmentsSubpath, ok := postSubpath.Props["attachments"].([]*model.SlackAttachment) diff --git a/app/notification_push.go b/app/notification_push.go index a80b6278c8..f4a3db7942 100644 --- a/app/notification_push.go +++ b/app/notification_push.go @@ -29,6 +29,7 @@ type PushNotificationsHub struct { type PushNotification struct { notificationType NotificationType + currentSessionId string userId string channelId string post *model.Post @@ -189,7 +190,7 @@ func (a *App) getPushNotificationMessage(postMessage string, explicitMention, ch return "@" + senderName + userLocale("api.post.send_notifications_and_forget.push_general_message") } -func (a *App) ClearPushNotificationSync(userId string, channelId string) { +func (a *App) ClearPushNotificationSync(currentSessionId, userId, channelId string) { sessions, err := a.getMobileAppSessions(userId) if err != nil { mlog.Error(err.Error()) @@ -207,19 +208,21 @@ func (a *App) ClearPushNotificationSync(userId string, channelId string) { msg.Badge = int(badge.Data.(int64)) } - mlog.Debug(fmt.Sprintf("Clearing push notification to %v with channel_id %v", msg.DeviceId, msg.ChannelId)) - for _, session := range sessions { - tmpMessage := *model.PushNotificationFromJson(strings.NewReader(msg.ToJson())) - tmpMessage.SetDeviceIdAndPlatform(session.DeviceId) - a.sendToPushProxy(tmpMessage, session) + if currentSessionId != session.Id { + tmpMessage := *model.PushNotificationFromJson(strings.NewReader(msg.ToJson())) + tmpMessage.SetDeviceIdAndPlatform(session.DeviceId) + mlog.Debug(fmt.Sprintf("Clearing push notification to %v with channel_id %v", session.DeviceId, msg.ChannelId)) + a.sendToPushProxy(tmpMessage, session) + } } } -func (a *App) ClearPushNotification(userId string, channelId string) { +func (a *App) ClearPushNotification(currentSessionId, userId, channelId string) { channel := a.Srv.PushNotificationsHub.GetGoChannelFromUserId(userId) channel <- PushNotification{ notificationType: NOTIFICATION_TYPE_CLEAR, + currentSessionId: currentSessionId, userId: userId, channelId: channelId, } @@ -239,7 +242,7 @@ func (a *App) pushNotificationWorker(notifications chan PushNotification) { for notification := range notifications { switch notification.notificationType { case NOTIFICATION_TYPE_CLEAR: - a.ClearPushNotificationSync(notification.userId, notification.channelId) + a.ClearPushNotificationSync(notification.currentSessionId, notification.userId, notification.channelId) case NOTIFICATION_TYPE_MESSAGE: a.sendPushNotificationSync( notification.post, diff --git a/app/plugin_api.go b/app/plugin_api.go index 6f9eaf4cf2..157c9a519b 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -376,7 +376,7 @@ func (api *PluginAPI) AddChannelMember(channelId, userId string) (*model.Channel return nil, err } - return api.app.AddChannelMember(userId, channel, userRequestorId, postRootId, false) + return api.app.AddChannelMember(userId, channel, userRequestorId, postRootId, "") } func (api *PluginAPI) GetChannelMember(channelId, userId string) (*model.ChannelMember, *model.AppError) { diff --git a/app/post.go b/app/post.go index b2d4594529..1f68843be7 100644 --- a/app/post.go +++ b/app/post.go @@ -22,7 +22,7 @@ const ( PENDING_POST_IDS_CACHE_TTL = 30 * time.Second ) -func (a *App) CreatePostAsUser(post *model.Post, clearPushNotifications bool) (*model.Post, *model.AppError) { +func (a *App) CreatePostAsUser(post *model.Post, currentSessionId string) (*model.Post, *model.AppError) { // Check that channel has not been deleted result := <-a.Srv.Store.Channel().Get(post.ChannelId, true) if result.Err != nil { @@ -74,7 +74,7 @@ func (a *App) CreatePostAsUser(post *model.Post, clearPushNotifications bool) (* // Update the LastViewAt only if the post does not have from_webhook prop set (eg. Zapier app) if _, ok := post.Props["from_webhook"]; !ok { - if _, err := a.MarkChannelsAsViewed([]string{post.ChannelId}, post.UserId, clearPushNotifications); err != nil { + if _, err := a.MarkChannelsAsViewed([]string{post.ChannelId}, post.UserId, currentSessionId); err != nil { mlog.Error(fmt.Sprintf("Encountered error updating last viewed, channel_id=%s, user_id=%s, err=%v", post.ChannelId, post.UserId, err)) } } diff --git a/app/post_test.go b/app/post_test.go index 7a54c46346..cbbd81756f 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -29,7 +29,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "message", PendingPostId: pendingPostId, - }, false) + }, "") require.Nil(t, err) require.Equal(t, "message", post.Message) @@ -38,7 +38,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "message", PendingPostId: pendingPostId, - }, false) + }, "") require.Nil(t, err) require.Equal(t, post.Id, duplicatePost.Id, "should have returned previously created post id") require.Equal(t, "message", duplicatePost.Message) @@ -78,7 +78,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "message", PendingPostId: pendingPostId, - }, false) + }, "") require.NotNil(t, err) require.Equal(t, "Post rejected by plugin. rejected", err.Id) require.Nil(t, post) @@ -88,7 +88,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "message", PendingPostId: pendingPostId, - }, false) + }, "") require.Nil(t, err) require.Equal(t, "message", duplicatePost.Message) }) @@ -138,7 +138,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "plugin delayed", PendingPostId: pendingPostId, - }, false) + }, "") require.Nil(t, err) require.Equal(t, post.Message, "plugin delayed") }() @@ -152,7 +152,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "plugin delayed", PendingPostId: pendingPostId, - }, false) + }, "") require.NotNil(t, err) require.Equal(t, "api.post.deduplicate_create_post.pending", err.Id) require.Nil(t, duplicatePost) @@ -168,7 +168,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "message", PendingPostId: pendingPostId, - }, false) + }, "") require.Nil(t, err) require.Equal(t, "message", post.Message) @@ -179,7 +179,7 @@ func TestCreatePostDeduplicate(t *testing.T) { ChannelId: th.BasicChannel.Id, Message: "message", PendingPostId: pendingPostId, - }, false) + }, "") require.Nil(t, err) require.NotEqual(t, post.Id, duplicatePost.Id, "should have created new post id") require.Equal(t, "message", duplicatePost.Message) @@ -350,7 +350,7 @@ func TestPostReplyToPostWhereRootPosterLeftChannel(t *testing.T) { CreateAt: 0, } - if _, err := th.App.CreatePostAsUser(&replyPost, false); err != nil { + if _, err := th.App.CreatePostAsUser(&replyPost, ""); err != nil { t.Fatal(err) } } @@ -373,7 +373,7 @@ func TestPostAttachPostToChildPost(t *testing.T) { CreateAt: 0, } - res1, err := th.App.CreatePostAsUser(&replyPost1, false) + res1, err := th.App.CreatePostAsUser(&replyPost1, "") if err != nil { t.Fatal(err) } @@ -388,7 +388,7 @@ func TestPostAttachPostToChildPost(t *testing.T) { CreateAt: 0, } - _, err = th.App.CreatePostAsUser(&replyPost2, false) + _, err = th.App.CreatePostAsUser(&replyPost2, "") if err.StatusCode != http.StatusBadRequest { t.Fatal(fmt.Sprintf("Expected BadRequest error, got %v", err)) } @@ -403,7 +403,7 @@ func TestPostAttachPostToChildPost(t *testing.T) { CreateAt: 0, } - if _, err := th.App.CreatePostAsUser(&replyPost3, false); err != nil { + if _, err := th.App.CreatePostAsUser(&replyPost3, ""); err != nil { t.Fatal(err) } } @@ -437,7 +437,7 @@ func TestPostChannelMentions(t *testing.T) { CreateAt: 0, } - result, err := th.App.CreatePostAsUser(post, false) + result, err := th.App.CreatePostAsUser(post, "") require.Nil(t, err) assert.Equal(t, map[string]interface{}{ "mention-test": map[string]interface{}{ diff --git a/app/syncables.go b/app/syncables.go index c386b7da9d..0d04ff6441 100644 --- a/app/syncables.go +++ b/app/syncables.go @@ -48,7 +48,7 @@ func (a *App) PopulateSyncablesSince(groupMembersCreatedAfter int64) error { mlog.String("team_id", channel.TeamId), ) - _, err = a.AddChannelMember(userChannel.UserID, channel, "", "", false) + _, err = a.AddChannelMember(userChannel.UserID, channel, "", "", "") if err != nil { return err } diff --git a/app/syncables_test.go b/app/syncables_test.go index 4734420ebd..bf2a676d2e 100644 --- a/app/syncables_test.go +++ b/app/syncables_test.go @@ -271,7 +271,7 @@ func TestPopulateSyncablesSince(t *testing.T) { } // Ensure members are in channel - _, err = th.App.AddChannelMember(scientist1.Id, experimentsChannel, "", "", false) + _, err = th.App.AddChannelMember(scientist1.Id, experimentsChannel, "", "", "") if err != nil { t.Errorf("unable to add user to channel: %s", err.Error()) } @@ -281,7 +281,7 @@ func TestPopulateSyncablesSince(t *testing.T) { if err != nil { t.Errorf("unable to add user to team: %s", err.Error()) } - _, err = th.App.AddChannelMember(singer1.Id, experimentsChannel, "", "", false) + _, err = th.App.AddChannelMember(singer1.Id, experimentsChannel, "", "", "") if err != nil { t.Errorf("unable to add user to channel: %s", err.Error()) } diff --git a/store/sqlstore/user_store.go b/store/sqlstore/user_store.go index ecd471fd9f..00fee28c41 100644 --- a/store/sqlstore/user_store.go +++ b/store/sqlstore/user_store.go @@ -1117,7 +1117,7 @@ func (us SqlUserStore) GetUnreadCount(userId string) store.StoreChannel { func (us SqlUserStore) GetUnreadCountForChannel(userId string, channelId string) store.StoreChannel { return store.Do(func(result *store.StoreResult) { - if count, err := us.GetReplica().SelectInt("SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE cm.MentionCount END) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = :ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { + if count, err := us.GetReplica().SelectInt("SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE cm.MentionCount END) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = cm.ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { result.Err = model.NewAppError("SqlUserStore.GetMentionCountForChannel", "store.sql_user.get_unread_count_for_channel.app_error", nil, err.Error(), http.StatusInternalServerError) } else { result.Data = count @@ -1127,7 +1127,7 @@ func (us SqlUserStore) GetUnreadCountForChannel(userId string, channelId string) func (us SqlUserStore) GetAnyUnreadPostCountForChannel(userId string, channelId string) store.StoreChannel { return store.Do(func(result *store.StoreResult) { - if count, err := us.GetReplica().SelectInt("SELECT SUM(c.TotalMsgCount - cm.MsgCount) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = :ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { + if count, err := us.GetReplica().SelectInt("SELECT SUM(c.TotalMsgCount - cm.MsgCount) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = cm.ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { result.Err = model.NewAppError("SqlUserStore.GetMentionCountForChannel", "store.sql_user.get_unread_count_for_channel.app_error", nil, err.Error(), http.StatusInternalServerError) } else { result.Data = count