fix: sync legacy community projection
Sync telesrv 07644e0 (fix(communities): hide aggregates from legacy layers).
This commit is contained in:
parent
ba82fbccd6
commit
08c763b683
3 changed files with 54 additions and 6 deletions
|
|
@ -9,6 +9,8 @@ import (
|
|||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
const communitiesLayer = 228
|
||||
|
||||
func communityErr(err error) error {
|
||||
switch {
|
||||
case err == nil:
|
||||
|
|
@ -160,6 +162,17 @@ func (r *Router) communityMutationUpdates(ctx context.Context, userID int64, vie
|
|||
}
|
||||
|
||||
func (r *Router) withCommunityDialogList(ctx context.Context, userID int64, filter domain.DialogFilter, list domain.DialogList) (domain.DialogList, error) {
|
||||
if LayerFrom(ctx) < communitiesLayer {
|
||||
return list, nil
|
||||
}
|
||||
return r.withCollapsedCommunityDialogs(ctx, userID, filter, list)
|
||||
}
|
||||
|
||||
// withCollapsedCommunityDialogs applies the account-level Community dialog
|
||||
// state without a wire-layer visibility decision. Business invariants such as
|
||||
// the shared pinned limit use this path; RPC response construction must use
|
||||
// withCommunityDialogList instead.
|
||||
func (r *Router) withCollapsedCommunityDialogs(ctx context.Context, userID int64, filter domain.DialogFilter, list domain.DialogList) (domain.DialogList, error) {
|
||||
if r.deps.Communities == nil || (filter.HasFolderID && filter.FolderID != domain.DialogMainFolderID) {
|
||||
return list, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func communityRPCChannel(t *testing.T, service *appchannels.Service, creator dom
|
|||
}
|
||||
|
||||
func TestCommunityDialogsSharePinnedLimit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx := WithLayer(context.Background(), communitiesLayer)
|
||||
users := memory.NewUserStore()
|
||||
owner, err := users.Create(ctx, domain.User{AccessHash: 711, Phone: "15552000011", FirstName: "Pin Owner"})
|
||||
if err != nil {
|
||||
|
|
@ -102,6 +102,21 @@ func TestCommunityDialogsSharePinnedLimit(t *testing.T) {
|
|||
if len(order) != domain.MaxPinnedDialogsMainFolder || order[0] != (domain.Peer{Type: domain.PeerTypeChannel, ID: ordinary.ID}) {
|
||||
t.Fatalf("combined pinned order = %+v (dialogs=%+v communities=%+v count=%d), want ordinary dialog promoted above Communities", order, pinned.Dialogs, pinned.Communities, pinned.Count)
|
||||
}
|
||||
legacyPinned, err := r.pinnedDialogsList(WithLayer(ctx, 227), owner.ID, domain.DialogMainFolderID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(legacyPinned.Communities) != 0 || len(legacyPinned.Dialogs) != 1 || legacyPinned.Count != 1 {
|
||||
t.Fatalf("Layer 227 pinned dialogs = %+v, want only the ordinary pinned dialog", legacyPinned)
|
||||
}
|
||||
legacyOrdinary := communityRPCChannel(t, channelService, owner, "Legacy Ordinary Pinned Channel")
|
||||
legacyToggle := &tg.MessagesToggleDialogPinRequest{Peer: &tg.InputDialogPeer{Peer: &tg.InputPeerChannel{
|
||||
ChannelID: legacyOrdinary.ID, AccessHash: legacyOrdinary.AccessHash,
|
||||
}}}
|
||||
legacyToggle.SetPinned(true)
|
||||
if ok, err := r.onMessagesToggleDialogPin(WithLayer(WithUserID(ctx, owner.ID), 227), legacyToggle); err == nil || ok || !tgerr.Is(err, "PINNED_DIALOGS_TOO_MUCH") {
|
||||
t.Fatalf("Layer 227 pin beyond shared account limit = %v, %v", ok, err)
|
||||
}
|
||||
overLimit := &tg.MessagesToggleDialogPinRequest{Peer: &tg.InputDialogPeerCommunity{Community: inputs[len(inputs)-1]}}
|
||||
overLimit.SetPinned(true)
|
||||
if ok, err := r.onMessagesToggleDialogPin(WithUserID(ctx, owner.ID), overLimit); err == nil || ok || !tgerr.Is(err, "PINNED_DIALOGS_TOO_MUCH") {
|
||||
|
|
@ -116,7 +131,7 @@ func TestCommunityDialogsSharePinnedLimit(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCommunitiesRPCLayer228Lifecycle(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx := WithLayer(context.Background(), communitiesLayer)
|
||||
userStore := memory.NewUserStore()
|
||||
owner, _ := userStore.Create(ctx, domain.User{AccessHash: 701, Phone: "15552000001", FirstName: "Owner"})
|
||||
member, _ := userStore.Create(ctx, domain.User{AccessHash: 702, Phone: "15552000002", FirstName: "Member"})
|
||||
|
|
@ -186,8 +201,12 @@ func TestCommunitiesRPCLayer228Lifecycle(t *testing.T) {
|
|||
if len(collapsed.Chats) == 0 || !collapsed.Chats[0].(*tg.Community).CollapsedInDialogs {
|
||||
t.Fatalf("collapsed updates = %+v", collapsed.Chats)
|
||||
}
|
||||
legacyList, err := r.withCommunityDialogList(WithLayer(ctx, 227), owner.ID, domain.DialogFilter{}, domain.DialogList{Count: 7})
|
||||
if err != nil || len(legacyList.Communities) != 0 || legacyList.Count != 7 {
|
||||
t.Fatalf("Layer 227 community dialog projection = %+v err=%v, want unchanged list", legacyList, err)
|
||||
}
|
||||
list, err := r.withCommunityDialogList(ctx, owner.ID, domain.DialogFilter{}, domain.DialogList{})
|
||||
if err != nil || len(list.Communities) != 1 {
|
||||
if err != nil || len(list.Communities) != 1 || list.Count != 1 {
|
||||
t.Fatalf("community dialog list = %+v err=%v", list, err)
|
||||
}
|
||||
dialogs := tgMessagesDialogs(owner.ID, list).(*tg.MessagesDialogs)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,14 @@ import (
|
|||
)
|
||||
|
||||
func (r *Router) pinnedDialogsList(ctx context.Context, userID int64, folderID int) (domain.DialogList, error) {
|
||||
list, err := r.pinnedDialogsBaseList(ctx, userID, folderID)
|
||||
if err != nil {
|
||||
return domain.DialogList{}, err
|
||||
}
|
||||
return r.withCommunityDialogList(ctx, userID, domain.DialogFilter{PinnedOnly: true, HasFolderID: true, FolderID: folderID}, list)
|
||||
}
|
||||
|
||||
func (r *Router) pinnedDialogsBaseList(ctx context.Context, userID int64, folderID int) (domain.DialogList, error) {
|
||||
if r == nil {
|
||||
return domain.DialogList{}, nil
|
||||
}
|
||||
|
|
@ -28,11 +36,19 @@ func (r *Router) pinnedDialogsList(ctx context.Context, userID int64, folderID i
|
|||
return domain.DialogList{}, err
|
||||
}
|
||||
if list, ok := value.(domain.DialogList); ok {
|
||||
return r.withCommunityDialogList(ctx, userID, domain.DialogFilter{PinnedOnly: true, HasFolderID: true, FolderID: folderID}, list)
|
||||
return list, nil
|
||||
}
|
||||
return domain.DialogList{}, nil
|
||||
}
|
||||
|
||||
func (r *Router) combinedPinnedDialogsList(ctx context.Context, userID int64, folderID int) (domain.DialogList, error) {
|
||||
list, err := r.pinnedDialogsBaseList(ctx, userID, folderID)
|
||||
if err != nil {
|
||||
return domain.DialogList{}, err
|
||||
}
|
||||
return r.withCollapsedCommunityDialogs(ctx, userID, domain.DialogFilter{PinnedOnly: true, HasFolderID: true, FolderID: folderID}, list)
|
||||
}
|
||||
|
||||
// combinedPinnedDialogPeers merges ordinary dialogs and collapsed Communities
|
||||
// by their shared server order. The two persistence implementations deliberately
|
||||
// store their own rows, but Layer 228 exposes one messages.getPinnedDialogs list.
|
||||
|
|
@ -74,7 +90,7 @@ func combinedPinnedDialogPeers(list domain.DialogList) []domain.Peer {
|
|||
}
|
||||
|
||||
func (r *Router) ensureCombinedPinCapacity(ctx context.Context, userID int64, folderID int, peer domain.Peer) error {
|
||||
list, err := r.pinnedDialogsList(ctx, userID, folderID)
|
||||
list, err := r.combinedPinnedDialogsList(ctx, userID, folderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -94,7 +110,7 @@ func (r *Router) ensureCombinedPinCapacity(ctx context.Context, userID int64, fo
|
|||
// dialogs and Communities. It is called after the underlying row is pinned so
|
||||
// both stores can project the same mixed order without owning each other's data.
|
||||
func (r *Router) promoteCombinedPinnedDialog(ctx context.Context, userID int64, folderID int, peer domain.Peer) error {
|
||||
list, err := r.pinnedDialogsList(ctx, userID, folderID)
|
||||
list, err := r.combinedPinnedDialogsList(ctx, userID, folderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue