chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
573
internal/store/memory/channel_read.go
Normal file
573
internal/store/memory/channel_read.go
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func (s *ChannelStore) SetChannelDialogUnreadMark(_ context.Context, userID, channelID int64, unread bool) (bool, error) {
|
||||
if userID == 0 || channelID == 0 {
|
||||
return false, nil
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
channel, err := s.channelForMemberLocked(userID, channelID)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
dialog := s.dialogForUserLocked(userID, channel)
|
||||
changed := dialog.UnreadMark != unread
|
||||
dialog.UnreadMark = unread
|
||||
member := s.members[channelID][userID]
|
||||
member.UnreadMark = unread
|
||||
s.members[channelID][userID] = member
|
||||
if s.dialogs[userID] == nil {
|
||||
s.dialogs[userID] = make(map[int64]domain.ChannelDialog)
|
||||
}
|
||||
s.dialogs[userID][channelID] = dialog
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ListChannelUnreadMarked(_ context.Context, userID int64) ([]domain.Peer, error) {
|
||||
if userID == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
out := make([]domain.Peer, 0, len(s.dialogs[userID]))
|
||||
for channelID, dialog := range s.dialogs[userID] {
|
||||
if !dialog.UnreadMark {
|
||||
continue
|
||||
}
|
||||
if _, err := s.channelForMemberLocked(userID, channelID); err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, domain.Peer{Type: domain.PeerTypeChannel, ID: channelID})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ReadChannelMessageContents(_ context.Context, req domain.ReadChannelMessageContentsRequest) (domain.ReadChannelMessageContentsResult, error) {
|
||||
if req.UserID == 0 || req.ChannelID == 0 {
|
||||
return domain.ReadChannelMessageContentsResult{}, domain.ErrChannelInvalid
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
channel, member, err := s.channelAndMemberLocked(req.UserID, req.ChannelID)
|
||||
if err != nil {
|
||||
return domain.ReadChannelMessageContentsResult{}, err
|
||||
}
|
||||
if len(req.IDs) == 0 {
|
||||
return domain.ReadChannelMessageContentsResult{Channel: channel}, nil
|
||||
}
|
||||
if len(req.IDs) > domain.MaxGetMessageIDs {
|
||||
return domain.ReadChannelMessageContentsResult{}, domain.ErrChannelInvalid
|
||||
}
|
||||
wanted := make(map[int]struct{}, len(req.IDs))
|
||||
for _, id := range req.IDs {
|
||||
if id <= 0 || id > domain.MaxMessageBoxID {
|
||||
return domain.ReadChannelMessageContentsResult{}, domain.ErrMessageIDInvalid
|
||||
}
|
||||
wanted[id] = struct{}{}
|
||||
}
|
||||
messages := make([]domain.ChannelMessage, 0, len(wanted))
|
||||
for _, msg := range s.messages[req.ChannelID] {
|
||||
if _, ok := wanted[msg.ID]; !ok {
|
||||
continue
|
||||
}
|
||||
if msg.Deleted || msg.ID <= member.AvailableMinID {
|
||||
continue
|
||||
}
|
||||
messages = append(messages, cloneChannelMessage(msg))
|
||||
}
|
||||
sort.Slice(messages, func(i, j int) bool { return messages[i].ID > messages[j].ID })
|
||||
clearedSet := make(map[int]struct{})
|
||||
for _, msg := range messages {
|
||||
byUser := s.reactions[req.ChannelID][msg.ID]
|
||||
if len(byUser) == 0 {
|
||||
continue
|
||||
}
|
||||
for reactedUserID, rows := range byUser {
|
||||
changed := false
|
||||
for i := range rows {
|
||||
if rows[i].SenderUserID == req.UserID && rows[i].UserID != req.UserID && rows[i].Unread {
|
||||
rows[i].Unread = false
|
||||
changed = true
|
||||
clearedSet[msg.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
byUser[reactedUserID] = rows
|
||||
}
|
||||
}
|
||||
}
|
||||
cleared := make([]int, 0, len(clearedSet))
|
||||
for id := range clearedSet {
|
||||
cleared = append(cleared, id)
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.IntSlice(cleared)))
|
||||
if len(cleared) > 0 {
|
||||
s.refreshChannelUnreadReactionsDialogLocked(req.UserID, req.ChannelID)
|
||||
}
|
||||
// 视口内容已读同步翻转 mention 未读:客户端本地已减计数,服务端
|
||||
// 不落库角标会在下一次 getDialogs 复活。
|
||||
clearedMentions := make([]int, 0, len(messages))
|
||||
mentionFlipped := false
|
||||
for _, msg := range messages {
|
||||
mention, ok := s.mentions[req.UserID][req.ChannelID][msg.ID]
|
||||
if !ok || !mention.unread {
|
||||
continue
|
||||
}
|
||||
mention.unread = false
|
||||
s.mentions[req.UserID][req.ChannelID][msg.ID] = mention
|
||||
clearedMentions = append(clearedMentions, msg.ID)
|
||||
mentionFlipped = true
|
||||
}
|
||||
sort.Ints(clearedMentions)
|
||||
if mentionFlipped {
|
||||
if dialogs := s.dialogs[req.UserID]; dialogs != nil {
|
||||
dialog := dialogs[req.ChannelID]
|
||||
dialog.UserID = req.UserID
|
||||
dialog.ChannelID = req.ChannelID
|
||||
dialog.UnreadMentions = s.countChannelUnreadMentionsLocked(req.UserID, req.ChannelID, 0)
|
||||
dialogs[req.ChannelID] = dialog
|
||||
}
|
||||
}
|
||||
s.populateChannelMessageRepliesLocked(req.UserID, req.ChannelID, messages)
|
||||
s.populateChannelMessageReactionsLocked(req.UserID, channel, messages)
|
||||
return domain.ReadChannelMessageContentsResult{
|
||||
Channel: channel,
|
||||
Messages: messages,
|
||||
ClearedUnreadReactionMessageIDs: cleared,
|
||||
ClearedUnreadMentionMessageIDs: clearedMentions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ListChannelUnreadMentions(_ context.Context, viewerUserID int64, filter domain.ChannelUnreadMentionsFilter) (domain.ChannelHistory, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
channel, member, err := s.channelAndMemberLocked(viewerUserID, filter.ChannelID)
|
||||
if err != nil {
|
||||
return domain.ChannelHistory{}, err
|
||||
}
|
||||
limit := filter.Limit
|
||||
if limit <= 0 || limit > domain.MaxChannelUnreadMentionsLimit {
|
||||
limit = domain.MaxChannelUnreadMentionsLimit
|
||||
}
|
||||
filter.AddOffset = domain.ClampMessageHistoryAddOffset(filter.AddOffset)
|
||||
base := make([]domain.ChannelMessage, 0, limit)
|
||||
for msgID, mention := range s.mentions[viewerUserID][filter.ChannelID] {
|
||||
if !mention.unread {
|
||||
continue
|
||||
}
|
||||
if filter.TopMsgID > 0 && mention.topID != filter.TopMsgID && !(filter.TopMsgID == 1 && mention.topID == 0) {
|
||||
continue
|
||||
}
|
||||
msg, ok := s.findMessageLocked(filter.ChannelID, msgID)
|
||||
if !ok || msg.Deleted || msg.ID <= member.AvailableMinID {
|
||||
continue
|
||||
}
|
||||
if filter.MaxID > 0 && msg.ID >= filter.MaxID {
|
||||
continue
|
||||
}
|
||||
if filter.MinID > 0 && msg.ID <= filter.MinID {
|
||||
continue
|
||||
}
|
||||
base = append(base, msg)
|
||||
}
|
||||
sort.SliceStable(base, func(i, j int) bool { return channelMessageLess(base[i], base[j]) })
|
||||
page := pageChannelMessageHistory(base, domain.ChannelRepliesFilter{
|
||||
OffsetID: filter.OffsetID,
|
||||
OffsetDate: filter.OffsetDate,
|
||||
AddOffset: filter.AddOffset,
|
||||
Limit: limit,
|
||||
MaxID: filter.MaxID,
|
||||
MinID: filter.MinID,
|
||||
}, limit)
|
||||
out := make([]domain.ChannelMessage, 0, len(page))
|
||||
for _, msg := range page {
|
||||
out = append(out, cloneChannelMessage(msg))
|
||||
}
|
||||
s.populateChannelMessageRepliesLocked(viewerUserID, filter.ChannelID, out)
|
||||
s.populateChannelMessageReactionsLocked(viewerUserID, channel, out)
|
||||
return domain.ChannelHistory{Channel: channel, Messages: out, Count: len(base)}, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ReadChannelMentions(_ context.Context, req domain.ReadChannelMentionsRequest) (domain.ReadChannelMentionsResult, error) {
|
||||
if req.UserID == 0 || req.ChannelID == 0 {
|
||||
return domain.ReadChannelMentionsResult{}, domain.ErrChannelInvalid
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
channel, err := s.channelForMemberLocked(req.UserID, req.ChannelID)
|
||||
if err != nil {
|
||||
return domain.ReadChannelMentionsResult{}, err
|
||||
}
|
||||
limit := req.Limit
|
||||
if limit <= 0 || limit > domain.MaxChannelReadMentionsBatch {
|
||||
limit = domain.MaxChannelReadMentionsBatch
|
||||
}
|
||||
msgIDs := make([]int, 0, limit)
|
||||
for msgID, mention := range s.mentions[req.UserID][req.ChannelID] {
|
||||
if !mention.unread {
|
||||
continue
|
||||
}
|
||||
if req.TopMsgID > 0 && mention.topID != req.TopMsgID && !(req.TopMsgID == 1 && mention.topID == 0) {
|
||||
continue
|
||||
}
|
||||
msgIDs = append(msgIDs, msgID)
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.IntSlice(msgIDs)))
|
||||
if len(msgIDs) > limit {
|
||||
msgIDs = msgIDs[:limit]
|
||||
}
|
||||
for _, msgID := range msgIDs {
|
||||
mention := s.mentions[req.UserID][req.ChannelID][msgID]
|
||||
mention.unread = false
|
||||
s.mentions[req.UserID][req.ChannelID][msgID] = mention
|
||||
}
|
||||
remaining := s.countChannelUnreadMentionsLocked(req.UserID, req.ChannelID, req.TopMsgID)
|
||||
if dialogs := s.dialogs[req.UserID]; dialogs != nil {
|
||||
dialog := dialogs[req.ChannelID]
|
||||
dialog.UnreadMentions = s.countChannelUnreadMentionsLocked(req.UserID, req.ChannelID, 0)
|
||||
dialog.UserID = req.UserID
|
||||
dialog.ChannelID = req.ChannelID
|
||||
dialogs[req.ChannelID] = dialog
|
||||
}
|
||||
offset := 0
|
||||
if remaining > 0 {
|
||||
offset = 1
|
||||
}
|
||||
return domain.ReadChannelMentionsResult{
|
||||
Channel: channel,
|
||||
Cleared: len(msgIDs),
|
||||
Remaining: remaining,
|
||||
Offset: offset,
|
||||
ChannelPts: channel.Pts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ReadChannelHistory(_ context.Context, req domain.ReadChannelHistoryRequest) (domain.ReadChannelHistoryResult, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
channel, err := s.channelForMemberLocked(req.UserID, req.ChannelID)
|
||||
if err != nil {
|
||||
return domain.ReadChannelHistoryResult{}, err
|
||||
}
|
||||
maxID := req.MaxID
|
||||
if maxID <= 0 || maxID > channel.TopMessageID {
|
||||
maxID = channel.TopMessageID
|
||||
}
|
||||
member := s.members[req.ChannelID][req.UserID]
|
||||
previous := member.ReadInboxMaxID
|
||||
changed := maxID > member.ReadInboxMaxID
|
||||
var outboxUpdates []domain.ChannelReadOutboxUpdate
|
||||
if changed {
|
||||
member.ReadInboxMaxID = maxID
|
||||
member.ReadInboxDate = req.Date
|
||||
member.UnreadMark = false
|
||||
s.members[req.ChannelID][req.UserID] = member
|
||||
s.readMarks[req.ChannelID] = s.readMarks[req.ChannelID].advance(req.UserID, maxID)
|
||||
outboxUpdates = s.advanceChannelReadOutboxLocked(req.ChannelID, req.UserID, previous, maxID)
|
||||
}
|
||||
dialog := s.dialogForUserLocked(req.UserID, channel)
|
||||
dialog.ReadInboxMaxID = member.ReadInboxMaxID
|
||||
dialog.UnreadCount = s.channelUnreadCountLocked(req.UserID, channel.ID, member.ReadInboxMaxID, dialog.TopMessageID)
|
||||
dialog.UnreadMark = false
|
||||
if s.dialogs[req.UserID] == nil {
|
||||
s.dialogs[req.UserID] = make(map[int64]domain.ChannelDialog)
|
||||
}
|
||||
s.dialogs[req.UserID][req.ChannelID] = dialog
|
||||
return domain.ReadChannelHistoryResult{
|
||||
ChannelID: req.ChannelID,
|
||||
MaxID: maxID,
|
||||
StillUnreadCount: dialog.UnreadCount,
|
||||
Changed: changed,
|
||||
Pts: channel.Pts,
|
||||
Forum: channel.Forum,
|
||||
Dialog: dialog,
|
||||
OutboxUpdates: outboxUpdates,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) advanceChannelReadOutboxLocked(channelID, readerUserID int64, previous, maxID int) []domain.ChannelReadOutboxUpdate {
|
||||
if maxID <= previous {
|
||||
return nil
|
||||
}
|
||||
lowerID := previous
|
||||
if maxID-lowerID > domain.MaxChannelReadOutboxScanMessages {
|
||||
lowerID = maxID - domain.MaxChannelReadOutboxScanMessages
|
||||
}
|
||||
bySender := make(map[int64]int, domain.MaxChannelReadOutboxFanout)
|
||||
messages := s.messages[channelID]
|
||||
for i := len(messages) - 1; i >= 0; i-- {
|
||||
msg := messages[i]
|
||||
if msg.ID <= lowerID {
|
||||
break
|
||||
}
|
||||
if msg.ID > maxID || msg.Deleted || msg.SenderUserID == 0 || msg.SenderUserID == readerUserID {
|
||||
continue
|
||||
}
|
||||
if _, ok := bySender[msg.SenderUserID]; ok {
|
||||
continue
|
||||
}
|
||||
bySender[msg.SenderUserID] = msg.ID
|
||||
if len(bySender) >= domain.MaxChannelReadOutboxFanout {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(bySender) == 0 {
|
||||
return nil
|
||||
}
|
||||
senderIDs := make([]int64, 0, len(bySender))
|
||||
for userID := range bySender {
|
||||
senderIDs = append(senderIDs, userID)
|
||||
}
|
||||
sort.Slice(senderIDs, func(i, j int) bool { return senderIDs[i] < senderIDs[j] })
|
||||
channel := s.channels[channelID]
|
||||
out := make([]domain.ChannelReadOutboxUpdate, 0, len(senderIDs))
|
||||
for _, userID := range senderIDs {
|
||||
maxForSender := bySender[userID]
|
||||
member, ok := s.members[channelID][userID]
|
||||
if !ok || member.Status != domain.ChannelMemberActive || maxForSender <= member.ReadOutboxMaxID {
|
||||
continue
|
||||
}
|
||||
member.ReadOutboxMaxID = maxForSender
|
||||
s.members[channelID][userID] = member
|
||||
dialog := s.dialogForUserLocked(userID, channel)
|
||||
if dialog.ReadOutboxMaxID < maxForSender {
|
||||
dialog.ReadOutboxMaxID = maxForSender
|
||||
}
|
||||
if s.dialogs[userID] == nil {
|
||||
s.dialogs[userID] = make(map[int64]domain.ChannelDialog)
|
||||
}
|
||||
s.dialogs[userID][channelID] = dialog
|
||||
out = append(out, domain.ChannelReadOutboxUpdate{UserID: userID, MaxID: maxForSender})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *ChannelStore) ListMessageReadParticipants(_ context.Context, req domain.ChannelReadParticipantsRequest) (domain.ChannelReadParticipantsResult, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
channel, err := s.channelForMemberLocked(req.UserID, req.ChannelID)
|
||||
if err != nil {
|
||||
return domain.ChannelReadParticipantsResult{}, err
|
||||
}
|
||||
member := s.members[req.ChannelID][req.UserID]
|
||||
msg, found := s.findMessageLocked(req.ChannelID, req.MessageID)
|
||||
if !found || msg.Deleted || msg.ID <= member.AvailableMinID {
|
||||
return domain.ChannelReadParticipantsResult{}, domain.ErrMessageIDInvalid
|
||||
}
|
||||
result := domain.ChannelReadParticipantsResult{
|
||||
Channel: channel,
|
||||
Message: cloneChannelMessage(msg),
|
||||
}
|
||||
if !channel.Megagroup || channel.ParticipantsHidden || channel.ParticipantsCount > domain.MaxChannelReadParticipants {
|
||||
return result, nil
|
||||
}
|
||||
now := req.Date
|
||||
if now > 0 && msg.Date+domain.ChannelReadMarkExpirePeriod <= now {
|
||||
return result, nil
|
||||
}
|
||||
limit := req.Limit
|
||||
if limit <= 0 || limit > domain.MaxChannelReadParticipants {
|
||||
limit = domain.MaxChannelReadParticipants
|
||||
}
|
||||
for _, reader := range s.members[req.ChannelID] {
|
||||
if reader.UserID == req.UserID || reader.Status != domain.ChannelMemberActive || reader.BannedRights.ViewMessages {
|
||||
continue
|
||||
}
|
||||
if reader.ReadInboxDate <= 0 {
|
||||
continue
|
||||
}
|
||||
if reader.AvailableMinID >= req.MessageID || reader.ReadInboxMaxID < req.MessageID {
|
||||
continue
|
||||
}
|
||||
result.Participants = append(result.Participants, domain.ChannelReadParticipant{
|
||||
UserID: reader.UserID,
|
||||
Date: reader.ReadInboxDate,
|
||||
})
|
||||
if len(result.Participants) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
sort.Slice(result.Participants, func(i, j int) bool {
|
||||
if result.Participants[i].Date == result.Participants[j].Date {
|
||||
return result.Participants[i].UserID < result.Participants[j].UserID
|
||||
}
|
||||
return result.Participants[i].Date < result.Participants[j].Date
|
||||
})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *ChannelStore) channelThreadUnreadCountLocked(viewerUserID, channelID int64, rootID, readMaxID int) int {
|
||||
unread := 0
|
||||
for _, msg := range s.messages[channelID] {
|
||||
if msg.Deleted || msg.ID <= readMaxID || msg.SenderUserID == viewerUserID {
|
||||
continue
|
||||
}
|
||||
if channelReplyBelongsToRoot(msg, channelID, rootID) {
|
||||
unread++
|
||||
// 钳到 MaxDialogUnreadCount(P1-v),与 postgres 的 LIMIT 子查询同 min(actual,cap) 语义。
|
||||
if unread >= domain.MaxDialogUnreadCount {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return unread
|
||||
}
|
||||
|
||||
func (s *ChannelStore) channelUnreadCountLocked(viewerUserID, channelID int64, readMaxID, topID int) int {
|
||||
if viewerUserID == 0 || channelID == 0 || topID <= readMaxID {
|
||||
return 0
|
||||
}
|
||||
unread := 0
|
||||
for _, msg := range s.messages[channelID] {
|
||||
if msg.Deleted || msg.ID <= readMaxID || msg.ID > topID || msg.SenderUserID == viewerUserID {
|
||||
continue
|
||||
}
|
||||
unread++
|
||||
// 钳到 MaxDialogUnreadCount(P1-v),与 postgres 的 LIMIT 子查询同 min(actual,cap) 语义。
|
||||
if unread >= domain.MaxDialogUnreadCount {
|
||||
break
|
||||
}
|
||||
}
|
||||
return unread
|
||||
}
|
||||
|
||||
func (s *ChannelStore) addChannelUnreadMentionsLocked(channelID int64, msg domain.ChannelMessage, senderUserID int64, userIDs []int64) {
|
||||
if len(userIDs) == 0 || msg.ID == 0 {
|
||||
return
|
||||
}
|
||||
seen := make(map[int64]struct{}, len(userIDs))
|
||||
written := 0
|
||||
topID := channelMentionTopID(msg)
|
||||
for _, userID := range userIDs {
|
||||
if userID == 0 || userID == senderUserID {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[userID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[userID] = struct{}{}
|
||||
member, ok := s.members[channelID][userID]
|
||||
if !ok || member.Status != domain.ChannelMemberActive || member.BannedRights.ViewMessages {
|
||||
continue
|
||||
}
|
||||
if msg.ID <= member.AvailableMinID || msg.ID <= member.ReadInboxMaxID {
|
||||
continue
|
||||
}
|
||||
if s.mentions[userID] == nil {
|
||||
s.mentions[userID] = make(map[int64]map[int]memoryMention)
|
||||
}
|
||||
if s.mentions[userID][channelID] == nil {
|
||||
s.mentions[userID][channelID] = make(map[int]memoryMention)
|
||||
}
|
||||
s.mentions[userID][channelID][msg.ID] = memoryMention{topID: topID, unread: true}
|
||||
written++
|
||||
if written == domain.MaxChannelMentionRecipients {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ChannelStore) countChannelUnreadMentionsLocked(userID, channelID int64, topMsgID int) int {
|
||||
count := 0
|
||||
for _, mention := range s.mentions[userID][channelID] {
|
||||
if !mention.unread {
|
||||
continue
|
||||
}
|
||||
if topMsgID == 0 || mention.topID == topMsgID || (topMsgID == 1 && mention.topID == 0) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (s *ChannelStore) deleteChannelUnreadMentionsLocked(channelID int64, ids []int) {
|
||||
if len(ids) == 0 {
|
||||
return
|
||||
}
|
||||
set := make(map[int]struct{}, len(ids))
|
||||
for _, id := range ids {
|
||||
set[id] = struct{}{}
|
||||
}
|
||||
for userID, byChannel := range s.mentions {
|
||||
mentions := byChannel[channelID]
|
||||
if len(mentions) == 0 {
|
||||
continue
|
||||
}
|
||||
for id := range set {
|
||||
delete(mentions, id)
|
||||
}
|
||||
if len(mentions) == 0 {
|
||||
delete(byChannel, channelID)
|
||||
}
|
||||
if len(byChannel) == 0 {
|
||||
delete(s.mentions, userID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ChannelStore) deleteChannelUnreadMentionsUpToLocked(userID, channelID int64, maxID int) {
|
||||
if maxID <= 0 || len(s.mentions[userID][channelID]) == 0 {
|
||||
return
|
||||
}
|
||||
for id := range s.mentions[userID][channelID] {
|
||||
if id <= maxID {
|
||||
delete(s.mentions[userID][channelID], id)
|
||||
}
|
||||
}
|
||||
if len(s.mentions[userID][channelID]) == 0 {
|
||||
delete(s.mentions[userID], channelID)
|
||||
}
|
||||
if len(s.mentions[userID]) == 0 {
|
||||
delete(s.mentions, userID)
|
||||
}
|
||||
}
|
||||
|
||||
func channelMentionTopID(msg domain.ChannelMessage) int {
|
||||
if msg.ReplyTo == nil {
|
||||
return 0
|
||||
}
|
||||
if msg.ReplyTo.TopMessageID > 0 {
|
||||
return msg.ReplyTo.TopMessageID
|
||||
}
|
||||
return msg.ReplyTo.MessageID
|
||||
}
|
||||
|
||||
func (s *ChannelStore) populateChannelMessageUnreadFlagsLocked(viewerUserID int64, messages []domain.ChannelMessage) {
|
||||
if viewerUserID == 0 || len(messages) == 0 {
|
||||
return
|
||||
}
|
||||
for i := range messages {
|
||||
if messages[i].ChannelID == 0 || messages[i].ID <= 0 {
|
||||
continue
|
||||
}
|
||||
mention, ok := s.mentions[viewerUserID][messages[i].ChannelID][messages[i].ID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
messages[i].Mentioned = true
|
||||
messages[i].MediaUnread = mention.unread
|
||||
}
|
||||
}
|
||||
|
||||
// clearChannelMentionsForUserLocked 在离开/被踢时清空该用户的提及状态。
|
||||
func (s *ChannelStore) clearChannelMentionsForUserLocked(channelID, userID int64) {
|
||||
if byChannel := s.mentions[userID]; byChannel != nil {
|
||||
delete(byChannel, channelID)
|
||||
if len(byChannel) == 0 {
|
||||
delete(s.mentions, userID)
|
||||
}
|
||||
}
|
||||
if dialogs := s.dialogs[userID]; dialogs != nil {
|
||||
if dialog, ok := dialogs[channelID]; ok {
|
||||
dialog.UnreadMentions = 0
|
||||
dialogs[channelID] = dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue