Postbox -> TelegramEngine wave 44

Migrate RenderedChannelParticipant.peers from [PeerId: Peer] to
[EnginePeer.Id: EnginePeer]. Closes the wave-41 ratchet — the public
struct no longer leaks raw Peer types in any field (presences stays
Postbox-typed; separate migration).

Consumer-surface: -10 bridges. Dropped 6 EnginePeer(peer) read-wraps
at participant.peers[...] extraction sites across
ChannelAdminsController, ChannelMembersSearchContainerNode,
ChannelBlacklistController. Dropped 5 .mapValues({ $0._asPeer() })
constructor-unwrap transforms in ChannelAdminsController,
ChannelMembersSearchContainerNode, ChannelMembersSearchControllerNode.
Added 2 ._asPeer() unwraps in ChatRecentActionsHistoryTransition at
the two iteration sites (line 673 via participant.peers, line 2273
via new.peers in participantSubscriptionExtended) where the iterated
value is inserted into a raw-Peer SimpleDictionary.

TelegramCore producers: 8 files build the local peers dict inside
postbox.transaction and wrap at the insertion point. ChannelMembers,
RequestStartBot, ChannelOwnershipTransfer, JoinChannel, AddPeerMember,
PeerAdmins, ChannelBlacklist, Ranks.

2-iteration build convergence. Iteration-1 surfaced new.peers at
ChatRecentActionsHistoryTransition:2272 that the plan's participant.peers
pre-flight grep missed; wider grep now confirms the two iteration sites
are the complete surface.

No unit tests in this project; full Telegram/Telegram build verified
under configuration=debug_sim_arm64.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
isaac 2026-04-24 23:08:33 +04:00
parent 68a95f53f2
commit c0acc09cb1
14 changed files with 38 additions and 38 deletions

View file

@ -294,7 +294,7 @@ private enum ChannelAdminsEntry: ItemListNodeEntry {
if peer.id == participant.peer.id {
peerText = strings.Channel_Management_LabelAdministrator
} else {
peerText = strings.Channel_Management_PromotedBy(EnginePeer(peer).displayTitle(strings: strings, displayOrder: nameDisplayOrder)).string
peerText = strings.Channel_Management_PromotedBy(peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)).string
}
} else {
peerText = ""
@ -923,7 +923,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
var peers: [EnginePeer.Id: EnginePeer] = [:]
peers[creator.id] = creator
peers[peer.id] = peer
result.append(RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .internal_groupSpecific), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: rank, subscriptionUntilDate: nil), peer: peer, peers: peers.mapValues({ $0._asPeer() }), presences: presences))
result.append(RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: .internal_groupSpecific), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: rank, subscriptionUntilDate: nil), peer: peer, peers: peers, presences: presences))
case .member:
break
}

View file

@ -162,7 +162,7 @@ private enum ChannelBlacklistEntry: ItemListNodeEntry {
switch participant.participant {
case let .member(_, _, _, banInfo, _, _):
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
text = .text(strings.Channel_Management_RemovedBy(EnginePeer(peer).displayTitle(strings: strings, displayOrder: nameDisplayOrder)).string, .secondary)
text = .text(strings.Channel_Management_RemovedBy(peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)).string, .secondary)
}
default:
break

View file

