diff --git a/submodules/AccountContext/Sources/ContactMultiselectionController.swift b/submodules/AccountContext/Sources/ContactMultiselectionController.swift index 8f7d54bb8d..4d00d76eb1 100644 --- a/submodules/AccountContext/Sources/ContactMultiselectionController.swift +++ b/submodules/AccountContext/Sources/ContactMultiselectionController.swift @@ -96,19 +96,21 @@ public final class ContactMultiselectionControllerParams { public let options: [ContactListAdditionalOption] public let filters: [ContactListFilter] public let onlyWriteable: Bool + public let isGroupInvitation: Bool public let isPeerEnabled: ((EnginePeer) -> Bool)? public let attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)? public let alwaysEnabled: Bool public let limit: Int32? public let reachedLimit: ((Int32) -> Void)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool = false, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)? = nil, alwaysEnabled: Bool = false, limit: Int32? = nil, reachedLimit: ((Int32) -> Void)? = nil) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool = false, isGroupInvitation: Bool = false, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)? = nil, alwaysEnabled: Bool = false, limit: Int32? = nil, reachedLimit: ((Int32) -> Void)? = nil) { self.context = context self.updatedPresentationData = updatedPresentationData self.mode = mode self.options = options self.filters = filters self.onlyWriteable = onlyWriteable + self.isGroupInvitation = isGroupInvitation self.isPeerEnabled = isPeerEnabled self.attemptDisabledItemSelection = attemptDisabledItemSelection self.alwaysEnabled = alwaysEnabled diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index f1e67d9bc5..004f5acb75 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -1059,7 +1059,7 @@ public final class ContactListNode: ASDisplayNode { private let isPeerEnabled: ((EnginePeer) -> Bool)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, presentation: Signal, filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, displayCallIcons: Bool = false, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)? = nil, isSearch: Bool = false, multipleSelection: Bool = false) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, presentation: Signal, filters: [ContactListFilter] = [.excludeSelf], onlyWriteable: Bool, isGroupInvitation: Bool, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, displayCallIcons: Bool = false, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)? = nil, isSearch: Bool = false, multipleSelection: Bool = false) { self.context = context self.filters = filters self.displayPermissionPlaceholder = displayPermissionPlaceholder @@ -1383,7 +1383,7 @@ public final class ContactListNode: ASDisplayNode { }) let peerRequiresPremiumForMessaging: Signal<[EnginePeer.Id: Bool], NoError> - if onlyWriteable { + if onlyWriteable && !isGroupInvitation { peerRequiresPremiumForMessaging = foundPeers.get() |> map { foundPeers -> Set in var result = Set() diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index b23c24310c..b72c8501fb 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -114,7 +114,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate { var contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)? - self.contactListNode = ContactListNode(context: context, presentation: presentation, onlyWriteable: false, displaySortOptions: true, contextAction: { peer, node, gesture, location, isStories in + self.contactListNode = ContactListNode(context: context, presentation: presentation, onlyWriteable: false, isGroupInvitation: false, displaySortOptions: true, contextAction: { peer, node, gesture, location, isStories in contextAction?(peer, node, gesture, location, isStories) }) diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index 42ef265d82..588c242c38 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -1244,8 +1244,8 @@ public func channelAdminController(context: AccountContext, updatedPresentationD return current.withUpdatedUpdating(false) } - if let adminPeer { - let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [adminPeer]) + if let adminPeer, case let .restricted(forbiddenPeer) = error { + let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [forbiddenPeer ?? TelegramForbiddenInvitePeer(peer: adminPeer, canInviteWithPremium: false, premiumRequiredToContact: false)]) pushControllerImpl?(inviteScreen) dismissImpl?() @@ -1433,9 +1433,9 @@ public func channelAdminController(context: AccountContext, updatedPresentationD return current.withUpdatedUpdating(true) } updateRightsDisposable.set((context.peerChannelMemberCategoriesContextsManager.updateMemberAdminRights(engine: context.engine, peerId: peerId, memberId: adminId, adminRights: TelegramChatAdminRights(rights: updateFlags), rank: updateRank) |> deliverOnMainQueue).start(error: { error in - if case let .addMemberError(addMemberError) = error, let admin = adminPeer { + if case let .addMemberError(addMemberError) = error, case let .restricted(forbiddenPeer) = addMemberError, let admin = adminPeer { if let channelPeer { - let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [admin]) + let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [forbiddenPeer ?? TelegramForbiddenInvitePeer(peer: admin, canInviteWithPremium: false, premiumRequiredToContact: false)]) pushControllerImpl?(inviteScreen) dismissImpl?() diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift index 235b87aa38..49341c10bf 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersController.swift @@ -549,25 +549,25 @@ public func channelMembersController(context: AccountContext, updatedPresentatio contactsController?.dismiss() } else { if let chatPeer { - let _ = (context.engine.data.get( - EngineDataList(failedPeerIds.compactMap { item -> EnginePeer.Id? in - return item.0 - }.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) - ) - |> deliverOnMainQueue).start(next: { peerItems in - let peers = peerItems.compactMap { $0 } - if !peers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { - var viewControllers = navigationController.viewControllers - if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { - let inviteScreen = SendInviteLinkScreen(context: context, peer: chatPeer, link: exportedInvitation?.link, peers: peers) - viewControllers.remove(at: index) - viewControllers.append(inviteScreen) - navigationController.setViewControllers(viewControllers, animated: true) - } + let failedPeers = failedPeerIds.compactMap { _, error -> TelegramForbiddenInvitePeer? in + if case let .restricted(peer) = error { + return peer } else { - contactsController?.dismiss() + return nil } - }) + } + + if !failedPeers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { + let inviteScreen = SendInviteLinkScreen(context: context, peer: chatPeer, link: exportedInvitation?.link, peers: failedPeers) + viewControllers.remove(at: index) + viewControllers.append(inviteScreen) + navigationController.setViewControllers(viewControllers, animated: true) + } + } else { + contactsController?.dismiss() + } return } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddPeerMember.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddPeerMember.swift index d97aeb1146..eddd975006 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddPeerMember.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddPeerMember.swift @@ -4,15 +4,50 @@ import SwiftSignalKit import TelegramApi import MtProtoKit - public enum AddGroupMemberError { case generic case groupFull - case privacy + case privacy(TelegramInvitePeersResult?) case notMutualContact case tooManyChannels } +public final class TelegramForbiddenInvitePeer: Equatable { + public let peer: EnginePeer + public let canInviteWithPremium: Bool + public let premiumRequiredToContact: Bool + + public init(peer: EnginePeer, canInviteWithPremium: Bool, premiumRequiredToContact: Bool) { + self.peer = peer + self.canInviteWithPremium = canInviteWithPremium + self.premiumRequiredToContact = premiumRequiredToContact + } + + public static func ==(lhs: TelegramForbiddenInvitePeer, rhs: TelegramForbiddenInvitePeer) -> Bool { + if lhs === rhs { + return true + } + if lhs.peer != rhs.peer { + return false + } + if lhs.canInviteWithPremium != rhs.canInviteWithPremium { + return false + } + if lhs.premiumRequiredToContact != rhs.premiumRequiredToContact { + return false + } + return true + } +} + +public final class TelegramInvitePeersResult { + public let forbiddenPeers: [TelegramForbiddenInvitePeer] + + public init(forbiddenPeers: [TelegramForbiddenInvitePeer]) { + self.forbiddenPeers = forbiddenPeers + } +} + func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId) -> Signal { return account.postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId), let memberPeer = transaction.getPeer(memberId), let inputUser = apiInputUser(memberPeer) { @@ -23,7 +58,7 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId case "USERS_TOO_MUCH": return .groupFull case "USER_PRIVACY_RESTRICTED": - return .privacy + return .privacy(nil) case "USER_CHANNELS_TOO_MUCH": return .tooManyChannels case "USER_NOT_MUTUAL_CONTACT": @@ -34,14 +69,16 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId } |> mapToSignal { result -> Signal in let updatesValue: Api.Updates + let missingInviteesValue: [Api.MissingInvitee] switch result { case let .invitedUsers(updates, missingInvitees): - let _ = missingInvitees updatesValue = updates + missingInviteesValue = missingInvitees } account.stateManager.addUpdates(updatesValue) - return account.postbox.transaction { transaction -> Void in + + return account.postbox.transaction { transaction -> TelegramInvitePeersResult in if let message = updatesValue.messages.first, let timestamp = message.timestamp { transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in if let cachedData = cachedData as? CachedGroupData, let participants = cachedData.participants { @@ -62,8 +99,29 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId } }) } + + return TelegramInvitePeersResult(forbiddenPeers: missingInviteesValue.compactMap { invitee -> TelegramForbiddenInvitePeer? in + switch invitee { + case let .missingInvitee(flags, userId): + guard let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) else { + return nil + } + return TelegramForbiddenInvitePeer( + peer: EnginePeer(peer), + canInviteWithPremium: (flags & (1 << 0)) != 0, + premiumRequiredToContact: (flags & (1 << 1)) != 0 + ) + } + }) } |> mapError { _ -> AddGroupMemberError in } + |> mapToSignal { result -> Signal in + if result.forbiddenPeers.isEmpty { + return .single(Void()) + } else { + return .fail(.privacy(result)) + } + } } } else { return .fail(.generic) @@ -76,7 +134,7 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId public enum AddChannelMemberError { case generic - case restricted + case restricted(TelegramForbiddenInvitePeer?) case notMutualContact case limitExceeded case tooMuchJoined @@ -108,7 +166,7 @@ func _internal_addChannelMember(account: Account, peerId: PeerId, memberId: Peer case "USERS_TOO_MUCH": return .fail(.limitExceeded) case "USER_PRIVACY_RESTRICTED": - return .fail(.restricted) + return .fail(.restricted(nil)) case "USER_NOT_MUTUAL_CONTACT": return .fail(.notMutualContact) case "USER_BOT": @@ -127,7 +185,14 @@ func _internal_addChannelMember(account: Account, peerId: PeerId, memberId: Peer let updatesValue: Api.Updates switch result { case let .invitedUsers(updates, missingInvitees): - let _ = missingInvitees + if case let .missingInvitee(flags, _) = missingInvitees.first { + return .fail(.restricted(TelegramForbiddenInvitePeer( + peer: EnginePeer(memberPeer), + canInviteWithPremium: (flags & (1 << 0)) != 0, + premiumRequiredToContact: (flags & (1 << 1)) != 0 + ))) + } + updatesValue = updates } @@ -193,8 +258,8 @@ func _internal_addChannelMember(account: Account, peerId: PeerId, memberId: Peer } } -func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [PeerId]) -> Signal { - let signal = account.postbox.transaction { transaction -> Signal in +func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [PeerId]) -> Signal { + let signal = account.postbox.transaction { transaction -> Signal in var memberPeerIds: [PeerId:Peer] = [:] var inputUsers: [Api.InputUser] = [] for memberId in memberIds { @@ -207,13 +272,13 @@ func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [P } if let peer = transaction.getPeer(peerId), let channel = peer as? TelegramChannel, let inputChannel = apiInputChannel(channel) { - let signal = account.network.request(Api.functions.channels.inviteToChannel(channel: inputChannel, users: inputUsers)) + let signal: Signal = account.network.request(Api.functions.channels.inviteToChannel(channel: inputChannel, users: inputUsers)) |> mapError { error -> AddChannelMemberError in switch error.errorDescription { case "CHANNELS_TOO_MUCH": return .tooMuchJoined case "USER_PRIVACY_RESTRICTED": - return .restricted + return .restricted(nil) case "USER_NOT_MUTUAL_CONTACT": return .notMutualContact case "USERS_TOO_MUCH": @@ -224,21 +289,39 @@ func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [P return .generic } } - |> map { result in + |> mapToQueue { result -> Signal in let updatesValue: Api.Updates + let missingInviteesValue: [Api.MissingInvitee] switch result { case let .invitedUsers(updates, missingInvitees): - let _ = missingInvitees updatesValue = updates + missingInviteesValue = missingInvitees } account.stateManager.addUpdates(updatesValue) account.viewTracker.forceUpdateCachedPeerData(peerId: peerId) + + return account.postbox.transaction { transaction -> TelegramInvitePeersResult in + return TelegramInvitePeersResult(forbiddenPeers: missingInviteesValue.compactMap { invitee -> TelegramForbiddenInvitePeer? in + switch invitee { + case let .missingInvitee(flags, userId): + guard let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) else { + return nil + } + return TelegramForbiddenInvitePeer( + peer: EnginePeer(peer), + canInviteWithPremium: (flags & (1 << 0)) != 0, + premiumRequiredToContact: (flags & (1 << 1)) != 0 + ) + } + }) + } + |> castError(AddChannelMemberError.self) } return signal } else { - return .single(Void()) + return .fail(.generic) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/CreateGroup.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/CreateGroup.swift index 88fbe49ed5..52091adb06 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/CreateGroup.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/CreateGroup.swift @@ -15,14 +15,14 @@ public enum CreateGroupError { public struct CreateGroupResult { public var peerId: EnginePeer.Id - public var failedToInvitePeerIds: [EnginePeer.Id] + public var result: TelegramInvitePeersResult public init( peerId: EnginePeer.Id, - failedToInvitePeerIds: [EnginePeer.Id] + result: TelegramInvitePeersResult ) { self.peerId = peerId - self.failedToInvitePeerIds = failedToInvitePeerIds + self.result = result } } @@ -50,14 +50,12 @@ func _internal_createGroup(account: Account, title: String, peerIds: [PeerId], t return .generic } |> mapToSignal { result -> Signal in - var failedToInvitePeerIds: [EnginePeer.Id] = [] - failedToInvitePeerIds = [] - let updatesValue: Api.Updates + let missingInviteesValue: [Api.MissingInvitee] switch result { case let .invitedUsers(updates, missingInvitees): - let _ = missingInvitees updatesValue = updates + missingInviteesValue = missingInvitees } account.stateManager.addUpdates(updatesValue) @@ -68,11 +66,26 @@ func _internal_createGroup(account: Account, title: String, peerIds: [PeerId], t } |> take(1) |> castError(CreateGroupError.self) - |> map { _ -> CreateGroupResult in - return CreateGroupResult( - peerId: peerId, - failedToInvitePeerIds: failedToInvitePeerIds - ) + |> mapToSignal { _ -> Signal in + return account.postbox.transaction { transaction -> CreateGroupResult in + return CreateGroupResult( + peerId: peerId, + result: TelegramInvitePeersResult(forbiddenPeers: missingInviteesValue.compactMap { invitee -> TelegramForbiddenInvitePeer? in + switch invitee { + case let .missingInvitee(flags, userId): + guard let peer = transaction.getPeer(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))) else { + return nil + } + return TelegramForbiddenInvitePeer( + peer: EnginePeer(peer), + canInviteWithPremium: (flags & (1 << 0)) != 0, + premiumRequiredToContact: (flags & (1 << 1)) != 0 + ) + } + }) + ) + } + |> castError(CreateGroupError.self) } } else { return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift index 8d6e5b9777..5df0981456 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/PeerAdmins.swift @@ -78,7 +78,7 @@ func _internal_addGroupAdmin(account: Account, peerId: PeerId, adminId: PeerId) } ) } else if error.errorDescription == "USER_PRIVACY_RESTRICTED" { - return .fail(.addMemberError(.privacy)) + return .fail(.addMemberError(.privacy(nil))) } else if error.errorDescription == "ADMINS_TOO_MUCH" { return .fail(.adminsTooMuch) } @@ -204,7 +204,7 @@ func _internal_updateChannelAdminRights(account: Account, peerId: PeerId, adminI } else if error.errorDescription == "USER_NOT_MUTUAL_CONTACT" { return .fail(.addMemberError(.notMutualContact)) } else if error.errorDescription == "USER_PRIVACY_RESTRICTED" { - return .fail(.addMemberError(.restricted)) + return .fail(.addMemberError(.restricted(nil))) } else if error.errorDescription == "USER_CHANNELS_TOO_MUCH" { return .fail(.addMemberError(.tooMuchJoined)) } else if error.errorDescription == "ADMINS_TOO_MUCH" { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index f188a40c9e..9bbd612e7e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -589,7 +589,7 @@ public extension TelegramEngine { return _internal_sendBotRequestedPeer(account: self.account, peerId: messageId.peerId, messageId: messageId, buttonId: buttonId, requestedPeerIds: requestedPeerIds) } - public func addChannelMembers(peerId: PeerId, memberIds: [PeerId]) -> Signal { + public func addChannelMembers(peerId: PeerId, memberIds: [PeerId]) -> Signal { return _internal_addChannelMembers(account: self.account, peerId: peerId, memberIds: memberIds) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 9e5a0ead26..710d3568a3 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -316,6 +316,7 @@ public enum PresentationResourceKey: Int32 { case sharedLinkIcon case hideIconImage + case peerStatusLockedImage } public enum ChatExpiredStoryIndicatorType: Hashable { diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift index 157e027eca..91f5f1c975 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesItemList.swift @@ -408,4 +408,10 @@ public struct PresentationResourcesItemList { return generateTintedImage(image: UIImage(bundleImageName: "Chat List/Archive/IconHide"), color: theme.list.itemAccentColor) }) } + + public static func peerStatusLockedImage(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.peerStatusLockedImage.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/SmallLock"), color: theme.list.itemSecondaryTextColor) + }) + } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift index 2654f9840f..7b8ac46a98 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoMembers.swift @@ -274,6 +274,9 @@ private final class PeerInfoMembersContextImpl { deinit { self.disposable.dispose() self.peerDisposable.dispose() + for (_, disposable) in self.removingMemberIds { + disposable.dispose() + } } private func pushState() { @@ -293,7 +296,7 @@ private final class PeerInfoMembersContextImpl { } func removeMember(memberId: PeerId) { - if removingMemberIds[memberId] == nil { + if self.removingMemberIds[memberId] == nil { let signal: Signal if self.peerId.namespace == Namespaces.Peer.CloudChannel { signal = context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: self.context.engine, peerId: self.peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index f19d68bb18..63402485b8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -12427,20 +12427,8 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa }, clearHighlightAutomatically: true)) } - let contactsController: ViewController - /*if groupPeer.id.namespace == Namespaces.Peer.CloudGroup { - contactsController = context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, autoDismiss: false, title: { $0.GroupInfo_AddParticipantTitle }, options: options, confirmation: { peer in - if let confirmationImpl = confirmationImpl, case let .peer(peer, _, _) = peer { - return confirmationImpl(peer.id) - } else { - return .single(false) - } - })) + let contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), options: options, filters: [.excludeSelf, .disable(recentIds)], onlyWriteable: true, isGroupInvitation: true)) contactsController.navigationPresentation = .modal - } else {*/ - contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), options: options, filters: [.excludeSelf, .disable(recentIds)], onlyWriteable: true)) - contactsController.navigationPresentation = .modal - //} confirmationImpl = { [weak contactsController] peerId in return context.account.postbox.loadedPeerWithId(peerId) @@ -12464,162 +12452,6 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa } } - /*let addMember: (ContactListPeer) -> Signal = { [weak contactsController] memberPeer -> Signal in - if case let .peer(selectedPeer, _, _) = memberPeer { - let memberId = selectedPeer.id - if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { - return context.peerChannelMemberCategoriesContextsManager.addMember(engine: context.engine, peerId: groupPeer.id, memberId: memberId) - |> map { _ -> Void in - } - |> `catch` { error -> Signal in - let text: String - switch error { - case .limitExceeded: - text = presentationData.strings.Channel_ErrorAddTooMuch - case .tooMuchJoined: - text = presentationData.strings.Invite_ChannelsTooMuch - case .generic: - text = presentationData.strings.Login_UnknownError - case .restricted: - text = presentationData.strings.Channel_ErrorAddBlocked - case .notMutualContact: - if let peer = groupPeer as? TelegramChannel, case .broadcast = peer.info { - text = presentationData.strings.Channel_AddUserLeftError - } else { - text = presentationData.strings.GroupInfo_AddUserLeftError - } - case let .bot(memberId): - guard let peer = groupPeer as? TelegramChannel else { - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - contactsController?.dismiss() - return .complete() - } - - if peer.hasPermission(.addAdmins) { - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Channel_AddBotErrorHaveRights, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_AddBotAsAdmin, action: { - contactsController?.dismiss() - - parentController?.push(channelAdminController(context: context, updatedPresentationData: updatedPresentationData, peerId: groupPeer.id, adminId: memberId, initialParticipant: nil, updated: { _ in - }, upgradedToSupergroup: { _, f in f () }, transferedOwnership: { _ in })) - })]), in: .window(.root)) - } else { - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Channel_AddBotErrorHaveRights, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } - - contactsController?.dismiss() - return .complete() - case .botDoesntSupportGroups: - text = presentationData.strings.Channel_BotDoesntSupportGroups - case .tooMuchBots: - text = presentationData.strings.Channel_TooMuchBots - case .kicked: - text = presentationData.strings.Channel_AddUserKickedError - } - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - return .complete() - } - } else { - return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: groupPeer.id)) - |> mapToSignal { exportedInvitation in - return context.engine.peers.addGroupMember(peerId: groupPeer.id, memberId: memberId) - |> deliverOnMainQueue - |> `catch` { error -> Signal in - if let exportedInvitation, let link = exportedInvitation.link { - let failedPeerIds: [(PeerId, AddGroupMemberError)] = [(memberId, error)] - let _ = (context.engine.data.get( - EngineDataList(failedPeerIds.compactMap { item -> EnginePeer.Id? in - return item.0 - }.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) - ) - |> deliverOnMainQueue).start(next: { peerItems in - let peers = peerItems.compactMap { $0 } - if !peers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { - var viewControllers = navigationController.viewControllers - - let inviteScreen = SendInviteLinkScreen(context: context, link: link, peers: peers) - - if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { - viewControllers.remove(at: index) - viewControllers.append(inviteScreen) - navigationController.setViewControllers(viewControllers, animated: true) - } else { - navigationController.pushViewController(inviteScreen) - } - } else { - contactsController?.dismiss() - } - }) - - return .complete() - } - - switch error { - case .generic: - return .complete() - case .privacy: - let _ = (context.account.postbox.loadedPeerWithId(memberId) - |> deliverOnMainQueue).start(next: { peer in - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(EnginePeer(peer).compactDisplayTitle, EnginePeer(peer).compactDisplayTitle).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }) - return .complete() - case .notMutualContact: - let _ = (context.account.postbox.loadedPeerWithId(memberId) - |> deliverOnMainQueue).start(next: { peer in - let text: String - if let peer = peer as? TelegramChannel, case .broadcast = peer.info { - text = presentationData.strings.Channel_AddUserLeftError - } else { - text = presentationData.strings.GroupInfo_AddUserLeftError - } - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }) - return .complete() - case .tooManyChannels: - let _ = (context.account.postbox.loadedPeerWithId(memberId) - |> deliverOnMainQueue).start(next: { peer in - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }) - return .complete() - case .groupFull: - let signal = context.engine.peers.convertGroupToSupergroup(peerId: groupPeer.id) - |> map(Optional.init) - |> `catch` { error -> Signal in - switch error { - case .tooManyChannels: - Queue.mainQueue().async { - parentController?.push(oldChannelsController(context: context, intent: .upgrade)) - } - default: - break - } - return .single(nil) - } - |> mapToSignal { upgradedPeerId -> Signal in - guard let upgradedPeerId = upgradedPeerId else { - return .single(nil) - } - return context.peerChannelMemberCategoriesContextsManager.addMember(engine: context.engine, peerId: upgradedPeerId, memberId: memberId) - |> `catch` { _ -> Signal in - return .complete() - } - |> mapToSignal { _ -> Signal in - } - |> then(.single(upgradedPeerId)) - } - |> deliverOnMainQueue - |> mapToSignal { _ -> Signal in - return .complete() - } - return signal - } - } - } - } - } else { - return .complete() - } - }*/ - let addMembers: ([ContactListPeerId]) -> Signal<[(PeerId, AddChannelMemberError)], NoError> = { members -> Signal<[(PeerId, AddChannelMemberError)], NoError> in let memberIds = members.compactMap { contact -> PeerId? in switch contact { @@ -12655,8 +12487,8 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa return .generic case .groupFull: return .limitExceeded - case .privacy: - return .restricted + case let .privacy(privacy): + return .restricted(privacy?.forbiddenPeers.first) case .notMutualContact: return .notMutualContact case .tooManyChannels: @@ -12686,25 +12518,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa } parentController?.push(contactsController) - /*if let contactsController = contactsController as? ContactSelectionController { - selectAddMemberDisposable.set((contactsController.result - |> deliverOnMainQueue).start(next: { [weak contactsController] result in - guard let (peers, _, _, _, _) = result, let memberPeer = peers.first else { - return - } - - contactsController?.displayProgress = true - addMemberDisposable.set((addMember(memberPeer) - |> deliverOnMainQueue).start(completed: { - contactsController?.dismiss() - })) - })) - contactsController.dismissed = { - selectAddMemberDisposable.set(nil) - addMemberDisposable.set(nil) - } - }*/ - if let contactsController = contactsController as? ContactMultiselectionController { + do { selectAddMemberDisposable.set(( combineLatest(queue: .mainQueue(), context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: groupPeer.id)), @@ -12746,56 +12560,25 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa }) } } else { - if "".isEmpty { - let _ = (context.engine.data.get( - EngineDataList(failedPeerIds.compactMap { item -> EnginePeer.Id? in - return item.0 - }.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) - ) - |> deliverOnMainQueue).startStandalone(next: { peerItems in - let peers = peerItems.compactMap { $0 } - if !peers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { - var viewControllers = navigationController.viewControllers - if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { - let inviteScreen = SendInviteLinkScreen(context: context, peer: EnginePeer(groupPeer), link: exportedInvitation?.link, peers: peers) - viewControllers.remove(at: index) - viewControllers.append(inviteScreen) - navigationController.setViewControllers(viewControllers, animated: true) - } - } else { - contactsController?.dismiss() - } - }) - - return - } - - if peers.count == 1, case .restricted = failedPeerIds[0].1 { - switch peers[0] { - case let .peer(peerId): - let _ = (context.account.postbox.loadedPeerWithId(peerId) - |> deliverOnMainQueue).startStandalone(next: { peer in - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(EnginePeer(peer).compactDisplayTitle, EnginePeer(peer).compactDisplayTitle).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - }) - default: - break - } - } else if peers.count == 1, case .notMutualContact = failedPeerIds[0].1 { - let text: String - if let peer = groupPeer as? TelegramChannel, case .broadcast = peer.info { - text = presentationData.strings.Channel_AddUserLeftError + let failedPeers = failedPeerIds.compactMap { _, error -> TelegramForbiddenInvitePeer? in + if case let .restricted(peer) = error { + return peer } else { - text = presentationData.strings.GroupInfo_AddUserLeftError + return nil } - - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } else if case .tooMuchJoined = failedPeerIds[0].1 { - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } else if peers.count == 1, case .kicked = failedPeerIds[0].1 { - parentController?.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Channel_AddUserKickedError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } - contactsController?.dismiss() + if !failedPeers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { + let inviteScreen = SendInviteLinkScreen(context: context, peer: EnginePeer(groupPeer), link: exportedInvitation?.link, peers: failedPeers) + viewControllers.remove(at: index) + viewControllers.append(inviteScreen) + navigationController.setViewControllers(viewControllers, animated: true) + } + } else { + contactsController?.dismiss() + } } })) })) diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index 5f05cc2089..1237bcd3f0 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -1354,7 +1354,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.controller?.containerLayoutUpdated(layout, transition: .immediate) } } else { - let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false, topPeers: .none)), onlyWriteable: self.filter.contains(.onlyWriteable)) + let contactListNode = ContactListNode(context: self.context, updatedPresentationData: self.updatedPresentationData, presentation: .single(.natural(options: [], includeChatList: false, topPeers: .none)), onlyWriteable: self.filter.contains(.onlyWriteable), isGroupInvitation: false) self.contactListNode = contactListNode contactListNode.enableUpdates = true contactListNode.selectionStateUpdated = { [weak self] selectionState in diff --git a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift index 53f06c2c24..55e3f10ea6 100644 --- a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/PeerListItemComponent.swift @@ -34,13 +34,22 @@ final class PeerListItemComponent: Component { case editing(isSelected: Bool) } + enum SubtitleIcon { + case lock + } + + enum Subtitle: Equatable { + case presence(EnginePeer.Presence?) + case text(text: String, icon: SubtitleIcon) + } + let context: AccountContext let theme: PresentationTheme let strings: PresentationStrings let sideInset: CGFloat let title: String + let subtitle: Subtitle let peer: EnginePeer? - let presence: EnginePeer.Presence? let selectionState: SelectionState let hasNext: Bool let action: (EnginePeer) -> Void @@ -51,8 +60,8 @@ final class PeerListItemComponent: Component { strings: PresentationStrings, sideInset: CGFloat, title: String, + subtitle: Subtitle, peer: EnginePeer?, - presence: EnginePeer.Presence?, selectionState: SelectionState, hasNext: Bool, action: @escaping (EnginePeer) -> Void @@ -62,8 +71,8 @@ final class PeerListItemComponent: Component { self.strings = strings self.sideInset = sideInset self.title = title + self.subtitle = subtitle self.peer = peer - self.presence = presence self.selectionState = selectionState self.hasNext = hasNext self.action = action @@ -85,10 +94,10 @@ final class PeerListItemComponent: Component { if lhs.title != rhs.title { return false } - if lhs.peer != rhs.peer { + if lhs.subtitle != rhs.subtitle { return false } - if lhs.presence != rhs.presence { + if lhs.peer != rhs.peer { return false } if lhs.selectionState != rhs.selectionState { @@ -108,6 +117,7 @@ final class PeerListItemComponent: Component { private let separatorLayer: SimpleLayer private let avatarNode: AvatarNode + private var labelIconView: UIImageView? private var checkLayer: CheckLayer? private var component: PeerListItemComponent? @@ -165,7 +175,7 @@ final class PeerListItemComponent: Component { self.component = component self.state = state - if let presence = component.presence { + if case let .presence(presence) = component.subtitle, let presence { let statusManager: PeerPresenceStatusManager if let current = self.statusManager { statusManager = current @@ -240,11 +250,26 @@ final class PeerListItemComponent: Component { } } + var labelIcon: UIImage? let labelData: (String, Bool) - if let presence = component.presence { - labelData = stringAndActivityForUserPresence(strings: component.strings, dateTimeFormat: PresentationDateTimeFormat(), presence: presence, relativeTo: Int32(Date().timeIntervalSince1970)) - } else { - labelData = (component.strings.LastSeen_Offline, false) + switch component.subtitle { + case let .presence(presence): + if let presence { + labelData = stringAndActivityForUserPresence(strings: component.strings, dateTimeFormat: PresentationDateTimeFormat(), presence: presence, relativeTo: Int32(Date().timeIntervalSince1970)) + } else { + labelData = (component.strings.LastSeen_Offline, false) + } + case let .text(text, icon): + switch icon { + case .lock: + labelIcon = PresentationResourcesItemList.peerStatusLockedImage(component.theme) + } + labelData = (text, false) + } + + var maxTextSize = availableSize.width - leftInset - rightInset + if labelIcon != nil { + maxTextSize -= 48.0 } let labelSize = self.label.update( @@ -253,7 +278,7 @@ final class PeerListItemComponent: Component { text: .plain(NSAttributedString(string: labelData.0, font: Font.regular(15.0), textColor: labelData.1 ? component.theme.list.itemAccentColor : component.theme.list.itemSecondaryTextColor)) )), environment: {}, - containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0) + containerSize: CGSize(width: maxTextSize, height: 100.0) ) let previousTitleFrame = self.title.view?.frame @@ -268,7 +293,7 @@ final class PeerListItemComponent: Component { text: .plain(NSAttributedString(string: component.title, font: Font.semibold(17.0), textColor: component.theme.list.itemPrimaryTextColor)) )), environment: {}, - containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0) + containerSize: CGSize(width: maxTextSize, height: 100.0) ) let titleSpacing: CGFloat = 1.0 @@ -296,6 +321,27 @@ final class PeerListItemComponent: Component { transition.animateAlpha(view: titleView, from: 0.0, to: 1.0) } } + + if let labelIcon { + let labelIconView: UIImageView + if let current = self.labelIconView { + labelIconView = current + } else { + labelIconView = UIImageView() + self.labelIconView = labelIconView + self.containerButton.addSubview(labelIconView) + } + labelIconView.image = labelIcon + + let labelIconFrame = CGRect(origin: CGPoint(x: availableSize.width - rightInset - 48.0 + floor((48.0 - labelIcon.size.width) * 0.5), y: floor((height - verticalInset * 2.0 - labelIcon.size.height) / 2.0)), size: CGSize(width: labelIcon.size.width, height: labelIcon.size.height)) + transition.setFrame(view: labelIconView, frame: labelIconFrame) + } else { + if let labelIconView = self.labelIconView { + self.labelIconView = nil + labelIconView.removeFromSuperview() + } + } + if let labelView = self.label.view { if labelView.superview == nil { labelView.isUserInteractionEnabled = false diff --git a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift index ec54736a9d..ff4d9dc468 100644 --- a/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift +++ b/submodules/TelegramUI/Components/SendInviteLinkScreen/Sources/SendInviteLinkScreen.swift @@ -22,13 +22,13 @@ private final class SendInviteLinkScreenComponent: Component { let context: AccountContext let link: String? - let peers: [EnginePeer] + let peers: [TelegramForbiddenInvitePeer] let peerPresences: [EnginePeer.Id: EnginePeer.Presence] init( context: AccountContext, link: String?, - peers: [EnginePeer], + peers: [TelegramForbiddenInvitePeer], peerPresences: [EnginePeer.Id: EnginePeer.Presence] ) { self.context = context @@ -272,7 +272,9 @@ private final class SendInviteLinkScreenComponent: Component { if self.component == nil { for peer in component.peers { - self.selectedItems.insert(peer.id) + if component.link != nil && !peer.premiumRequiredToContact { + self.selectedItems.insert(peer.peer.id) + } } } @@ -280,8 +282,15 @@ private final class SendInviteLinkScreenComponent: Component { self.state = state self.environment = environment - let hasPremiumRestrictedUsers = "".isEmpty - let hasInviteLink = "".isEmpty + let premiumRestrictedUsers = component.peers.filter { peer in + return peer.canInviteWithPremium + } + var hasInviteLink = true + if premiumRestrictedUsers.count == component.peers.count && component.link == nil { + hasInviteLink = false + } else if component.link != nil && !premiumRestrictedUsers.isEmpty && component.peers.allSatisfy({ $0.premiumRequiredToContact }) { + hasInviteLink = false + } if themeUpdated { self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.5) @@ -312,12 +321,13 @@ private final class SendInviteLinkScreenComponent: Component { self.scrollContentView.addSubview(avatarsNode.view) } - let avatarsContent = self.avatarsContext.update(peers: component.peers.count <= 3 ? component.peers : Array(component.peers.prefix(upTo: 3)), animated: false) + let avatarPeers = component.peers.map(\.peer) + let avatarsContent = self.avatarsContext.update(peers: avatarPeers.count <= 3 ? avatarPeers : Array(avatarPeers.prefix(upTo: 3)), animated: false) let avatarsSize = avatarsNode.update( context: component.context, content: avatarsContent, itemSize: CGSize(width: 60.0, height: 60.0), - customSpacing: 50.0, + customSpacing: 30.0, font: avatarPlaceholderFont(size: 28.0), animated: false, synchronousLoad: true @@ -347,7 +357,7 @@ private final class SendInviteLinkScreenComponent: Component { transition.setFrame(view: leftButtonView, frame: leftButtonFrame) } - if hasPremiumRestrictedUsers { + if !premiumRestrictedUsers.isEmpty { var premiumItemsTransition = transition let premiumTitle: ComponentView @@ -397,20 +407,20 @@ private final class SendInviteLinkScreenComponent: Component { //TODO:localize let text: String - if component.peers.count == 1 { - text = "**\(component.peers[0].compactDisplayTitle)** accepts invitations to groups from contacts and **Premium** users." + if premiumRestrictedUsers.count == 1 { + text = "**\(premiumRestrictedUsers[0].peer.compactDisplayTitle)** accepts invitations to groups from contacts and **Premium** users." } else { - let extraCount = component.peers.count - 3 + let extraCount = premiumRestrictedUsers.count - 3 var peersText = "" - for i in 0 ..< min(3, component.peers.count) { - if extraCount == 0 && i == component.peers.count - 1 { + for i in 0 ..< min(3, premiumRestrictedUsers.count) { + if extraCount == 0 && i == premiumRestrictedUsers.count - 1 { peersText.append(", and ") } else if i != 0 { peersText.append(", ") } peersText.append("**") - peersText.append(component.peers[i].compactDisplayTitle) + peersText.append(premiumRestrictedUsers[i].peer.compactDisplayTitle) peersText.append("**") } @@ -641,13 +651,13 @@ private final class SendInviteLinkScreenComponent: Component { contentHeight += 8.0 let text: String - if hasPremiumRestrictedUsers { + if !premiumRestrictedUsers.isEmpty { if component.link != nil { //TODO:localize text = "You can try to send an invite link instead." } else { if component.peers.count == 1 { - text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].displayTitle(strings: environment.strings, displayOrder: .firstLast)).string + text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string } else { text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count)) } @@ -655,13 +665,13 @@ private final class SendInviteLinkScreenComponent: Component { } else { if component.link != nil { if component.peers.count == 1 { - text = environment.strings.SendInviteLink_TextAvailableSingleUser(component.peers[0].displayTitle(strings: environment.strings, displayOrder: .firstLast)).string + text = environment.strings.SendInviteLink_TextAvailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string } else { text = environment.strings.SendInviteLink_TextAvailableMultipleUsers(Int32(component.peers.count)) } } else { if component.peers.count == 1 { - text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].displayTitle(strings: environment.strings, displayOrder: .firstLast)).string + text = environment.strings.SendInviteLink_TextUnavailableSingleUser(component.peers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)).string } else { text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count)) } @@ -696,6 +706,7 @@ private final class SendInviteLinkScreenComponent: Component { contentHeight += descriptionTextFrame.height contentHeight += 22.0 + initialContentHeight = contentHeight var singleItemHeight: CGFloat = 0.0 @@ -706,7 +717,7 @@ private final class SendInviteLinkScreenComponent: Component { for _ in 0 ..< 1 { //let id: AnyHashable = AnyHashable("\(peer.id)_\(j)") - let id = AnyHashable(peer.id) + let id = AnyHashable(peer.peer.id) validIds.append(id) let item: ComponentView @@ -719,6 +730,15 @@ private final class SendInviteLinkScreenComponent: Component { self.items[id] = item } + let itemSubtitle: PeerListItemComponent.Subtitle + let canBeSelected = component.link != nil && !peer.premiumRequiredToContact + if peer.premiumRequiredToContact { + //TODO:localize + itemSubtitle = .text(text: "Available only to premium users", icon: .lock) + } else { + itemSubtitle = .presence(component.peerPresences[peer.peer.id]) + } + let itemSize = item.update( transition: itemTransition, component: AnyComponent(PeerListItemComponent( @@ -726,15 +746,18 @@ private final class SendInviteLinkScreenComponent: Component { theme: environment.theme, strings: environment.strings, sideInset: 0.0, - title: peer.displayTitle(strings: environment.strings, displayOrder: .firstLast), - peer: peer, - presence: component.peerPresences[peer.id], - selectionState: component.link == nil ? .none : .editing(isSelected: self.selectedItems.contains(peer.id)), + title: peer.peer.displayTitle(strings: environment.strings, displayOrder: .firstLast), + subtitle: itemSubtitle, + peer: peer.peer, + selectionState: !canBeSelected ? .none : .editing(isSelected: self.selectedItems.contains(peer.peer.id)), hasNext: i != component.peers.count - 1, action: { [weak self] peer in guard let self else { return } + if !canBeSelected { + return + } if self.selectedItems.contains(peer.id) { self.selectedItems.remove(peer.id) } else { @@ -771,7 +794,6 @@ private final class SendInviteLinkScreenComponent: Component { } transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: CGSize(width: availableSize.width - sideInset * 2.0, height: itemsHeight))) - initialContentHeight += singleItemHeight + 16.0 initialContentHeight += min(itemsHeight, floor(singleItemHeight * 2.5)) contentHeight += itemsHeight @@ -805,16 +827,16 @@ private final class SendInviteLinkScreenComponent: Component { if self.selectedItems.isEmpty { controller.dismiss() } else if let link = component.link { - let selectedPeers = component.peers.filter { self.selectedItems.contains($0.id) } + let selectedPeers = component.peers.filter { self.selectedItems.contains($0.peer.id) } let _ = enqueueMessagesToMultiplePeers(account: component.context.account, peerIds: Array(self.selectedItems), threadIds: [:], messages: [.message(text: link, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).start() let text: String if selectedPeers.count == 1 { - text = environment.strings.Conversation_ShareLinkTooltip_Chat_One(selectedPeers[0].displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: "")).string + text = environment.strings.Conversation_ShareLinkTooltip_Chat_One(selectedPeers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: "")).string } else if selectedPeers.count == 2 { - text = environment.strings.Conversation_ShareLinkTooltip_TwoChats_One(selectedPeers[0].displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: ""), selectedPeers[1].displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: "")).string + text = environment.strings.Conversation_ShareLinkTooltip_TwoChats_One(selectedPeers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: ""), selectedPeers[1].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: "")).string } else { - text = environment.strings.Conversation_ShareLinkTooltip_ManyChats_One(selectedPeers[0].displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: ""), "\(selectedPeers.count - 1)").string + text = environment.strings.Conversation_ShareLinkTooltip_ManyChats_One(selectedPeers[0].peer.displayTitle(strings: environment.strings, displayOrder: .firstLast).replacingOccurrences(of: "*", with: ""), "\(selectedPeers.count - 1)").string } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } @@ -863,7 +885,7 @@ private final class SendInviteLinkScreenComponent: Component { let topInset: CGFloat = max(0.0, availableSize.height - containerInset - initialContentHeight) - let scrollContentHeight = max(topInset + contentHeight, availableSize.height - containerInset) + let scrollContentHeight = max(topInset + contentHeight + containerInset, availableSize.height - containerInset) self.scrollContentClippingView.layer.cornerRadius = 10.0 @@ -906,13 +928,13 @@ private final class SendInviteLinkScreenComponent: Component { public class SendInviteLinkScreen: ViewControllerComponentContainer { private let context: AccountContext private let link: String? - private let peers: [EnginePeer] + private let peers: [TelegramForbiddenInvitePeer] private var isDismissed: Bool = false private var presenceDisposable: Disposable? - public init(context: AccountContext, peer: EnginePeer, link: String?, peers: [EnginePeer]) { + public init(context: AccountContext, peer: EnginePeer, link: String?, peers: [TelegramForbiddenInvitePeer]) { self.context = context var link = link @@ -920,6 +942,120 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer { link = "https://t.me/\(addressName)" } + #if DEBUG + var peers = peers + + if !"".isEmpty { + enum TestConfiguration: CaseIterable { + case singlePeerNoPremiumLink + case singlePeerPremiumLink + case singlePeerNoPremiumNoLink + case singlePeerPremiumNoLink + case somePeersNoPremiumLink + case somePeersOnePremiumLink + case somePeersAllPremiumLink + case somePeersNoPremiumNoLink + case somePeersOnePremiumNoLink + case somePeersAllPremiumNoLink + case morePeersNoPremiumLink + case morePeersOnePremiumLink + case morePeersAllPremiumLink + case morePeersNoPremiumNoLink + case morePeersOnePremiumNoLink + case morePeersAllPremiumNoLink + } + + var nextPeerId: Int64 = 1 + let makePeer: (Bool, Bool) -> TelegramForbiddenInvitePeer = { canInviteWithPremium, premiumRequiredToContact in + guard case let .user(user) = peers[0].peer else { + preconditionFailure() + } + let id = nextPeerId + nextPeerId += 1 + return TelegramForbiddenInvitePeer( + peer: .user(TelegramUser( + id: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id)), + accessHash: user.accessHash, + firstName: user.firstName, + lastName: user.lastName, + username: user.username, + phone: user.phone, + photo: user.photo, + botInfo: user.botInfo, + restrictionInfo: user.restrictionInfo, + flags: user.flags, + emojiStatus: user.emojiStatus, + usernames: user.usernames, + storiesHidden: user.storiesHidden, + nameColor: user.nameColor, + backgroundEmojiId: user.backgroundEmojiId, + profileColor: user.profileColor, + profileBackgroundEmojiId: user.profileBackgroundEmojiId + )), + canInviteWithPremium: canInviteWithPremium, + premiumRequiredToContact: premiumRequiredToContact + ) + } + + let caseIndex = 9 + let configuration = TestConfiguration.allCases[caseIndex] + do { + switch configuration { + case .singlePeerNoPremiumLink: + peers = [makePeer(false, false)] + link = "abcd" + case .singlePeerPremiumLink: + peers = [makePeer(true, false)] + link = "abcd" + case .singlePeerNoPremiumNoLink: + peers = [makePeer(false, false)] + link = nil + case .singlePeerPremiumNoLink: + peers = [makePeer(true, false)] + link = nil + case .somePeersNoPremiumLink: + peers = (0 ..< 3).map { _ in makePeer(false, false) } + link = "abcd" + case .somePeersOnePremiumLink: + peers = [ + makePeer(false, false), + makePeer(true, true), + makePeer(false, false) + ] + link = "abcd" + case .somePeersAllPremiumLink: + peers = (0 ..< 3).map { _ in makePeer(true, false) } + link = "abcd" + case .somePeersNoPremiumNoLink: + peers = (0 ..< 3).map { _ in makePeer(false, false) } + link = nil + case .somePeersOnePremiumNoLink: + peers = [ + makePeer(false, false), + makePeer(true, false), + makePeer(false, false) + ] + link = nil + case .somePeersAllPremiumNoLink: + peers = (0 ..< 3).map { _ in makePeer(true, false) } + link = nil + case .morePeersNoPremiumLink: + preconditionFailure() + case .morePeersOnePremiumLink: + preconditionFailure() + case .morePeersAllPremiumLink: + preconditionFailure() + case .morePeersNoPremiumNoLink: + preconditionFailure() + case .morePeersOnePremiumNoLink: + preconditionFailure() + case .morePeersAllPremiumNoLink: + preconditionFailure() + } + } + } + #endif + self.link = link self.peers = peers @@ -930,7 +1066,7 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer { self.blocksBackgroundWhenInOverlay = true self.presenceDisposable = (context.engine.data.subscribe(EngineDataMap( - peers.map(\.id).map(TelegramEngine.EngineData.Item.Peer.Presence.init(id:)) + peers.map(\.peer.id).map(TelegramEngine.EngineData.Item.Peer.Presence.init(id:)) )) |> deliverOnMainQueue).start(next: { [weak self] presences in guard let self else { diff --git a/submodules/TelegramUI/Sources/ComposeControllerNode.swift b/submodules/TelegramUI/Sources/ComposeControllerNode.swift index 59c8835503..6bb6a6b24b 100644 --- a/submodules/TelegramUI/Sources/ComposeControllerNode.swift +++ b/submodules/TelegramUI/Sources/ComposeControllerNode.swift @@ -52,7 +52,7 @@ final class ComposeControllerNode: ASDisplayNode { ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: { openCreateNewChannelImpl?() }) - ], includeChatList: false, topPeers: .none)), onlyWriteable: false, displayPermissionPlaceholder: false) + ], includeChatList: false, topPeers: .none)), onlyWriteable: false, isGroupInvitation: false, displayPermissionPlaceholder: false) super.init() diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift index 2a6f453bb1..a21c3faef0 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift @@ -83,6 +83,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection private let options: [ContactListAdditionalOption] private let filters: [ContactListFilter] private let onlyWriteable: Bool + private let isGroupInvitation: Bool private let limit: Int32? init(_ params: ContactMultiselectionControllerParams) { @@ -94,6 +95,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection self.options = params.options self.filters = params.filters self.onlyWriteable = params.onlyWriteable + self.isGroupInvitation = params.isGroupInvitation self.limit = params.limit self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 } @@ -302,7 +304,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection } override func loadDisplayNode() { - self.displayNode = ContactMultiselectionControllerNode(navigationBar: self.navigationBar, context: self.context, presentationData: self.presentationData, mode: self.mode, isPeerEnabled: self.isPeerEnabled, attemptDisabledItemSelection: self.attemptDisabledItemSelection, options: self.options, filters: self.filters, onlyWriteable: self.onlyWriteable, limit: self.limit, reachedSelectionLimit: self.params.reachedLimit, present: { [weak self] c, a in + self.displayNode = ContactMultiselectionControllerNode(navigationBar: self.navigationBar, context: self.context, presentationData: self.presentationData, mode: self.mode, isPeerEnabled: self.isPeerEnabled, attemptDisabledItemSelection: self.attemptDisabledItemSelection, options: self.options, filters: self.filters, onlyWriteable: self.onlyWriteable, isGroupInvitation: self.isGroupInvitation, limit: self.limit, reachedSelectionLimit: self.params.reachedLimit, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }) switch self.contactsNode.contentNode { diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index 3d6b9bafc0..4929a3a737 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -78,8 +78,9 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { private let isPeerEnabled: ((EnginePeer) -> Bool)? private let onlyWriteable: Bool + private let isGroupInvitation: Bool - init(navigationBar: NavigationBar?, context: AccountContext, presentationData: PresentationData, mode: ContactMultiselectionControllerMode, isPeerEnabled: ((EnginePeer) -> Bool)?, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?, options: [ContactListAdditionalOption], filters: [ContactListFilter], onlyWriteable: Bool, limit: Int32?, reachedSelectionLimit: ((Int32) -> Void)?, present: @escaping (ViewController, Any?) -> Void) { + init(navigationBar: NavigationBar?, context: AccountContext, presentationData: PresentationData, mode: ContactMultiselectionControllerMode, isPeerEnabled: ((EnginePeer) -> Bool)?, attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?, options: [ContactListAdditionalOption], filters: [ContactListFilter], onlyWriteable: Bool, isGroupInvitation: Bool, limit: Int32?, reachedSelectionLimit: ((Int32) -> Void)?, present: @escaping (ViewController, Any?) -> Void) { self.navigationBar = navigationBar self.context = context @@ -90,6 +91,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { self.isPeerEnabled = isPeerEnabled self.onlyWriteable = onlyWriteable + self.isGroupInvitation = isGroupInvitation var proceedImpl: (() -> Void)? @@ -231,7 +233,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { } else { displayTopPeers = .none } - let contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)), filters: filters, onlyWriteable: onlyWriteable, selectionState: ContactListNodeGroupSelectionState()) + let contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList, topPeers: displayTopPeers)), filters: filters, onlyWriteable: onlyWriteable, isGroupInvitation: isGroupInvitation, selectionState: ContactListNodeGroupSelectionState()) self.contentNode = .contacts(contactListNode) if !selectedPeers.isEmpty { @@ -359,7 +361,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { searchChannels: searchChannels, globalSearch: globalSearch, displaySavedMessages: displaySavedMessages - ))), filters: filters, onlyWriteable: strongSelf.onlyWriteable, isPeerEnabled: strongSelf.isPeerEnabled, selectionState: selectionState, isSearch: true) + ))), filters: filters, onlyWriteable: strongSelf.onlyWriteable, isGroupInvitation: strongSelf.isGroupInvitation, isPeerEnabled: strongSelf.isPeerEnabled, selectionState: selectionState, isSearch: true) searchResultsNode.openPeer = { peer, _ in self?.tokenListNode.setText("") self?.openPeer?(peer) diff --git a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift index 0a446de4a4..3285c1f824 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift @@ -68,7 +68,7 @@ final class ContactSelectionControllerNode: ASDisplayNode { self.filters = filters var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? - self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false, topPeers: .none)), filters: filters, onlyWriteable: false, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in + self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false, topPeers: .none)), filters: filters, onlyWriteable: false, isGroupInvitation: false, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in contextActionImpl?(peer, node, gesture, nil) } : nil, multipleSelection: multipleSelection) diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index e85dadea05..a47f348bc9 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -676,7 +676,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] case .supergroup: createSignal = context.engine.peers.createSupergroup(title: title, description: nil) |> map { peerId -> CreateGroupResult? in - return CreateGroupResult(peerId: peerId, failedToInvitePeerIds: []) + return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: [])) } |> mapError { error -> CreateGroupError in switch error { @@ -705,7 +705,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] } return context.engine.peers.createSupergroup(title: title, description: nil, location: (location.latitude, location.longitude, address)) |> map { peerId -> CreateGroupResult? in - return CreateGroupResult(peerId: peerId, failedToInvitePeerIds: []) + return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: [])) } |> mapError { error -> CreateGroupError in switch error { @@ -731,7 +731,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] let createGroupSignal: (Bool) -> Signal = { isForum in return context.engine.peers.createSupergroup(title: title, description: nil, isForum: isForum) |> map { peerId -> CreateGroupResult? in - return CreateGroupResult(peerId: peerId, failedToInvitePeerIds: []) + return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: [])) } |> mapError { error -> CreateGroupError in switch error { @@ -834,7 +834,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: result.peerId)) replaceControllerImpl?(controller) - if !result.failedToInvitePeerIds.isEmpty { + if !result.result.forbiddenPeers.isEmpty { context.account.viewTracker.forceUpdateCachedPeerData(peerId: result.peerId) let _ = (context.engine.data.subscribe( TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: result.peerId) @@ -847,26 +847,10 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] TelegramEngine.EngineData.Item.Peer.Peer(id: result.peerId) ) |> deliverOnMainQueue).start(next: { peer in - let _ = controller - let _ = exportedInvitation - if let peer, let exportedInvitation, let link = exportedInvitation.link { - let _ = (context.engine.data.get( - EngineDataList(result.failedToInvitePeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) - ) - |> deliverOnMainQueue).start(next: { peerItems in - guard let controller else { - return - } - let _ = controller - let _ = peerItems - - let peers = peerItems.compactMap { $0 } - if !peers.isEmpty { - let inviteScreen = SendInviteLinkScreen(context: context, peer: peer, link: link, peers: peers) - controller.push(inviteScreen) - } - }) + + let inviteScreen = SendInviteLinkScreen(context: context, peer: peer, link: link, peers: result.result.forbiddenPeers) + controller?.push(inviteScreen) } }) })