196 lines
5.2 KiB
Go
196 lines
5.2 KiB
Go
package tdesktop
|
|
|
|
import "telesrv/internal/domain"
|
|
|
|
// ShouldMergePinnedIntoInitialDialogs reports whether a TDesktop main-list
|
|
// messages.getDialogs request should carry pinned/archive rows inline.
|
|
//
|
|
// TDesktop starts the main list with getDialogs(exclude_pinned=true) and loads
|
|
// pinned rows through getPinnedDialogs in parallel. If the exclude_pinned result
|
|
// is rendered first, the first visible list temporarily lacks pinned/archive
|
|
// rows. Keep the workaround scoped to the first main-folder page with no hash so
|
|
// pagination and cache probes retain normal Telegram semantics.
|
|
func ShouldMergePinnedIntoInitialDialogs(filter domain.DialogFilter) bool {
|
|
if !filter.ExcludePinned || filter.Hash != 0 {
|
|
return false
|
|
}
|
|
if filter.HasFolderID && filter.FolderID != domain.DialogMainFolderID {
|
|
return false
|
|
}
|
|
if filter.OffsetID != 0 || filter.OffsetDate != 0 || filter.HasOffsetPeer {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// MergeInitialDialogsWithPinned prepends the pinned getPinnedDialogs view to an
|
|
// initial getDialogs(exclude_pinned=true) main-list response. The returned list
|
|
// remains a normal messages.dialogs/messages.dialogsSlice projection; only the
|
|
// first response carries extra pinned/archive rows for TDesktop startup.
|
|
func MergeInitialDialogsWithPinned(main, pinned domain.DialogList) domain.DialogList {
|
|
if pinned.ArchiveSummary == nil && len(pinned.Dialogs) == 0 {
|
|
return main
|
|
}
|
|
out := main
|
|
out.ArchiveSummary = pinned.ArchiveSummary
|
|
out.Dialogs = mergeDialogsByPeer(pinned.Dialogs, main.Dialogs)
|
|
out.Messages = mergePrivateMessages(pinned.Messages, main.Messages)
|
|
out.ChannelMessages = mergeChannelMessages(pinned.ChannelMessages, main.ChannelMessages)
|
|
out.Users = mergeUsersByID(pinned.Users, main.Users)
|
|
out.Channels = mergeChannelsByID(pinned.Channels, main.Channels)
|
|
pinnedEntries := len(pinned.Dialogs)
|
|
if pinned.ArchiveSummary != nil {
|
|
pinnedEntries++
|
|
}
|
|
mainCount := main.Count
|
|
if mainCount == 0 {
|
|
mainCount = len(main.Dialogs)
|
|
}
|
|
out.Count = mainCount + pinnedEntries
|
|
out.Hash = 0
|
|
return out
|
|
}
|
|
|
|
func mergeDialogsByPeer(first, second []domain.Dialog) []domain.Dialog {
|
|
out := make([]domain.Dialog, 0, len(first)+len(second))
|
|
seen := make(map[domain.Peer]struct{}, len(first)+len(second))
|
|
for _, d := range first {
|
|
if d.Peer.ID == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[d.Peer]; ok {
|
|
continue
|
|
}
|
|
seen[d.Peer] = struct{}{}
|
|
out = append(out, d)
|
|
}
|
|
for _, d := range second {
|
|
if d.Peer.ID == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[d.Peer]; ok {
|
|
continue
|
|
}
|
|
seen[d.Peer] = struct{}{}
|
|
out = append(out, d)
|
|
}
|
|
return out
|
|
}
|
|
|
|
type privateMessageKey struct {
|
|
peer domain.Peer
|
|
id int
|
|
}
|
|
|
|
func mergePrivateMessages(first, second []domain.Message) []domain.Message {
|
|
out := make([]domain.Message, 0, len(first)+len(second))
|
|
seen := make(map[privateMessageKey]struct{}, len(first)+len(second))
|
|
for _, msg := range first {
|
|
key := privateMessageKey{peer: msg.Peer, id: msg.ID}
|
|
if key.peer.ID == 0 || key.id == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[key]; ok {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
out = append(out, msg)
|
|
}
|
|
for _, msg := range second {
|
|
key := privateMessageKey{peer: msg.Peer, id: msg.ID}
|
|
if key.peer.ID == 0 || key.id == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[key]; ok {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
out = append(out, msg)
|
|
}
|
|
return out
|
|
}
|
|
|
|
type channelMessageKey struct {
|
|
channelID int64
|
|
id int
|
|
}
|
|
|
|
func mergeChannelMessages(first, second []domain.ChannelMessage) []domain.ChannelMessage {
|
|
out := make([]domain.ChannelMessage, 0, len(first)+len(second))
|
|
seen := make(map[channelMessageKey]struct{}, len(first)+len(second))
|
|
for _, msg := range first {
|
|
key := channelMessageKey{channelID: msg.ChannelID, id: msg.ID}
|
|
if key.channelID == 0 || key.id == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[key]; ok {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
out = append(out, msg)
|
|
}
|
|
for _, msg := range second {
|
|
key := channelMessageKey{channelID: msg.ChannelID, id: msg.ID}
|
|
if key.channelID == 0 || key.id == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[key]; ok {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
out = append(out, msg)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func mergeUsersByID(first, second []domain.User) []domain.User {
|
|
out := make([]domain.User, 0, len(first)+len(second))
|
|
seen := make(map[int64]struct{}, len(first)+len(second))
|
|
for _, user := range first {
|
|
if user.ID == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[user.ID]; ok {
|
|
continue
|
|
}
|
|
seen[user.ID] = struct{}{}
|
|
out = append(out, user)
|
|
}
|
|
for _, user := range second {
|
|
if user.ID == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[user.ID]; ok {
|
|
continue
|
|
}
|
|
seen[user.ID] = struct{}{}
|
|
out = append(out, user)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func mergeChannelsByID(first, second []domain.Channel) []domain.Channel {
|
|
out := make([]domain.Channel, 0, len(first)+len(second))
|
|
seen := make(map[int64]struct{}, len(first)+len(second))
|
|
for _, channel := range first {
|
|
if channel.ID == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[channel.ID]; ok {
|
|
continue
|
|
}
|
|
seen[channel.ID] = struct{}{}
|
|
out = append(out, channel)
|
|
}
|
|
for _, channel := range second {
|
|
if channel.ID == 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[channel.ID]; ok {
|
|
continue
|
|
}
|
|
seen[channel.ID] = struct{}{}
|
|
out = append(out, channel)
|
|
}
|
|
return out
|
|
}
|