@ -836,7 +836,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
if peer.id == participant.peer.id {
label = presentationData.strings.Channel_Management_LabelAdministrator
} else {
label = presentationData.strings.Channel_Management_PromotedBy(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
label = presentationData.strings.Channel_Management_PromotedBy(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
}
}
}
@ -867,7 +867,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
switch participant.participant {
case let .member(_, _, _, banInfo, _, _):
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
label = presentationData.strings.Channel_Management_RemovedBy(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
label = presentationData.strings.Channel_Management_RemovedBy(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
}
default:
break
@ -991,11 +991,11 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
peers[creator.id] = creator
}
peers[peer.id] = EnginePeer(peer)
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: .legacyGroup(group))), promotedBy: creatorPeer?.id ?? context.account.peerId, canBeEditedByAccountPeer: creatorPeer?.id == context.account.peerId), banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers.mapValues({ $0._asPeer() }))
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: .legacyGroup(group))), promotedBy: creatorPeer?.id ?? context.account.peerId, canBeEditedByAccountPeer: creatorPeer?.id == context.account.peerId), banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers)
case .member:
var peers: [EnginePeer.Id: EnginePeer] = [:]
peers[peer.id] = EnginePeer(peer)
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers.mapValues({ $0._asPeer() }))
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers)
}
matchingMembers.append(renderedParticipant)
}
@ -1088,7 +1088,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
if peer.id == participant.peer.id {
label = presentationData.strings.Channel_Management_LabelAdministrator
} else {
label = presentationData.strings.Channel_Management_PromotedBy(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
label = presentationData.strings.Channel_Management_PromotedBy(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
}
}
}
@ -1119,7 +1119,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon
switch participant.participant {
case let .member(_, _, _, banInfo, _, _):
if let banInfo = banInfo, let peer = participant.peers[banInfo.restrictedBy] {
label = presentationData.strings.Channel_Management_RemovedBy(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
label = presentationData.strings.Channel_Management_RemovedBy(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
}
default:
break

View file

@ -406,11 +406,11 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
var peers: [EnginePeer.Id: EnginePeer] = [:]
peers[creator.id] = creator
peers[peer.id] = EnginePeer(peer)
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(mainPeer))), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers.mapValues({ $0._asPeer() }), presences: peerView.peerPresences)
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(rights: TelegramChatAdminRightsFlags.peerSpecific(peer: EnginePeer(mainPeer))), promotedBy: creator.id, canBeEditedByAccountPeer: creator.id == context.account.peerId), banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers, presences: peerView.peerPresences)
case .member:
var peers: [EnginePeer.Id: EnginePeer] = [:]
peers[peer.id] = EnginePeer(peer)
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers.mapValues({ $0._asPeer() }), presences: peerView.peerPresences)
renderedParticipant = RenderedChannelParticipant(participant: .member(id: peer.id, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil, subscriptionUntilDate: nil), peer: EnginePeer(peer), peers: peers, presences: peerView.peerPresences)
}
entries.append(.peer(index, renderedParticipant, ContactsPeerItemEditing(editable: false, editing: false, revealed: false), label, enabled, false, false))

View file

@ -58,10 +58,10 @@ func _internal_requestStartBotInGroup(account: Account, botPeerId: PeerId, group
|> mapToSignal { participant -> Signal<StartBotInGroupResult, RequestStartBotInGroupError> in
return account.postbox.transaction { transaction -> StartBotInGroupResult in
if let participant = participant, let peer = transaction.getPeer(participant.peerId) {
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
let presences: [PeerId: PeerPresence] = [:]
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
return .channelParticipant(RenderedChannelParticipant(participant: participant, peer: EnginePeer(peer), peers: peers, presences: presences))
} else {
return .none

View file

@ -239,16 +239,16 @@ func _internal_addChannelMember(account: Account, peerId: PeerId, memberId: Peer
return cachedData
}
})
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
var presences: [PeerId: PeerPresence] = [:]
peers[memberPeer.id] = memberPeer
peers[memberPeer.id] = EnginePeer(memberPeer)
if let presence = transaction.getPeerPresence(peerId: memberPeer.id) {
presences[memberPeer.id] = presence
}
if case let .member(_, _, maybeAdminInfo, _, _, _) = updatedParticipant {
if let adminInfo = maybeAdminInfo {
if let peer = transaction.getPeer(adminInfo.promotedBy) {
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
}
}
}

View file

@ -125,15 +125,15 @@ func _internal_updateChannelMemberBannedRights(account: Account, peerId: PeerId,
return cachedData
}
})
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
var presences: [PeerId: PeerPresence] = [:]
peers[memberPeer.id] = memberPeer
peers[memberPeer.id] = EnginePeer(memberPeer)
if let presence = transaction.getPeerPresence(peerId: memberPeer.id) {
presences[memberPeer.id] = presence
}
if case let .member(_, _, _, maybeBanInfo, _, _) = updatedParticipant, let banInfo = maybeBanInfo {
if let peer = transaction.getPeer(banInfo.restrictedBy) {
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
}
}

View file

@ -99,10 +99,10 @@ func _internal_channelMembers(postbox: Postbox, network: Network, accountPeerId:
let (participants, chats, users) = (channelParticipantsData.participants, channelParticipantsData.chats, channelParticipantsData.users)
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
for id in parsedPeers.allIds {
if let peer = transaction.getPeer(id) {
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
}
}

View file

@ -167,13 +167,13 @@ func _internal_updateChatOwnership(account: Account, peerId: PeerId, memberId: P
return cachedData
}
})
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
var presences: [PeerId: PeerPresence] = [:]
peers[accountUser.id] = accountUser
peers[accountUser.id] = EnginePeer(accountUser)
if let presence = transaction.getPeerPresence(peerId: accountUser.id) {
presences[accountUser.id] = presence
}
peers[user.id] = user
peers[user.id] = EnginePeer(user)
if let presence = transaction.getPeerPresence(peerId: user.id) {
presences[user.id] = presence
}

