fix: align private and channel update semantics

(cherry picked from commit c65f76f56278f74082c4fa792ed49104d5d33c38)
This commit is contained in:
A 2026-06-07 22:22:23 +08:00
parent dce7b92772
commit d84fa6e126
36 changed files with 1765 additions and 382 deletions

View file

@ -1375,6 +1375,17 @@ func (s *Service) ActiveChannelIDsForUser(ctx context.Context, userID, afterChan
return s.channels.ListActiveChannelIDsForUser(ctx, userID, afterChannelID, limit)
}
// DirtyActiveChannelsForUser pages active joined channels with channel events after sinceDate.
func (s *Service) DirtyActiveChannelsForUser(ctx context.Context, userID int64, sinceDate int, afterChannelID int64, limit int) ([]domain.DirtyChannel, error) {
if s == nil || s.channels == nil || userID == 0 || sinceDate <= 0 || afterChannelID < 0 {
return nil, domain.ErrChannelInvalid
}
if limit <= 0 || limit > domain.MaxChannelDifferenceLimit {
limit = domain.MaxChannelDifferenceLimit
}
return s.channels.ListDirtyActiveChannelsForUser(ctx, userID, sinceDate, afterChannelID, limit)
}
// ActiveMemberIDs returns a bounded list for transient online fanout such as typing.
func (s *Service) ActiveMemberIDs(ctx context.Context, userID, channelID int64, limit int) ([]int64, error) {
if s == nil || s.channels == nil || userID == 0 || channelID == 0 {

View file

@ -113,6 +113,7 @@ func TestChannelUnreadMentionsArePagedAndCleared(t *testing.T) {
ChannelID: created.Channel.ID,
RandomID: 9101,
Message: "hello @friend",
Media: &domain.MessageMedia{Kind: domain.MessageMediaKindDocument},
MentionUserIDs: []int64{1002, 1002, 1001},
Date: 1700000101,
})
@ -133,6 +134,31 @@ func TestChannelUnreadMentionsArePagedAndCleared(t *testing.T) {
if other.Dialog.UnreadMentions != 0 {
t.Fatalf("unmentioned dialog unread mentions = %d, want 0", other.Dialog.UnreadMentions)
}
history, err := service.GetHistory(ctx, 1002, domain.ChannelHistoryFilter{ChannelID: created.Channel.ID, Limit: 10})
if err != nil {
t.Fatalf("GetHistory mentioned: %v", err)
}
if len(history.Messages) == 0 || !history.Messages[0].Mentioned || !history.Messages[0].MediaUnread {
t.Fatalf("mentioned history = %+v, want mentioned/media_unread flags", history.Messages)
}
otherHistory, err := service.GetHistory(ctx, 1003, domain.ChannelHistoryFilter{ChannelID: created.Channel.ID, Limit: 10})
if err != nil {
t.Fatalf("GetHistory other: %v", err)
}
if len(otherHistory.Messages) == 0 || otherHistory.Messages[0].Mentioned || otherHistory.Messages[0].MediaUnread {
t.Fatalf("other history = %+v, want no viewer-specific mention flags", otherHistory.Messages)
}
diff, err := service.GetDifference(ctx, 1002, domain.ChannelDifferenceRequest{
ChannelID: created.Channel.ID,
Pts: sent.Event.Pts - 1,
Limit: 10,
})
if err != nil {
t.Fatalf("GetDifference mentioned: %v", err)
}
if len(diff.NewMessages) != 1 || !diff.NewMessages[0].Mentioned || !diff.NewMessages[0].MediaUnread {
t.Fatalf("mentioned diff = %+v, want mentioned/media_unread flags", diff.NewMessages)
}
mentions, err := service.GetUnreadMentions(ctx, 1002, domain.ChannelUnreadMentionsFilter{
ChannelID: created.Channel.ID,
OffsetID: 1,
@ -159,6 +185,13 @@ func TestChannelUnreadMentionsArePagedAndCleared(t *testing.T) {
if mentions.Count != 0 || len(mentions.Messages) != 0 {
t.Fatalf("mentions after read = count %d messages %d, want empty", mentions.Count, len(mentions.Messages))
}
history, err = service.GetHistory(ctx, 1002, domain.ChannelHistoryFilter{ChannelID: created.Channel.ID, Limit: 10})
if err != nil {
t.Fatalf("GetHistory after read mentions: %v", err)
}
if len(history.Messages) == 0 || history.Messages[0].Mentioned || history.Messages[0].MediaUnread {
t.Fatalf("mentioned history after read = %+v, want mention flags cleared", history.Messages)
}
}
func TestServiceRejectsMismatchedUserContextForStateReads(t *testing.T) {

View file

@ -229,13 +229,59 @@ func (s *Service) GetPeerSettings(ctx context.Context, userID int64, peer domain
if err != nil {
return domain.PeerSettings{}, err
}
blocked, err := s.contacts.IsBlocked(ctx, userID, peer.ID)
if err != nil {
return domain.PeerSettings{}, err
}
return domain.PeerSettings{
AddContact: !found,
BlockContact: !found,
BlockContact: !blocked,
ShareContact: found,
}, nil
}
// BlockContact adds peer to the current user's blocklist.
func (s *Service) BlockContact(ctx context.Context, userID, peerUserID int64, date int) (bool, error) {
if s == nil || s.contacts == nil || userID == 0 || peerUserID == 0 || peerUserID == userID {
return false, ErrContactIDInvalid
}
if s.users != nil {
if _, found, err := s.users.ByID(ctx, peerUserID); err != nil {
return false, err
} else if !found {
return false, ErrContactIDInvalid
}
}
return s.contacts.Block(ctx, userID, peerUserID, date)
}
// UnblockContact removes peer from the current user's blocklist.
func (s *Service) UnblockContact(ctx context.Context, userID, peerUserID int64) (bool, error) {
if s == nil || s.contacts == nil || userID == 0 || peerUserID == 0 || peerUserID == userID {
return false, ErrContactIDInvalid
}
return s.contacts.Unblock(ctx, userID, peerUserID)
}
// IsBlocked reports whether owner has blocked peer.
func (s *Service) IsBlocked(ctx context.Context, userID, peerUserID int64) (bool, error) {
if s == nil || s.contacts == nil || userID == 0 || peerUserID == 0 {
return false, nil
}
return s.contacts.IsBlocked(ctx, userID, peerUserID)
}
// GetBlocked returns a bounded blocked contact page.
func (s *Service) GetBlocked(ctx context.Context, userID int64, offset, limit int) (domain.BlockedContactList, error) {
if s == nil || s.contacts == nil || userID == 0 {
return domain.BlockedContactList{}, nil
}
if limit <= 0 || limit > 100 {
limit = 100
}
return s.contacts.ListBlocked(ctx, userID, offset, limit)
}
func (s *Service) ContactIDs(ctx context.Context, userID int64, hash int64) ([]int, bool, error) {
list, notModified, err := s.GetContacts(ctx, userID, hash)
if err != nil || notModified {