From b07a2fbb770e8e675052f006c319081a90471c80 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 24 Feb 2026 03:03:45 +0400 Subject: [PATCH] Various improvements --- .../Sources/ChannelAdminsController.swift | 32 +++++++---- .../Sources/ChannelMembersController.swift | 57 +++++++++---------- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift index 565b3e0766..d969182e81 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import SwiftSignalKit +import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences @@ -860,22 +861,27 @@ public func channelAdminsController(context: AccountContext, updatedPresentation return context.engine.data.subscribe( TelegramEngine.EngineData.Item.Peer.LegacyGroupParticipants(id: peerId) ) - |> mapToSignal { participants -> Signal<[(EngineLegacyGroupParticipant, EnginePeer?)]?, NoError> in + |> mapToSignal { participants -> Signal<[(EngineLegacyGroupParticipant, EnginePeer?, EnginePeer.Presence?)]?, NoError> in guard case let .known(participants) = participants else { return .single(nil) } return context.engine.data.subscribe( - EngineDataMap(participants.map { TelegramEngine.EngineData.Item.Peer.Peer(id: $0.peerId) }) + EngineDataMap(participants.map { TelegramEngine.EngineData.Item.Peer.Peer(id: $0.peerId) }), + EngineDataMap(participants.map { TelegramEngine.EngineData.Item.Peer.Presence(id: $0.peerId) }) ) - |> map { peers -> [(EngineLegacyGroupParticipant, EnginePeer?)]? in - var result: [(EngineLegacyGroupParticipant, EnginePeer?)] = [] + |> map { peers, presences -> [(EngineLegacyGroupParticipant, EnginePeer?, EnginePeer.Presence?)]? in + var result: [(EngineLegacyGroupParticipant, EnginePeer?, EnginePeer.Presence?)] = [] for participant in participants { var peer: EnginePeer? + var presence: EnginePeer.Presence? if let peerValue = peers[participant.peerId] { peer = peerValue } - result.append((participant, peer)) + if let presenceValue = presences[participant.peerId] { + presence = presenceValue + } + result.append((participant, peer, presence)) } return result } @@ -887,7 +893,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation var result: [RenderedChannelParticipant] = [] var creatorPeer: EnginePeer? - for (participant, peer) in participants { + for (participant, peer, _) in participants { if let peer { switch participant { case .creator: @@ -900,16 +906,20 @@ public func channelAdminsController(context: AccountContext, updatedPresentation guard let creator = creatorPeer else { return nil } - for (participant, peer) in participants { + for (participant, peer, presence) in participants { if let peer { + var presences: [PeerId: PeerPresence] = [:] + if let presence { + presences[peer.id] = presence._asPresence() + } switch participant { - case .creator: - result.append(RenderedChannelParticipant(participant: .creator(id: peer.id, adminInfo: nil, rank: nil), peer: peer._asPeer())) - case .admin: + case let .creator(_, rank): + result.append(RenderedChannelParticipant(participant: .creator(id: peer.id, adminInfo: nil, rank: rank), peer: peer._asPeer(), presences: presences)) + case let .admin(_, _, _, rank): 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: nil, subscriptionUntilDate: nil), peer: peer._asPeer(), peers: peers.mapValues({ $0._asPeer() }))) + 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._asPeer(), peers: peers.mapValues({ $0._asPeer() }), presences: presences)) case .member: break } diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift index 5d95c3fafd..07afff8d60 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift @@ -23,7 +23,7 @@ private final class ChannelMembersControllerArguments { let addMember: () -> Void let setPeerIdWithRevealedOptions: (EnginePeer.Id?, EnginePeer.Id?) -> Void let removePeer: (EnginePeer.Id) -> Void - let openParticipant: (RenderedChannelParticipant) -> Void + let openParticipant: (RenderedChannelParticipant, Bool) -> Void let inviteViaLink: () -> Void let updateHideMembers: (Bool) -> Void let displayHideMembersTip: (HideMembersDisabledReason) -> Void @@ -33,7 +33,7 @@ private final class ChannelMembersControllerArguments { addMember: @escaping () -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, - openParticipant: @escaping (RenderedChannelParticipant) -> Void, + openParticipant: @escaping (RenderedChannelParticipant, Bool) -> Void, inviteViaLink: @escaping () -> Void, updateHideMembers: @escaping (Bool) -> Void, displayHideMembersTip: @escaping (HideMembersDisabledReason) -> Void @@ -74,7 +74,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry { case inviteLink(PresentationTheme, String) case contactsTitle(PresentationTheme, String) case peersTitle(PresentationTheme, String) - case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool) + case peerItem(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, RenderedChannelParticipant, ItemListPeerItemEditing, Bool, Bool, Bool) var section: ItemListSectionId { switch self { @@ -86,7 +86,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry { return ChannelMembersSection.contacts.rawValue case .peersTitle: return ChannelMembersSection.peers.rawValue - case let .peerItem(_, _, _, _, _, _, _, _, isContact): + case let .peerItem(_, _, _, _, _, _, _, _, isContact, _): return isContact ? ChannelMembersSection.contacts.rawValue : ChannelMembersSection.peers.rawValue } } @@ -107,7 +107,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry { return .index(7) case .peersTitle: return .index(8) - case let .peerItem(_, _, _, _, _, participant, _, _, _): + case let .peerItem(_, _, _, _, _, participant, _, _, _, _): return .peer(participant.peer.id) } } @@ -156,8 +156,8 @@ private enum ChannelMembersEntry: ItemListNodeEntry { } else { return false } - case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled, lhsIsContact): - if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled, rhsIsContact) = rhs { + case let .peerItem(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsParticipant, lhsEditing, lhsEnabled, lhsIsContact, lhsIsGroup): + if case let .peerItem(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsParticipant, rhsEditing, rhsEnabled, rhsIsContact, rhsIsGroup) = rhs { if lhsIndex != rhsIndex { return false } @@ -185,6 +185,9 @@ private enum ChannelMembersEntry: ItemListNodeEntry { if lhsIsContact != rhsIsContact { return false } + if lhsIsGroup != rhsIsGroup { + return false + } return true } else { return false @@ -240,18 +243,18 @@ private enum ChannelMembersEntry: ItemListNodeEntry { switch rhs { case .hideMembers, .hideMembersInfo, .addMember, .addMemberInfo, .inviteLink, .contactsTitle: return false - case let .peerItem(_, _, _, _, _, _, _, _, isContact): + case let .peerItem(_, _, _, _, _, _, _, _, isContact, _): return !isContact default: return true } - case let .peerItem(lhsIndex, _, _, _, _, _, _, _, lhsIsContact): + case let .peerItem(lhsIndex, _, _, _, _, _, _, _, lhsIsContact, _): switch rhs { case .contactsTitle: return false case .peersTitle: return lhsIsContact - case let .peerItem(rhsIndex, _, _, _, _, _, _, _, _): + case let .peerItem(rhsIndex, _, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex case .hideMembers, .hideMembersInfo, .addMember, .addMemberInfo, .inviteLink: return false @@ -288,7 +291,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry { return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .contactsTitle(_, text), let .peersTitle(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) - case let .peerItem(_, _, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled, _): + case let .peerItem(_, _, strings, dateTimeFormat, nameDisplayOrder, participant, editing, enabled, _, isGroup): let text: ItemListPeerItemText if let user = participant.peer as? TelegramUser, let _ = user.botInfo { text = .text(strings.Bot_GenericBotStatus, .secondary) @@ -319,7 +322,7 @@ private enum ChannelMembersEntry: ItemListNodeEntry { } return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(participant.peer), presence: participant.presences[participant.peer.id].flatMap(EnginePeer.Presence.init), text: text, label: label, editing: editing, switchValue: nil, enabled: enabled, selectable: participant.peer.id != arguments.context.account.peerId, sectionId: self.section, action: { - arguments.openParticipant(participant) + arguments.openParticipant(participant, isGroup) }, setPeerIdWithRevealedOptions: { previousId, id in arguments.setPeerIdWithRevealedOptions(previousId, id) }, removePeer: { peerId in @@ -480,7 +483,7 @@ private func channelMembersControllerEntries(context: AccountContext, presentati editable = canEditMembers } } - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, true)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, true, isGroup)) existingPeerIds.insert(participant.peer.id) index += 1 } @@ -508,7 +511,7 @@ private func channelMembersControllerEntries(context: AccountContext, presentati editable = canEditMembers } } - entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, false)) + entries.append(.peerItem(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, participant, ItemListPeerItemEditing(editable: editable, editing: state.editing, revealed: participant.peer.id == state.peerIdWithRevealedOptions), state.removingPeerId != participant.peer.id, false, isGroup)) index += 1 } } @@ -680,10 +683,16 @@ public func channelMembersController(context: AccountContext, updatedPresentatio return $0.withUpdatedRemovingPeerId(nil) } })) - }, openParticipant: { participant in - let controller = channelBannedMemberController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, memberId: participant.peer.id, editMember: true, initialParticipant: participant.participant, updated: { rights in - }, upgradedToSupergroup: { _, _ in }) - pushControllerImpl?(controller) + }, openParticipant: { participant, isGroup in + if isGroup { + let controller = channelBannedMemberController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, memberId: participant.peer.id, editMember: true, initialParticipant: participant.participant, updated: { rights in + }, upgradedToSupergroup: { _, _ in }) + pushControllerImpl?(controller) + } else { + if let infoController = context.sharedContext.makePeerInfoController(context: context, updatedPresentationData: nil, peer: participant.peer, mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + pushControllerImpl?(infoController) + } + } }, inviteViaLink: { if let controller = getControllerImpl?() { dismissInputImpl?() @@ -789,17 +798,7 @@ public func channelMembersController(context: AccountContext, updatedPresentatio } } - var title: String = isGroup ? presentationData.strings.Group_Members_Title : presentationData.strings.Channel_Subscribers_Title - if let cachedData = view.cachedData as? CachedGroupData { - if let count = cachedData.participants?.participants.count { - title = presentationData.strings.GroupInfo_TitleMembers(Int32(count)) - } - } else if let cachedData = view.cachedData as? CachedChannelData { - if let count = cachedData.participantsSummary.memberCount { - title = presentationData.strings.GroupInfo_TitleMembers(count) - } - } - + let title: String = isGroup ? presentationData.strings.Group_Members_Title : presentationData.strings.Channel_Subscribers_Title let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelMembersControllerEntries(context: context, presentationData: presentationData, view: view, state: state, contacts: contacts, participants: peers, isGroup: isGroup), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: animateChanges)