View file

@ -8,10 +8,10 @@ import MtProtoKit
public struct RenderedChannelParticipant: Equatable {
public let participant: ChannelParticipant
public let peer: EnginePeer
public let peers: [PeerId: Peer]
public let peers: [EnginePeer.Id: EnginePeer]
public let presences: [PeerId: PeerPresence]
public init(participant: ChannelParticipant, peer: EnginePeer, peers: [PeerId: Peer] = [:], presences: [PeerId: PeerPresence] = [:]) {
public init(participant: ChannelParticipant, peer: EnginePeer, peers: [EnginePeer.Id: EnginePeer] = [:], presences: [PeerId: PeerPresence] = [:]) {
self.participant = participant
self.peer = peer
self.peers = peers

View file

@ -56,12 +56,12 @@ func _internal_joinChannel(account: Account, peerId: PeerId, hash: String?) -> S
return .fail(.generic)
}
return account.postbox.transaction { transaction -> RenderedChannelParticipant? in
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
var presences: [PeerId: PeerPresence] = [:]
guard let peer = transaction.getPeer(account.peerId) else {
return nil
}
peers[account.peerId] = peer
peers[account.peerId] = EnginePeer(peer)
if let presence = transaction.getPeerPresence(peerId: account.peerId) {
presences[account.peerId] = presence
}
@ -74,7 +74,7 @@ func _internal_joinChannel(account: Account, peerId: PeerId, hash: String?) -> S
if case let .member(_, _, maybeAdminInfo, _, _, _) = updatedParticipant {
if let adminInfo = maybeAdminInfo {
if let peer = transaction.getPeer(adminInfo.promotedBy) {
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
}
}
}

View file

@ -248,15 +248,15 @@ func _internal_updateChannelAdminRights(account: Account, peerId: PeerId, adminI
return cachedData
}
})
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
var presences: [PeerId: PeerPresence] = [:]
peers[adminPeer.id] = adminPeer
peers[adminPeer.id] = EnginePeer(adminPeer)
if let presence = transaction.getPeerPresence(peerId: adminPeer.id) {
presences[adminPeer.id] = presence
}
if case let .member(_, _, maybeAdminInfo, _, _, _) = updatedParticipant, let adminInfo = maybeAdminInfo {
if let peer = transaction.getPeer(adminInfo.promotedBy) {
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
}
}
return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: EnginePeer(adminPeer), peers: peers, presences: presences))

View file

@ -57,15 +57,15 @@ func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId,
return nil
} else {
let updatedParticipant = currentParticipant?.withUpdated(rank: rank) ?? .member(id: userId, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: rank, subscriptionUntilDate: nil)
var peers: [PeerId: Peer] = [:]
var peers: [EnginePeer.Id: EnginePeer] = [:]
var presences: [PeerId: PeerPresence] = [:]
peers[user.id] = user
peers[user.id] = EnginePeer(user)
if let presence = transaction.getPeerPresence(peerId: user.id) {
presences[user.id] = presence
}
if case let .member(_, _, maybeAdminInfo, _, _, _) = updatedParticipant, let adminInfo = maybeAdminInfo {
if let peer = transaction.getPeer(adminInfo.promotedBy) {
peers[peer.id] = peer
peers[peer.id] = EnginePeer(peer)
}
}
let historyView = transaction.getMessagesHistoryViewState(input: .single(peerId: peerId, threadId: nil), ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 50, clipHoles: true, anchor: .upperBound, namespaces: .just(Set([Namespaces.Message.Cloud])))

View file

@ -670,7 +670,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
}
peers[peer.id] = peer
for (_, peer) in participant.peers {
peers[peer.id] = peer
peers[peer.id] = peer._asPeer()
}
peers[participant.peer.id] = participant.peer._asPeer()
@ -2270,7 +2270,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
}
peers[peer.id] = peer
for (_, peer) in new.peers {
peers[peer.id] = peer
peers[peer.id] = peer._asPeer()
}
peers[new.peer.id] = new.peer._asPeer()