From c85315e756f386c30f3885c5798e69de62415f41 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 22 Apr 2024 19:48:00 +0400 Subject: [PATCH 1/2] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index bb0023fc0e..f069c6c557 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "10.11.1", + "app": "10.12", "xcode": "15.2", "bazel": "7.1.1", "macos": "13.0" From 59f4ec2cddf821c6771a6be204a6df0ae2e215f9 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 22 Apr 2024 20:50:06 +0400 Subject: [PATCH 2/2] Chat moderation improvements --- .../Sources/ContactsPeerItem.swift | 110 +++++----- .../Sources/AdminUserActionsSheet.swift | 4 + .../Components/PeerInfo/PeerInfoScreen/BUILD | 1 + .../Sources/Panes/PeerInfoMembersPane.swift | 204 ++++++++++++++++-- .../Sources/PeerInfoPaneContainerNode.swift | 6 + .../Sources/ChatControllerAdminBanUsers.swift | 53 ++++- 6 files changed, 305 insertions(+), 73 deletions(-) diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index c504913c2c..170d30c7e3 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -165,6 +165,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { public let peer: ContactsPeerItemPeer let status: ContactsPeerItemStatus let badge: ContactsPeerItemBadge? + let rightLabelText: String? let requiresPremiumForMessaging: Bool let enabled: Bool let selection: ContactsPeerItemSelection @@ -202,6 +203,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, + rightLabelText: String? = nil, requiresPremiumForMessaging: Bool = false, enabled: Bool, selection: ContactsPeerItemSelection, @@ -233,6 +235,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { self.peer = peer self.status = status self.badge = badge + self.rightLabelText = rightLabelText self.requiresPremiumForMessaging = requiresPremiumForMessaging self.enabled = enabled self.selection = selection @@ -421,6 +424,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { private var actionButtonNodes: [HighlightableButtonNode]? private var moreButtonNode: MoreButtonNode? private var arrowButtonNode: HighlightableButtonNode? + private var rightLabelTextNode: TextNode? private var avatarTapRecognizer: UITapGestureRecognizer? @@ -579,6 +583,10 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: strongSelf.extractedBackgroundImageNode, frame: rect) } + if let rightLabelTextNode = strongSelf.rightLabelTextNode { + transition.updateTransform(node: rightLabelTextNode, transform: CGAffineTransformMakeTranslation(isExtracted ? -24.0 : 0.0, 0.0)) + } + transition.updateSublayerTransformOffset(layer: strongSelf.offsetContainerNode.layer, offset: CGPoint(x: isExtracted ? 12.0 : 0.0, y: 0.0)) transition.updateAlpha(node: strongSelf.extractedBackgroundImageNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in if !isExtracted { @@ -588,6 +596,20 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } } + override public func didLoad() { + super.didLoad() + + self.updateEnableGestures() + } + + private func updateEnableGestures() { + if let item = self.layoutParams?.0, !item.options.isEmpty { + self.view.disablesInteractiveTransitionGestureRecognizer = false + } else { + self.view.disablesInteractiveTransitionGestureRecognizer = false + } + } + public override func secondaryAction(at point: CGPoint) { guard let item = self.item, let contextAction = item.contextAction else { return @@ -665,6 +687,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { let currentSelectionNode = self.selectionNode let makeBadgeTextLayout = TextNode.asyncLayout(self.badgeTextNode) + let makeRightLabelTextLayout = TextNode.asyncLayout(self.rightLabelTextNode) let currentItem = self.layoutParams?.0 @@ -712,6 +735,13 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } } + var rightLabelTextLayoutAndApply: (TextNodeLayout, () -> TextNode)? + if let rightLabelText = item.rightLabelText { + let rightLabelTextLayoutAndApplyValue = makeRightLabelTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: rightLabelText, font: statusFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor), maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 20.0, height: 100.0))) + rightLabelTextLayoutAndApply = rightLabelTextLayoutAndApplyValue + rightInset -= 6.0 + rightLabelTextLayoutAndApplyValue.0.size.width + } + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) var credibilityIcon: EmojiStatusComponent.Content? @@ -1117,7 +1147,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { transition = .immediate } - let revealOffset = strongSelf.revealOffset + let revealOffset: CGFloat = 0.0 if let _ = updatedTheme { switch item.style { @@ -1479,6 +1509,28 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } } + if let (rightLabelTextLayout, rightLabelTextApply) = rightLabelTextLayoutAndApply { + let rightLabelTextNode = rightLabelTextApply() + var rightLabelTextTransition = transition + if rightLabelTextNode !== strongSelf.rightLabelTextNode { + strongSelf.rightLabelTextNode?.removeFromSupernode() + strongSelf.rightLabelTextNode = rightLabelTextNode + strongSelf.offsetContainerNode.addSubnode(rightLabelTextNode) + rightLabelTextTransition = .immediate + } + + var rightLabelTextFrame = CGRect(x: revealOffset + params.width - params.rightInset - 8.0 - rightLabelTextLayout.size.width, y: floor((nodeLayout.contentSize.height - rightLabelTextLayout.size.height) / 2.0), width: rightLabelTextLayout.size.width, height: rightLabelTextLayout.size.height) + if let arrowButtonImage = arrowButtonImage { + rightLabelTextFrame.origin.x -= arrowButtonImage.size.width + 6.0 + } + + rightLabelTextNode.bounds = CGRect(origin: CGPoint(), size: rightLabelTextFrame.size) + rightLabelTextTransition.updatePosition(node: rightLabelTextNode, position: rightLabelTextFrame.center) + } else if let rightLabelTextNode = strongSelf.rightLabelTextNode { + strongSelf.rightLabelTextNode = nil + rightLabelTextNode.removeFromSupernode() + } + if let updatedSelectionNode = updatedSelectionNode { let hadSelectionNode = strongSelf.selectionNode != nil if strongSelf.selectionNode !== updatedSelectionNode { @@ -1533,6 +1585,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.setRevealOptions((left: [], right: peerRevealOptions)) strongSelf.setRevealOptionsOpened(item.editing.revealed, animated: animated) } + + strongSelf.updateEnableGestures() } }) } else { @@ -1553,57 +1607,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { super.updateRevealOffset(offset: offset, transition: transition) - if let item = self.item, let params = self.layoutParams?.1 { - var leftInset: CGFloat = 65.0 + params.leftInset - - switch item.selection { - case .none: - break - case .selectable: - leftInset += 28.0 - } - - var avatarFrame = self.avatarNode.frame - avatarFrame.origin.x = offset + leftInset - 50.0 - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - var titleFrame = self.titleNode.frame - titleFrame.origin.x = leftInset + offset - transition.updateFrame(node: self.titleNode, frame: titleFrame) - - var statusFrame = self.statusNode.frame - let previousStatusFrame = statusFrame - statusFrame.origin.x = leftInset + offset - if let statusIconImage = self.statusIconNode?.image { - statusFrame.origin.x += statusIconImage.size.width + 1.0 - } - self.statusNode.frame = statusFrame - transition.animatePositionAdditive(node: self.statusNode, offset: CGPoint(x: previousStatusFrame.minX - statusFrame.minX, y: 0)) - - var nextIconX = titleFrame.maxX - if let credibilityIconView = self.credibilityIconView { - var iconFrame = credibilityIconView.frame - iconFrame.origin.x = nextIconX + 4.0 - nextIconX += 4.0 + iconFrame.width - transition.updateFrame(view: credibilityIconView, frame: iconFrame) - } - if let verifiedIconView = self.verifiedIconView { - var iconFrame = verifiedIconView.frame - iconFrame.origin.x = nextIconX + 4.0 - nextIconX += 4.0 + iconFrame.width - transition.updateFrame(view: verifiedIconView, frame: iconFrame) - } - - if let badgeBackgroundNode = self.badgeBackgroundNode, let badgeTextNode = self.badgeTextNode { - var badgeBackgroundFrame = badgeBackgroundNode.frame - badgeBackgroundFrame.origin.x = offset + params.width - params.rightInset - badgeBackgroundFrame.width - 6.0 - var badgeTextFrame = badgeTextNode.frame - badgeTextFrame.origin.x = badgeBackgroundFrame.midX - badgeTextFrame.width / 2.0 - - transition.updateFrame(node: badgeBackgroundNode, frame: badgeBackgroundFrame) - transition.updateFrame(node: badgeTextNode, frame: badgeTextFrame) - } - } + var offsetContainerBounds = self.offsetContainerNode.bounds + offsetContainerBounds.origin.x = -offset + transition.updateBounds(node: self.offsetContainerNode, bounds: offsetContainerBounds) } override public func revealOptionsInteractivelyOpened() { diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift index 049fd4aa3f..6091bc95f8 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift @@ -616,6 +616,10 @@ private final class AdminUserActionsSheetComponent: Component { canBanEveryone = false continue } + if let banInfo = peer.participant.banInfo, !banInfo.isMember { + canBanEveryone = false + continue + } switch peer.participant { case .creator: diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD index 027439654e..4799b93086 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -144,6 +144,7 @@ swift_library( "//submodules/TelegramUI/Components/Settings/BirthdayPickerScreen", "//submodules/TelegramUI/Components/Settings/PeerSelectionScreen", "//submodules/ConfettiEffect", + "//submodules/ContactsPeerItem", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift index cc18a4165d..b4cf661eab 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift @@ -14,6 +14,7 @@ import MergeLists import ItemListUI import PeerInfoVisualMediaPaneNode import PeerInfoPaneNode +import ContactsPeerItem private struct PeerMembersListTransaction { let deletions: [ListViewDeleteItem] @@ -84,7 +85,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { } } - func item(context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void) -> ListViewItem { + func item(context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem { switch self { case let .addMember(_, text): return ItemListPeerActionItem(presentationData: ItemListPresentationData(presentationData), icon: PresentationResourcesItemList.addPersonIcon(presentationData.theme), title: text, alwaysPlain: true, sectionId: 0, height: .compactPeerList, color: .accent, editing: false, action: { @@ -123,24 +124,104 @@ private enum PeerMembersListEntry: Comparable, Identifiable { action(member, .remove) })) } + + let presence: EnginePeer.Presence + if member.peer.id == context.account.peerId { + presence = EnginePeer.Presence(status: .present(until: Int32.max), lastActivity: 0) + } else if let value = member.presence { + presence = EnginePeer.Presence(value) + } else { + presence = EnginePeer.Presence(status: .longTimeAgo, lastActivity: 0) + } + + return ContactsPeerItem( + presentationData: ItemListPresentationData(presentationData), + style: .plain, + sectionId: 0, + sortOrder: presentationData.nameSortOrder, + displayOrder: presentationData.nameDisplayOrder, + context: context, + peerMode: .peer, + peer: .peer(peer: EnginePeer(member.peer), chatPeer: EnginePeer(member.peer)), + status: .presence(presence, presentationData.dateTimeFormat), + rightLabelText: label, + enabled: true, + selection: .none, + editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), + options: options, + additionalActions: [], + actionIcon: .none, + index: nil, + header: nil, + action: { _ in + action(member, .open) + }, + disabledAction: nil, + setPeerIdWithRevealedOptions: { _, _ in + }, + deletePeer: nil, + itemHighlighting: nil, + contextAction: contextAction == nil ? nil : { node, gesture, _ in + contextAction?(member, node, gesture) + }, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + storyStats: member.storyStats.flatMap { storyStats in + return ( + total: storyStats.totalCount, + unseen: storyStats.unseenCount, + hasUnseenCloseFriends: storyStats.hasUnseenCloseFriends + ) + }, + openStories: { _, sourceNode in + action(member, .openStories(sourceView: sourceNode.view)) + } + ) - return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: context, peer: EnginePeer(member.peer), presence: member.presence.flatMap(EnginePeer.Presence.init), text: .presence, label: label == nil ? .none : .text(label!, .standard), editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: false), revealOptions: ItemListPeerItemRevealOptions(options: options), switchValue: nil, enabled: true, selectable: member.id != context.account.peerId, sectionId: 0, action: { - action(member, .open) - }, setPeerIdWithRevealedOptions: { _, _ in - }, removePeer: { _ in - }, contextAction: nil, hasTopStripe: false, noInsets: true, noCorners: true, disableInteractiveTransitionIfNecessary: true, storyStats: member.storyStats, openStories: { sourceView in - action(member, .openStories(sourceView: sourceView)) - }) + /*return ItemListPeerItem( + presentationData: ItemListPresentationData(presentationData), + dateTimeFormat: presentationData.dateTimeFormat, + nameDisplayOrder: presentationData.nameDisplayOrder, + context: context, + peer: EnginePeer(member.peer), + presence: member.presence.flatMap(EnginePeer.Presence.init), + text: .presence, + label: label == nil ? .none : .text(label!, .standard), + editing: ItemListPeerItemEditing(editable: !options.isEmpty, editing: false, revealed: false), + revealOptions: ItemListPeerItemRevealOptions(options: options), + switchValue: nil, + enabled: true, + selectable: member.id != context.account.peerId, + sectionId: 0, + action: { + action(member, .open) + }, + setPeerIdWithRevealedOptions: { _, _ in + }, + removePeer: { _ in + }, + contextAction: contextAction == nil ? nil : { node, gesture in + contextAction?(member, node, gesture) + }, + hasTopStripe: false, + noInsets: true, + noCorners: true, + disableInteractiveTransitionIfNecessary: true, + storyStats: member.storyStats, + openStories: { sourceView in + action(member, .openStories(sourceView: sourceView)) + } + )*/ } } } -private func preparedTransition(from fromEntries: [PeerMembersListEntry], to toEntries: [PeerMembersListEntry], context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void) -> PeerMembersListTransaction { +private func preparedTransition(from fromEntries: [PeerMembersListEntry], to toEntries: [PeerMembersListEntry], context: AccountContext, presentationData: PresentationData, enclosingPeer: Peer, addMemberAction: @escaping () -> Void, action: @escaping (PeerInfoMember, PeerMembersListAction) -> Void, contextAction: ((PeerInfoMember, ASDisplayNode, ContextGesture?) -> Void)?) -> PeerMembersListTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enclosingPeer: enclosingPeer, addMemberAction: addMemberAction, action: action), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enclosingPeer: enclosingPeer, addMemberAction: addMemberAction, action: action), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enclosingPeer: enclosingPeer, addMemberAction: addMemberAction, action: action, contextAction: contextAction), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, presentationData: presentationData, enclosingPeer: enclosingPeer, addMemberAction: addMemberAction, action: action, contextAction: contextAction), directionHint: nil) } return PeerMembersListTransaction(deletions: deletions, insertions: insertions, updates: updates, animated: toEntries.count < fromEntries.count) } @@ -276,7 +357,79 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, enclosingPeer: enclosingPeer, addMemberAction: { [weak self] in self?.addMemberAction() }, action: { [weak self] member, action in - self?.action(member, action) + guard let self else { + return + } + + if case .open = action { + self.listNode.clearHighlightAnimated(true) + } + + self.action(member, action) + }, contextAction: { [weak self] member, sourceNode, gesture in + guard let self else { + return + } + var node: ContextExtractedContentContainingNode? + if let sourceNode = sourceNode as? ContextExtractedContentContainingNode { + node = sourceNode + } else { + for subnode in sourceNode.subnodes ?? [] { + if let subnode = subnode as? ContextExtractedContentContainingNode { + node = subnode + break + } + } + } + guard let node else { + gesture?.cancel() + return + } + + let actions = availableActionsForMemberOfPeer(accountPeerId: self.context.account.peerId, peer: enclosingPeer, member: member) + + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } + var items: [ContextMenuItem] = [] + let action = self.action + + if actions.contains(.promote) && enclosingPeer is TelegramChannel { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.GroupInfo_ActionPromote, icon: { _ in + return nil + }, action: { c, _ in + c.dismiss(completion: { + action(member, .promote) + }) + }))) + } + if actions.contains(.restrict) { + if enclosingPeer is TelegramChannel { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.GroupInfo_ActionRestrict, icon: { _ in + return nil + }, action: { c, _ in + c.dismiss(completion: { + action(member, .restrict) + }) + }))) + } + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Common_Delete, textColor: .destructive, icon: { _ in + return nil + }, action: { c, _ in + c.dismiss(completion: { + action(member, .remove) + }) + }))) + } + + if items.isEmpty { + gesture?.cancel() + return + } + + let dismissPromise = ValuePromise(false) + let source = PeerInfoMemberExtractedContentSource(sourceNode: node, keepInPlace: false, blurBackground: true, centerVertically: false, shouldBeDismissed: dismissPromise.get()) + + let contextController = ContextController(presentationData: presentationData, source: .extracted(source), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + self.parentController?.presentInGlobalOverlay(contextController) }) self.enclosingPeer = enclosingPeer self.currentEntries = entries @@ -335,3 +488,30 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode { func updateSelectedMessages(animated: Bool) { } } + +final class PeerInfoMemberExtractedContentSource: ContextExtractedContentSource { + var keepInPlace: Bool + let ignoreContentTouches: Bool = false + let blurBackground: Bool + + private let sourceNode: ContextExtractedContentContainingNode + + var centerVertically: Bool + var shouldBeDismissed: Signal + + init(sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool, blurBackground: Bool, centerVertically: Bool, shouldBeDismissed: Signal) { + self.sourceNode = sourceNode + self.keepInPlace = keepInPlace + self.blurBackground = blurBackground + self.centerVertically = centerVertically + self.shouldBeDismissed = shouldBeDismissed + } + + func takeView() -> ContextControllerTakeViewInfo? { + return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds) + } + + func putBack() -> ContextControllerPutBackViewInfo? { + return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index 9e57b3cc6d..ca3183dc1e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -648,6 +648,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } return [.leftCenter, .rightCenter] } + if case .members = currentPaneKey { + if index == 0 { + return .leftCenter + } + return [.leftCenter, .rightCenter] + } if strongSelf.currentPane?.node.navigationContentNode != nil { return [] } diff --git a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift index 74bda81035..d9ee42aa39 100644 --- a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift +++ b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift @@ -28,6 +28,9 @@ extension ChatControllerImpl { //TODO:localize var title: String? = messageIds.count == 1 ? "Message Deleted" : "Messages Deleted" + if !result.deleteAllFromPeers.isEmpty { + title = "Messages Deleted" + } var text: String = "" var undoRights: [EnginePeer.Id: InitialBannedRights] = [:] @@ -95,6 +98,9 @@ extension ChatControllerImpl { if text.isEmpty { text = messageIds.count == 1 ? "Message Deleted." : "Messages Deleted." + if !result.deleteAllFromPeers.isEmpty { + text = "Messages Deleted." + } title = nil } @@ -134,6 +140,9 @@ extension ChatControllerImpl { var signal = combineLatest(authors.map { author in self.context.engine.peers.fetchChannelParticipant(peerId: peerId, participantId: author.id) + |> map { result -> (Peer, ChannelParticipant?) in + return (author, result) + } }) let disposables = MetaDisposable() self.navigationActionDisposable.set(disposables) @@ -166,7 +175,7 @@ extension ChatControllerImpl { } disposables.set((signal - |> deliverOnMainQueue).startStrict(next: { [weak self] participants in + |> deliverOnMainQueue).startStrict(next: { [weak self] authorsAndParticipants in guard let self else { return } @@ -179,13 +188,23 @@ extension ChatControllerImpl { } var renderedParticipants: [RenderedChannelParticipant] = [] var initialUserBannedRights: [EnginePeer.Id: InitialBannedRights] = [:] - for maybeParticipant in participants { - guard let participant = maybeParticipant else { - continue - } - guard let peer = authors.first(where: { $0.id == participant.peerId }) else { - continue + for (author, maybeParticipant) in authorsAndParticipants { + let participant: ChannelParticipant + if let maybeParticipant { + participant = maybeParticipant + } else { + participant = .member(id: author.id, invitedAt: 0, adminInfo: nil, banInfo: ChannelParticipantBannedInfo( + rights: TelegramChatBannedRights( + flags: [.banReadMessages], + untilDate: Int32.max + ), + restrictedBy: self.context.account.peerId, + timestamp: 0, + isMember: false + ), rank: nil) } + + let peer = author renderedParticipants.append(RenderedChannelParticipant( participant: participant, peer: peer @@ -254,10 +273,26 @@ extension ChatControllerImpl { } disposables.set((signal - |> deliverOnMainQueue).startStrict(next: { [weak self] participant in - guard let self, let participant else { + |> deliverOnMainQueue).startStrict(next: { [weak self] maybeParticipant in + guard let self else { return } + + let participant: ChannelParticipant + if let maybeParticipant { + participant = maybeParticipant + } else { + participant = .member(id: author.id, invitedAt: 0, adminInfo: nil, banInfo: ChannelParticipantBannedInfo( + rights: TelegramChatBannedRights( + flags: [.banReadMessages], + untilDate: Int32.max + ), + restrictedBy: self.context.account.peerId, + timestamp: 0, + isMember: false + ), rank: nil) + } + let _ = (self.context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), TelegramEngine.EngineData.Item.Peer.Peer(id: author.id)