136 lines
4.3 KiB
Go
136 lines
4.3 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
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
|
|
}
|
|
key := fmt.Sprintf("%d:%d", userID, folderID)
|
|
value, err, _ := r.dialogsPinnedSF.Do(key, func() (any, error) {
|
|
if r.deps.Dialogs == nil {
|
|
return domain.DialogList{}, nil
|
|
}
|
|
return r.deps.Dialogs.GetDialogs(ctx, userID, domain.DialogFilter{
|
|
PinnedOnly: true,
|
|
HasFolderID: true,
|
|
FolderID: folderID,
|
|
Limit: 100,
|
|
})
|
|
})
|
|
if err != nil {
|
|
return domain.DialogList{}, err
|
|
}
|
|
if list, ok := value.(domain.DialogList); ok {
|
|
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.
|
|
func combinedPinnedDialogPeers(list domain.DialogList) []domain.Peer {
|
|
type item struct {
|
|
peer domain.Peer
|
|
order int
|
|
sequence int
|
|
}
|
|
items := make([]item, 0, len(list.Dialogs)+len(list.Communities))
|
|
seen := make(map[domain.Peer]struct{}, cap(items))
|
|
appendItem := func(peer domain.Peer, pinned bool, order int) {
|
|
if !pinned || peer.ID == 0 {
|
|
return
|
|
}
|
|
if _, ok := seen[peer]; ok {
|
|
return
|
|
}
|
|
seen[peer] = struct{}{}
|
|
items = append(items, item{peer: peer, order: order, sequence: len(items)})
|
|
}
|
|
for _, dialog := range list.Dialogs {
|
|
appendItem(dialog.Peer, dialog.Pinned, dialog.PinnedOrder)
|
|
}
|
|
for _, community := range list.Communities {
|
|
appendItem(domain.Peer{Type: domain.PeerTypeCommunity, ID: community.Community.ID}, community.State.Pinned, community.State.PinnedOrder)
|
|
}
|
|
sort.SliceStable(items, func(i, j int) bool {
|
|
if items[i].order != items[j].order {
|
|
return items[i].order > items[j].order
|
|
}
|
|
return items[i].sequence < items[j].sequence
|
|
})
|
|
out := make([]domain.Peer, 0, len(items))
|
|
for _, item := range items {
|
|
out = append(out, item.peer)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (r *Router) ensureCombinedPinCapacity(ctx context.Context, userID int64, folderID int, peer domain.Peer) error {
|
|
list, err := r.combinedPinnedDialogsList(ctx, userID, folderID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
peers := combinedPinnedDialogPeers(list)
|
|
for _, pinned := range peers {
|
|
if pinned == peer {
|
|
return nil
|
|
}
|
|
}
|
|
if len(peers) >= domain.PinnedDialogsLimit(folderID, r.userIsPremium(ctx, userID)) {
|
|
return domain.ErrPinnedDialogsTooMuch
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// promoteCombinedPinnedDialog assigns one collision-free order across ordinary
|
|
// 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.combinedPinnedDialogsList(ctx, userID, folderID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
current := combinedPinnedDialogPeers(list)
|
|
order := make([]domain.Peer, 0, len(current)+1)
|
|
order = append(order, peer)
|
|
for _, candidate := range current {
|
|
if candidate != peer {
|
|
order = append(order, candidate)
|
|
}
|
|
}
|
|
if r.deps.Dialogs != nil {
|
|
if _, err := r.deps.Dialogs.ReorderPinned(ctx, userID, folderID, order, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if folderID == domain.DialogMainFolderID && r.deps.Communities != nil {
|
|
if _, err := r.deps.Communities.ReorderPinned(ctx, userID, order, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|