mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-06-20 18:24:43 +00:00
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>
110 lines
7.3 KiB
Swift
110 lines
7.3 KiB
Swift
import Foundation
|
|
import Postbox
|
|
import SwiftSignalKit
|
|
import TelegramApi
|
|
import MtProtoKit
|
|
|
|
public enum UpdateChatRankError {
|
|
case generic
|
|
case changeForbidden
|
|
case chatAdminRequired
|
|
case chatCreatorRequired
|
|
case notParticipant
|
|
}
|
|
|
|
func _internal_updateChatRank(account: Account, peerId: PeerId, userId: PeerId, messageId: MessageId?, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> {
|
|
return account.postbox.transaction { transaction -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> in
|
|
if let user = transaction.getPeer(userId), let inputUser = apiInputPeer(user), let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
|
let currentParticipant: Signal<ChannelParticipant?, NoError>
|
|
if peerId.namespace == Namespaces.Peer.CloudChannel {
|
|
currentParticipant = _internal_fetchChannelParticipant(account: account, peerId: peerId, participantId: userId)
|
|
} else {
|
|
currentParticipant = .single(nil)
|
|
}
|
|
return currentParticipant
|
|
|> castError(UpdateChatRankError.self)
|
|
|> mapToSignal { currentParticipant in
|
|
return account.network.request(Api.functions.messages.editChatParticipantRank(peer: inputPeer, participant: inputUser, rank: rank ?? ""))
|
|
|> mapError { error -> UpdateChatRankError in
|
|
if error.errorDescription == "CHAT_ADMIN_REQUIRED" {
|
|
return .chatAdminRequired
|
|
} else if error.errorDescription == "CHAT_CREATOR_REQUIRED" {
|
|
return .chatCreatorRequired
|
|
} else if error.errorDescription == "USER_NOT_PARTICIPANT" {
|
|
return .notParticipant
|
|
} else if error.errorDescription == "RANK_CHANGE_FORBIDDEN" {
|
|
return .changeForbidden
|
|
} else {
|
|
return .generic
|
|
}
|
|
}
|
|
|> mapToSignal { updates -> Signal<(ChannelParticipant?, RenderedChannelParticipant)?, UpdateChatRankError> in
|
|
account.stateManager.addUpdates(updates)
|
|
|
|
return account.postbox.transaction { transaction -> (ChannelParticipant?, RenderedChannelParticipant)? in
|
|
if peerId.namespace == Namespaces.Peer.CloudGroup {
|
|
transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, current in
|
|
if let current = current as? CachedGroupData, let participants = current.participants {
|
|
var updatedParticipants = participants.participants
|
|
if let index = updatedParticipants.firstIndex(where: { $0.peerId == userId }) {
|
|
updatedParticipants[index] = updatedParticipants[index].withUpdated(rank: rank)
|
|
}
|
|
return current.withUpdatedParticipants(CachedGroupParticipants(participants: updatedParticipants, version: participants.version + 2))
|
|
} else {
|
|
return current
|
|
}
|
|
})
|
|
return nil
|
|
} else {
|
|
let updatedParticipant = currentParticipant?.withUpdated(rank: rank) ?? .member(id: userId, invitedAt: 0, adminInfo: nil, banInfo: nil, rank: rank, subscriptionUntilDate: nil)
|
|
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
|
var presences: [PeerId: PeerPresence] = [:]
|
|
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] = 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])))
|
|
var messageIds: [MessageId] = []
|
|
if let messageId {
|
|
messageIds.append(messageId)
|
|
}
|
|
for entry in historyView.entries {
|
|
if entry.message.id != messageId, let author = entry.message.author, author.id == userId {
|
|
messageIds.append(entry.message.id)
|
|
}
|
|
}
|
|
for messageId in messageIds {
|
|
transaction.updateMessage(messageId, update: { currentMessage in
|
|
var storeForwardInfo: StoreMessageForwardInfo?
|
|
if let forwardInfo = currentMessage.forwardInfo {
|
|
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
|
|
}
|
|
var attributes = currentMessage.attributes
|
|
attributes.removeAll(where: { $0 is ParticipantRankMessageAttribute })
|
|
if let rank, !rank.isEmpty {
|
|
attributes.append(ParticipantRankMessageAttribute(rank: rank))
|
|
}
|
|
return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
|
|
})
|
|
}
|
|
return (currentParticipant, RenderedChannelParticipant(participant: updatedParticipant, peer: EnginePeer(user), peers: peers, presences: presences))
|
|
}
|
|
}
|
|
|> castError(UpdateChatRankError.self)
|
|
}
|
|
}
|
|
} else {
|
|
return .fail(.generic)
|
|
}
|
|
}
|
|
|> mapError { _ -> UpdateChatRankError in }
|
|
|> switchToLatest
|
|
}
|
|
|
|
|