mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Group invitation sheet
This commit is contained in:
parent
4a8a14cb9f
commit
38bc517122
@ -96,19 +96,21 @@ public final class ContactMultiselectionControllerParams {
|
|||||||
public let options: [ContactListAdditionalOption]
|
public let options: [ContactListAdditionalOption]
|
||||||
public let filters: [ContactListFilter]
|
public let filters: [ContactListFilter]
|
||||||
public let onlyWriteable: Bool
|
public let onlyWriteable: Bool
|
||||||
|
public let isGroupInvitation: Bool
|
||||||
public let isPeerEnabled: ((EnginePeer) -> Bool)?
|
public let isPeerEnabled: ((EnginePeer) -> Bool)?
|
||||||
public let attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
public let attemptDisabledItemSelection: ((EnginePeer, ChatListDisabledPeerReason) -> Void)?
|
||||||
public let alwaysEnabled: Bool
|
public let alwaysEnabled: Bool
|
||||||
public let limit: Int32?
|
public let limit: Int32?
|
||||||
public let reachedLimit: ((Int32) -> Void)?
|
public let reachedLimit: ((Int32) -> Void)?
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = 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<PresentationData, NoError>)? = 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.context = context
|
||||||
self.updatedPresentationData = updatedPresentationData
|
self.updatedPresentationData = updatedPresentationData
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.options = options
|
self.options = options
|
||||||
self.filters = filters
|
self.filters = filters
|
||||||
self.onlyWriteable = onlyWriteable
|
self.onlyWriteable = onlyWriteable
|
||||||
|
self.isGroupInvitation = isGroupInvitation
|
||||||
self.isPeerEnabled = isPeerEnabled
|
self.isPeerEnabled = isPeerEnabled
|
||||||
self.attemptDisabledItemSelection = attemptDisabledItemSelection
|
self.attemptDisabledItemSelection = attemptDisabledItemSelection
|
||||||
self.alwaysEnabled = alwaysEnabled
|
self.alwaysEnabled = alwaysEnabled
|
||||||
|
|||||||
@ -1059,7 +1059,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let isPeerEnabled: ((EnginePeer) -> Bool)?
|
private let isPeerEnabled: ((EnginePeer) -> Bool)?
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, presentation: Signal<ContactListPresentation, NoError>, 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<PresentationData, NoError>)? = nil, presentation: Signal<ContactListPresentation, NoError>, 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.context = context
|
||||||
self.filters = filters
|
self.filters = filters
|
||||||
self.displayPermissionPlaceholder = displayPermissionPlaceholder
|
self.displayPermissionPlaceholder = displayPermissionPlaceholder
|
||||||
@ -1383,7 +1383,7 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let peerRequiresPremiumForMessaging: Signal<[EnginePeer.Id: Bool], NoError>
|
let peerRequiresPremiumForMessaging: Signal<[EnginePeer.Id: Bool], NoError>
|
||||||
if onlyWriteable {
|
if onlyWriteable && !isGroupInvitation {
|
||||||
peerRequiresPremiumForMessaging = foundPeers.get()
|
peerRequiresPremiumForMessaging = foundPeers.get()
|
||||||
|> map { foundPeers -> Set<EnginePeer.Id> in
|
|> map { foundPeers -> Set<EnginePeer.Id> in
|
||||||
var result = Set<EnginePeer.Id>()
|
var result = Set<EnginePeer.Id>()
|
||||||
|
|||||||
@ -114,7 +114,7 @@ final class ContactsControllerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
|
|
||||||
var contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?, Bool) -> Void)?
|
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)
|
contextAction?(peer, node, gesture, location, isStories)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1244,8 +1244,8 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||||||
return current.withUpdatedUpdating(false)
|
return current.withUpdatedUpdating(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let adminPeer {
|
if let adminPeer, case let .restricted(forbiddenPeer) = error {
|
||||||
let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [adminPeer])
|
let inviteScreen = SendInviteLinkScreen(context: context, peer: channelPeer, link: exportedInvitation?.link, peers: [forbiddenPeer ?? TelegramForbiddenInvitePeer(peer: adminPeer, canInviteWithPremium: false, premiumRequiredToContact: false)])
|
||||||
pushControllerImpl?(inviteScreen)
|
pushControllerImpl?(inviteScreen)
|
||||||
|
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
@ -1433,9 +1433,9 @@ public func channelAdminController(context: AccountContext, updatedPresentationD
|
|||||||
return current.withUpdatedUpdating(true)
|
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
|
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 {
|
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)
|
pushControllerImpl?(inviteScreen)
|
||||||
|
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
|||||||
@ -549,25 +549,25 @@ public func channelMembersController(context: AccountContext, updatedPresentatio
|
|||||||
contactsController?.dismiss()
|
contactsController?.dismiss()
|
||||||
} else {
|
} else {
|
||||||
if let chatPeer {
|
if let chatPeer {
|
||||||
let _ = (context.engine.data.get(
|
let failedPeers = failedPeerIds.compactMap { _, error -> TelegramForbiddenInvitePeer? in
|
||||||
EngineDataList(failedPeerIds.compactMap { item -> EnginePeer.Id? in
|
if case let .restricted(peer) = error {
|
||||||
return item.0
|
return peer
|
||||||
}.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)
|
|
||||||
}
|
|
||||||
} else {
|
} 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
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,15 +4,50 @@ import SwiftSignalKit
|
|||||||
import TelegramApi
|
import TelegramApi
|
||||||
import MtProtoKit
|
import MtProtoKit
|
||||||
|
|
||||||
|
|
||||||
public enum AddGroupMemberError {
|
public enum AddGroupMemberError {
|
||||||
case generic
|
case generic
|
||||||
case groupFull
|
case groupFull
|
||||||
case privacy
|
case privacy(TelegramInvitePeersResult?)
|
||||||
case notMutualContact
|
case notMutualContact
|
||||||
case tooManyChannels
|
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<Void, AddGroupMemberError> {
|
func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId) -> Signal<Void, AddGroupMemberError> {
|
||||||
return account.postbox.transaction { transaction -> Signal<Void, AddGroupMemberError> in
|
return account.postbox.transaction { transaction -> Signal<Void, AddGroupMemberError> in
|
||||||
if let peer = transaction.getPeer(peerId), let memberPeer = transaction.getPeer(memberId), let inputUser = apiInputUser(memberPeer) {
|
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":
|
case "USERS_TOO_MUCH":
|
||||||
return .groupFull
|
return .groupFull
|
||||||
case "USER_PRIVACY_RESTRICTED":
|
case "USER_PRIVACY_RESTRICTED":
|
||||||
return .privacy
|
return .privacy(nil)
|
||||||
case "USER_CHANNELS_TOO_MUCH":
|
case "USER_CHANNELS_TOO_MUCH":
|
||||||
return .tooManyChannels
|
return .tooManyChannels
|
||||||
case "USER_NOT_MUTUAL_CONTACT":
|
case "USER_NOT_MUTUAL_CONTACT":
|
||||||
@ -34,14 +69,16 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId
|
|||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<Void, AddGroupMemberError> in
|
|> mapToSignal { result -> Signal<Void, AddGroupMemberError> in
|
||||||
let updatesValue: Api.Updates
|
let updatesValue: Api.Updates
|
||||||
|
let missingInviteesValue: [Api.MissingInvitee]
|
||||||
switch result {
|
switch result {
|
||||||
case let .invitedUsers(updates, missingInvitees):
|
case let .invitedUsers(updates, missingInvitees):
|
||||||
let _ = missingInvitees
|
|
||||||
updatesValue = updates
|
updatesValue = updates
|
||||||
|
missingInviteesValue = missingInvitees
|
||||||
}
|
}
|
||||||
|
|
||||||
account.stateManager.addUpdates(updatesValue)
|
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 {
|
if let message = updatesValue.messages.first, let timestamp = message.timestamp {
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, cachedData -> CachedPeerData? in
|
||||||
if let cachedData = cachedData as? CachedGroupData, let participants = cachedData.participants {
|
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 }
|
|> mapError { _ -> AddGroupMemberError in }
|
||||||
|
|> mapToSignal { result -> Signal<Void, AddGroupMemberError> in
|
||||||
|
if result.forbiddenPeers.isEmpty {
|
||||||
|
return .single(Void())
|
||||||
|
} else {
|
||||||
|
return .fail(.privacy(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
@ -76,7 +134,7 @@ func _internal_addGroupMember(account: Account, peerId: PeerId, memberId: PeerId
|
|||||||
|
|
||||||
public enum AddChannelMemberError {
|
public enum AddChannelMemberError {
|
||||||
case generic
|
case generic
|
||||||
case restricted
|
case restricted(TelegramForbiddenInvitePeer?)
|
||||||
case notMutualContact
|
case notMutualContact
|
||||||
case limitExceeded
|
case limitExceeded
|
||||||
case tooMuchJoined
|
case tooMuchJoined
|
||||||
@ -108,7 +166,7 @@ func _internal_addChannelMember(account: Account, peerId: PeerId, memberId: Peer
|
|||||||
case "USERS_TOO_MUCH":
|
case "USERS_TOO_MUCH":
|
||||||
return .fail(.limitExceeded)
|
return .fail(.limitExceeded)
|
||||||
case "USER_PRIVACY_RESTRICTED":
|
case "USER_PRIVACY_RESTRICTED":
|
||||||
return .fail(.restricted)
|
return .fail(.restricted(nil))
|
||||||
case "USER_NOT_MUTUAL_CONTACT":
|
case "USER_NOT_MUTUAL_CONTACT":
|
||||||
return .fail(.notMutualContact)
|
return .fail(.notMutualContact)
|
||||||
case "USER_BOT":
|
case "USER_BOT":
|
||||||
@ -127,7 +185,14 @@ func _internal_addChannelMember(account: Account, peerId: PeerId, memberId: Peer
|
|||||||
let updatesValue: Api.Updates
|
let updatesValue: Api.Updates
|
||||||
switch result {
|
switch result {
|
||||||
case let .invitedUsers(updates, missingInvitees):
|
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
|
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<Void, AddChannelMemberError> {
|
func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [PeerId]) -> Signal<TelegramInvitePeersResult, AddChannelMemberError> {
|
||||||
let signal = account.postbox.transaction { transaction -> Signal<Void, AddChannelMemberError> in
|
let signal = account.postbox.transaction { transaction -> Signal<TelegramInvitePeersResult, AddChannelMemberError> in
|
||||||
var memberPeerIds: [PeerId:Peer] = [:]
|
var memberPeerIds: [PeerId:Peer] = [:]
|
||||||
var inputUsers: [Api.InputUser] = []
|
var inputUsers: [Api.InputUser] = []
|
||||||
for memberId in memberIds {
|
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) {
|
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<TelegramInvitePeersResult, AddChannelMemberError> = account.network.request(Api.functions.channels.inviteToChannel(channel: inputChannel, users: inputUsers))
|
||||||
|> mapError { error -> AddChannelMemberError in
|
|> mapError { error -> AddChannelMemberError in
|
||||||
switch error.errorDescription {
|
switch error.errorDescription {
|
||||||
case "CHANNELS_TOO_MUCH":
|
case "CHANNELS_TOO_MUCH":
|
||||||
return .tooMuchJoined
|
return .tooMuchJoined
|
||||||
case "USER_PRIVACY_RESTRICTED":
|
case "USER_PRIVACY_RESTRICTED":
|
||||||
return .restricted
|
return .restricted(nil)
|
||||||
case "USER_NOT_MUTUAL_CONTACT":
|
case "USER_NOT_MUTUAL_CONTACT":
|
||||||
return .notMutualContact
|
return .notMutualContact
|
||||||
case "USERS_TOO_MUCH":
|
case "USERS_TOO_MUCH":
|
||||||
@ -224,21 +289,39 @@ func _internal_addChannelMembers(account: Account, peerId: PeerId, memberIds: [P
|
|||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> map { result in
|
|> mapToQueue { result -> Signal<TelegramInvitePeersResult, AddChannelMemberError> in
|
||||||
let updatesValue: Api.Updates
|
let updatesValue: Api.Updates
|
||||||
|
let missingInviteesValue: [Api.MissingInvitee]
|
||||||
switch result {
|
switch result {
|
||||||
case let .invitedUsers(updates, missingInvitees):
|
case let .invitedUsers(updates, missingInvitees):
|
||||||
let _ = missingInvitees
|
|
||||||
updatesValue = updates
|
updatesValue = updates
|
||||||
|
missingInviteesValue = missingInvitees
|
||||||
}
|
}
|
||||||
|
|
||||||
account.stateManager.addUpdates(updatesValue)
|
account.stateManager.addUpdates(updatesValue)
|
||||||
account.viewTracker.forceUpdateCachedPeerData(peerId: peerId)
|
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
|
return signal
|
||||||
} else {
|
} else {
|
||||||
return .single(Void())
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,14 +15,14 @@ public enum CreateGroupError {
|
|||||||
|
|
||||||
public struct CreateGroupResult {
|
public struct CreateGroupResult {
|
||||||
public var peerId: EnginePeer.Id
|
public var peerId: EnginePeer.Id
|
||||||
public var failedToInvitePeerIds: [EnginePeer.Id]
|
public var result: TelegramInvitePeersResult
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
peerId: EnginePeer.Id,
|
peerId: EnginePeer.Id,
|
||||||
failedToInvitePeerIds: [EnginePeer.Id]
|
result: TelegramInvitePeersResult
|
||||||
) {
|
) {
|
||||||
self.peerId = peerId
|
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
|
return .generic
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<CreateGroupResult?, CreateGroupError> in
|
|> mapToSignal { result -> Signal<CreateGroupResult?, CreateGroupError> in
|
||||||
var failedToInvitePeerIds: [EnginePeer.Id] = []
|
|
||||||
failedToInvitePeerIds = []
|
|
||||||
|
|
||||||
let updatesValue: Api.Updates
|
let updatesValue: Api.Updates
|
||||||
|
let missingInviteesValue: [Api.MissingInvitee]
|
||||||
switch result {
|
switch result {
|
||||||
case let .invitedUsers(updates, missingInvitees):
|
case let .invitedUsers(updates, missingInvitees):
|
||||||
let _ = missingInvitees
|
|
||||||
updatesValue = updates
|
updatesValue = updates
|
||||||
|
missingInviteesValue = missingInvitees
|
||||||
}
|
}
|
||||||
|
|
||||||
account.stateManager.addUpdates(updatesValue)
|
account.stateManager.addUpdates(updatesValue)
|
||||||
@ -68,11 +66,26 @@ func _internal_createGroup(account: Account, title: String, peerIds: [PeerId], t
|
|||||||
}
|
}
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> castError(CreateGroupError.self)
|
|> castError(CreateGroupError.self)
|
||||||
|> map { _ -> CreateGroupResult in
|
|> mapToSignal { _ -> Signal<CreateGroupResult?, CreateGroupError> in
|
||||||
return CreateGroupResult(
|
return account.postbox.transaction { transaction -> CreateGroupResult in
|
||||||
peerId: peerId,
|
return CreateGroupResult(
|
||||||
failedToInvitePeerIds: failedToInvitePeerIds
|
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 {
|
} else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
|
|||||||
@ -78,7 +78,7 @@ func _internal_addGroupAdmin(account: Account, peerId: PeerId, adminId: PeerId)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||||
return .fail(.addMemberError(.privacy))
|
return .fail(.addMemberError(.privacy(nil)))
|
||||||
} else if error.errorDescription == "ADMINS_TOO_MUCH" {
|
} else if error.errorDescription == "ADMINS_TOO_MUCH" {
|
||||||
return .fail(.adminsTooMuch)
|
return .fail(.adminsTooMuch)
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ func _internal_updateChannelAdminRights(account: Account, peerId: PeerId, adminI
|
|||||||
} else if error.errorDescription == "USER_NOT_MUTUAL_CONTACT" {
|
} else if error.errorDescription == "USER_NOT_MUTUAL_CONTACT" {
|
||||||
return .fail(.addMemberError(.notMutualContact))
|
return .fail(.addMemberError(.notMutualContact))
|
||||||
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||||
return .fail(.addMemberError(.restricted))
|
return .fail(.addMemberError(.restricted(nil)))
|
||||||
} else if error.errorDescription == "USER_CHANNELS_TOO_MUCH" {
|
} else if error.errorDescription == "USER_CHANNELS_TOO_MUCH" {
|
||||||
return .fail(.addMemberError(.tooMuchJoined))
|
return .fail(.addMemberError(.tooMuchJoined))
|
||||||
} else if error.errorDescription == "ADMINS_TOO_MUCH" {
|
} else if error.errorDescription == "ADMINS_TOO_MUCH" {
|
||||||
|
|||||||
@ -589,7 +589,7 @@ public extension TelegramEngine {
|
|||||||
return _internal_sendBotRequestedPeer(account: self.account, peerId: messageId.peerId, messageId: messageId, buttonId: buttonId, requestedPeerIds: requestedPeerIds)
|
return _internal_sendBotRequestedPeer(account: self.account, peerId: messageId.peerId, messageId: messageId, buttonId: buttonId, requestedPeerIds: requestedPeerIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func addChannelMembers(peerId: PeerId, memberIds: [PeerId]) -> Signal<Void, AddChannelMemberError> {
|
public func addChannelMembers(peerId: PeerId, memberIds: [PeerId]) -> Signal<TelegramInvitePeersResult, AddChannelMemberError> {
|
||||||
return _internal_addChannelMembers(account: self.account, peerId: peerId, memberIds: memberIds)
|
return _internal_addChannelMembers(account: self.account, peerId: peerId, memberIds: memberIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -316,6 +316,7 @@ public enum PresentationResourceKey: Int32 {
|
|||||||
case sharedLinkIcon
|
case sharedLinkIcon
|
||||||
|
|
||||||
case hideIconImage
|
case hideIconImage
|
||||||
|
case peerStatusLockedImage
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ChatExpiredStoryIndicatorType: Hashable {
|
public enum ChatExpiredStoryIndicatorType: Hashable {
|
||||||
|
|||||||
@ -408,4 +408,10 @@ public struct PresentationResourcesItemList {
|
|||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/Archive/IconHide"), color: theme.list.itemAccentColor)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -274,6 +274,9 @@ private final class PeerInfoMembersContextImpl {
|
|||||||
deinit {
|
deinit {
|
||||||
self.disposable.dispose()
|
self.disposable.dispose()
|
||||||
self.peerDisposable.dispose()
|
self.peerDisposable.dispose()
|
||||||
|
for (_, disposable) in self.removingMemberIds {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pushState() {
|
private func pushState() {
|
||||||
@ -293,7 +296,7 @@ private final class PeerInfoMembersContextImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func removeMember(memberId: PeerId) {
|
func removeMember(memberId: PeerId) {
|
||||||
if removingMemberIds[memberId] == nil {
|
if self.removingMemberIds[memberId] == nil {
|
||||||
let signal: Signal<Never, NoError>
|
let signal: Signal<Never, NoError>
|
||||||
if self.peerId.namespace == Namespaces.Peer.CloudChannel {
|
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))
|
signal = context.peerChannelMemberCategoriesContextsManager.updateMemberBannedRights(engine: self.context.engine, peerId: self.peerId, memberId: memberId, bannedRights: TelegramChatBannedRights(flags: [.banReadMessages], untilDate: Int32.max))
|
||||||
|
|||||||
@ -12424,20 +12424,8 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
|||||||
}, clearHighlightAutomatically: true))
|
}, clearHighlightAutomatically: true))
|
||||||
}
|
}
|
||||||
|
|
||||||
let contactsController: ViewController
|
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))
|
||||||
/*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)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
contactsController.navigationPresentation = .modal
|
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
|
confirmationImpl = { [weak contactsController] peerId in
|
||||||
return context.account.postbox.loadedPeerWithId(peerId)
|
return context.account.postbox.loadedPeerWithId(peerId)
|
||||||
@ -12461,162 +12449,6 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*let addMember: (ContactListPeer) -> Signal<Void, NoError> = { [weak contactsController] memberPeer -> Signal<Void, NoError> 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<Void, NoError> 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<Void, NoError> 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<PeerId?, NoError> in
|
|
||||||
switch error {
|
|
||||||
case .tooManyChannels:
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
parentController?.push(oldChannelsController(context: context, intent: .upgrade))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return .single(nil)
|
|
||||||
}
|
|
||||||
|> mapToSignal { upgradedPeerId -> Signal<PeerId?, NoError> in
|
|
||||||
guard let upgradedPeerId = upgradedPeerId else {
|
|
||||||
return .single(nil)
|
|
||||||
}
|
|
||||||
return context.peerChannelMemberCategoriesContextsManager.addMember(engine: context.engine, peerId: upgradedPeerId, memberId: memberId)
|
|
||||||
|> `catch` { _ -> Signal<Never, NoError> in
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
|> mapToSignal { _ -> Signal<PeerId?, NoError> in
|
|
||||||
}
|
|
||||||
|> then(.single(upgradedPeerId))
|
|
||||||
}
|
|
||||||
|> deliverOnMainQueue
|
|
||||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
return signal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .complete()
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
let addMembers: ([ContactListPeerId]) -> Signal<[(PeerId, AddChannelMemberError)], NoError> = { members -> Signal<[(PeerId, AddChannelMemberError)], NoError> in
|
let addMembers: ([ContactListPeerId]) -> Signal<[(PeerId, AddChannelMemberError)], NoError> = { members -> Signal<[(PeerId, AddChannelMemberError)], NoError> in
|
||||||
let memberIds = members.compactMap { contact -> PeerId? in
|
let memberIds = members.compactMap { contact -> PeerId? in
|
||||||
switch contact {
|
switch contact {
|
||||||
@ -12652,8 +12484,8 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
|||||||
return .generic
|
return .generic
|
||||||
case .groupFull:
|
case .groupFull:
|
||||||
return .limitExceeded
|
return .limitExceeded
|
||||||
case .privacy:
|
case let .privacy(privacy):
|
||||||
return .restricted
|
return .restricted(privacy?.forbiddenPeers.first)
|
||||||
case .notMutualContact:
|
case .notMutualContact:
|
||||||
return .notMutualContact
|
return .notMutualContact
|
||||||
case .tooManyChannels:
|
case .tooManyChannels:
|
||||||
@ -12683,25 +12515,7 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
|||||||
}
|
}
|
||||||
|
|
||||||
parentController?.push(contactsController)
|
parentController?.push(contactsController)
|
||||||
/*if let contactsController = contactsController as? ContactSelectionController {
|
do {
|
||||||
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 {
|
|
||||||
selectAddMemberDisposable.set((
|
selectAddMemberDisposable.set((
|
||||||
combineLatest(queue: .mainQueue(),
|
combineLatest(queue: .mainQueue(),
|
||||||
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: groupPeer.id)),
|
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: groupPeer.id)),
|
||||||
@ -12743,56 +12557,25 @@ public func presentAddMembersImpl(context: AccountContext, updatedPresentationDa
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if "".isEmpty {
|
let failedPeers = failedPeerIds.compactMap { _, error -> TelegramForbiddenInvitePeer? in
|
||||||
let _ = (context.engine.data.get(
|
if case let .restricted(peer) = error {
|
||||||
EngineDataList(failedPeerIds.compactMap { item -> EnginePeer.Id? in
|
return peer
|
||||||
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
|
|
||||||
} else {
|
} 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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -1354,7 +1354,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
self.controller?.containerLayoutUpdated(layout, transition: .immediate)
|
self.controller?.containerLayoutUpdated(layout, transition: .immediate)
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
self.contactListNode = contactListNode
|
||||||
contactListNode.enableUpdates = true
|
contactListNode.enableUpdates = true
|
||||||
contactListNode.selectionStateUpdated = { [weak self] selectionState in
|
contactListNode.selectionStateUpdated = { [weak self] selectionState in
|
||||||
|
|||||||
@ -34,13 +34,22 @@ final class PeerListItemComponent: Component {
|
|||||||
case editing(isSelected: Bool)
|
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 context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let sideInset: CGFloat
|
let sideInset: CGFloat
|
||||||
let title: String
|
let title: String
|
||||||
|
let subtitle: Subtitle
|
||||||
let peer: EnginePeer?
|
let peer: EnginePeer?
|
||||||
let presence: EnginePeer.Presence?
|
|
||||||
let selectionState: SelectionState
|
let selectionState: SelectionState
|
||||||
let hasNext: Bool
|
let hasNext: Bool
|
||||||
let action: (EnginePeer) -> Void
|
let action: (EnginePeer) -> Void
|
||||||
@ -51,8 +60,8 @@ final class PeerListItemComponent: Component {
|
|||||||
strings: PresentationStrings,
|
strings: PresentationStrings,
|
||||||
sideInset: CGFloat,
|
sideInset: CGFloat,
|
||||||
title: String,
|
title: String,
|
||||||
|
subtitle: Subtitle,
|
||||||
peer: EnginePeer?,
|
peer: EnginePeer?,
|
||||||
presence: EnginePeer.Presence?,
|
|
||||||
selectionState: SelectionState,
|
selectionState: SelectionState,
|
||||||
hasNext: Bool,
|
hasNext: Bool,
|
||||||
action: @escaping (EnginePeer) -> Void
|
action: @escaping (EnginePeer) -> Void
|
||||||
@ -62,8 +71,8 @@ final class PeerListItemComponent: Component {
|
|||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.sideInset = sideInset
|
self.sideInset = sideInset
|
||||||
self.title = title
|
self.title = title
|
||||||
|
self.subtitle = subtitle
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.presence = presence
|
|
||||||
self.selectionState = selectionState
|
self.selectionState = selectionState
|
||||||
self.hasNext = hasNext
|
self.hasNext = hasNext
|
||||||
self.action = action
|
self.action = action
|
||||||
@ -85,10 +94,10 @@ final class PeerListItemComponent: Component {
|
|||||||
if lhs.title != rhs.title {
|
if lhs.title != rhs.title {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.peer != rhs.peer {
|
if lhs.subtitle != rhs.subtitle {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.presence != rhs.presence {
|
if lhs.peer != rhs.peer {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.selectionState != rhs.selectionState {
|
if lhs.selectionState != rhs.selectionState {
|
||||||
@ -108,6 +117,7 @@ final class PeerListItemComponent: Component {
|
|||||||
private let separatorLayer: SimpleLayer
|
private let separatorLayer: SimpleLayer
|
||||||
private let avatarNode: AvatarNode
|
private let avatarNode: AvatarNode
|
||||||
|
|
||||||
|
private var labelIconView: UIImageView?
|
||||||
private var checkLayer: CheckLayer?
|
private var checkLayer: CheckLayer?
|
||||||
|
|
||||||
private var component: PeerListItemComponent?
|
private var component: PeerListItemComponent?
|
||||||
@ -165,7 +175,7 @@ final class PeerListItemComponent: Component {
|
|||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
if let presence = component.presence {
|
if case let .presence(presence) = component.subtitle, let presence {
|
||||||
let statusManager: PeerPresenceStatusManager
|
let statusManager: PeerPresenceStatusManager
|
||||||
if let current = self.statusManager {
|
if let current = self.statusManager {
|
||||||
statusManager = current
|
statusManager = current
|
||||||
@ -240,11 +250,26 @@ final class PeerListItemComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var labelIcon: UIImage?
|
||||||
let labelData: (String, Bool)
|
let labelData: (String, Bool)
|
||||||
if let presence = component.presence {
|
switch component.subtitle {
|
||||||
labelData = stringAndActivityForUserPresence(strings: component.strings, dateTimeFormat: PresentationDateTimeFormat(), presence: presence, relativeTo: Int32(Date().timeIntervalSince1970))
|
case let .presence(presence):
|
||||||
} else {
|
if let presence {
|
||||||
labelData = (component.strings.LastSeen_Offline, false)
|
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(
|
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))
|
text: .plain(NSAttributedString(string: labelData.0, font: Font.regular(15.0), textColor: labelData.1 ? component.theme.list.itemAccentColor : component.theme.list.itemSecondaryTextColor))
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
containerSize: CGSize(width: maxTextSize, height: 100.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
let previousTitleFrame = self.title.view?.frame
|
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))
|
text: .plain(NSAttributedString(string: component.title, font: Font.semibold(17.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
containerSize: CGSize(width: maxTextSize, height: 100.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
let titleSpacing: CGFloat = 1.0
|
let titleSpacing: CGFloat = 1.0
|
||||||
@ -296,6 +321,27 @@ final class PeerListItemComponent: Component {
|
|||||||
transition.animateAlpha(view: titleView, from: 0.0, to: 1.0)
|
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 let labelView = self.label.view {
|
||||||
if labelView.superview == nil {
|
if labelView.superview == nil {
|
||||||
labelView.isUserInteractionEnabled = false
|
labelView.isUserInteractionEnabled = false
|
||||||
|
|||||||
@ -22,13 +22,13 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let link: String?
|
let link: String?
|
||||||
let peers: [EnginePeer]
|
let peers: [TelegramForbiddenInvitePeer]
|
||||||
let peerPresences: [EnginePeer.Id: EnginePeer.Presence]
|
let peerPresences: [EnginePeer.Id: EnginePeer.Presence]
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
link: String?,
|
link: String?,
|
||||||
peers: [EnginePeer],
|
peers: [TelegramForbiddenInvitePeer],
|
||||||
peerPresences: [EnginePeer.Id: EnginePeer.Presence]
|
peerPresences: [EnginePeer.Id: EnginePeer.Presence]
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -272,7 +272,9 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
for peer in component.peers {
|
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.state = state
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
|
||||||
let hasPremiumRestrictedUsers = "".isEmpty
|
let premiumRestrictedUsers = component.peers.filter { peer in
|
||||||
let hasInviteLink = "".isEmpty
|
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 {
|
if themeUpdated {
|
||||||
self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
||||||
@ -312,12 +321,13 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
self.scrollContentView.addSubview(avatarsNode.view)
|
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(
|
let avatarsSize = avatarsNode.update(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
content: avatarsContent,
|
content: avatarsContent,
|
||||||
itemSize: CGSize(width: 60.0, height: 60.0),
|
itemSize: CGSize(width: 60.0, height: 60.0),
|
||||||
customSpacing: 50.0,
|
customSpacing: 30.0,
|
||||||
font: avatarPlaceholderFont(size: 28.0),
|
font: avatarPlaceholderFont(size: 28.0),
|
||||||
animated: false,
|
animated: false,
|
||||||
synchronousLoad: true
|
synchronousLoad: true
|
||||||
@ -347,7 +357,7 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
transition.setFrame(view: leftButtonView, frame: leftButtonFrame)
|
transition.setFrame(view: leftButtonView, frame: leftButtonFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasPremiumRestrictedUsers {
|
if !premiumRestrictedUsers.isEmpty {
|
||||||
var premiumItemsTransition = transition
|
var premiumItemsTransition = transition
|
||||||
|
|
||||||
let premiumTitle: ComponentView<Empty>
|
let premiumTitle: ComponentView<Empty>
|
||||||
@ -397,20 +407,20 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
|
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
let text: String
|
let text: String
|
||||||
if component.peers.count == 1 {
|
if premiumRestrictedUsers.count == 1 {
|
||||||
text = "**\(component.peers[0].compactDisplayTitle)** accepts invitations to groups from contacts and **Premium** users."
|
text = "**\(premiumRestrictedUsers[0].peer.compactDisplayTitle)** accepts invitations to groups from contacts and **Premium** users."
|
||||||
} else {
|
} else {
|
||||||
let extraCount = component.peers.count - 3
|
let extraCount = premiumRestrictedUsers.count - 3
|
||||||
|
|
||||||
var peersText = ""
|
var peersText = ""
|
||||||
for i in 0 ..< min(3, component.peers.count) {
|
for i in 0 ..< min(3, premiumRestrictedUsers.count) {
|
||||||
if extraCount == 0 && i == component.peers.count - 1 {
|
if extraCount == 0 && i == premiumRestrictedUsers.count - 1 {
|
||||||
peersText.append(", and ")
|
peersText.append(", and ")
|
||||||
} else if i != 0 {
|
} else if i != 0 {
|
||||||
peersText.append(", ")
|
peersText.append(", ")
|
||||||
}
|
}
|
||||||
peersText.append("**")
|
peersText.append("**")
|
||||||
peersText.append(component.peers[i].compactDisplayTitle)
|
peersText.append(premiumRestrictedUsers[i].peer.compactDisplayTitle)
|
||||||
peersText.append("**")
|
peersText.append("**")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,13 +651,13 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
contentHeight += 8.0
|
contentHeight += 8.0
|
||||||
|
|
||||||
let text: String
|
let text: String
|
||||||
if hasPremiumRestrictedUsers {
|
if !premiumRestrictedUsers.isEmpty {
|
||||||
if component.link != nil {
|
if component.link != nil {
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
text = "You can try to send an invite link instead."
|
text = "You can try to send an invite link instead."
|
||||||
} else {
|
} else {
|
||||||
if component.peers.count == 1 {
|
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 {
|
} else {
|
||||||
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
|
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
|
||||||
}
|
}
|
||||||
@ -655,13 +665,13 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
} else {
|
} else {
|
||||||
if component.link != nil {
|
if component.link != nil {
|
||||||
if component.peers.count == 1 {
|
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 {
|
} else {
|
||||||
text = environment.strings.SendInviteLink_TextAvailableMultipleUsers(Int32(component.peers.count))
|
text = environment.strings.SendInviteLink_TextAvailableMultipleUsers(Int32(component.peers.count))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if component.peers.count == 1 {
|
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 {
|
} else {
|
||||||
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
|
text = environment.strings.SendInviteLink_TextUnavailableMultipleUsers(Int32(component.peers.count))
|
||||||
}
|
}
|
||||||
@ -696,6 +706,7 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
|
|
||||||
contentHeight += descriptionTextFrame.height
|
contentHeight += descriptionTextFrame.height
|
||||||
contentHeight += 22.0
|
contentHeight += 22.0
|
||||||
|
initialContentHeight = contentHeight
|
||||||
|
|
||||||
var singleItemHeight: CGFloat = 0.0
|
var singleItemHeight: CGFloat = 0.0
|
||||||
|
|
||||||
@ -706,7 +717,7 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
|
|
||||||
for _ in 0 ..< 1 {
|
for _ in 0 ..< 1 {
|
||||||
//let id: AnyHashable = AnyHashable("\(peer.id)_\(j)")
|
//let id: AnyHashable = AnyHashable("\(peer.id)_\(j)")
|
||||||
let id = AnyHashable(peer.id)
|
let id = AnyHashable(peer.peer.id)
|
||||||
validIds.append(id)
|
validIds.append(id)
|
||||||
|
|
||||||
let item: ComponentView<Empty>
|
let item: ComponentView<Empty>
|
||||||
@ -719,6 +730,15 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
self.items[id] = item
|
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(
|
let itemSize = item.update(
|
||||||
transition: itemTransition,
|
transition: itemTransition,
|
||||||
component: AnyComponent(PeerListItemComponent(
|
component: AnyComponent(PeerListItemComponent(
|
||||||
@ -726,15 +746,18 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
strings: environment.strings,
|
strings: environment.strings,
|
||||||
sideInset: 0.0,
|
sideInset: 0.0,
|
||||||
title: peer.displayTitle(strings: environment.strings, displayOrder: .firstLast),
|
title: peer.peer.displayTitle(strings: environment.strings, displayOrder: .firstLast),
|
||||||
peer: peer,
|
subtitle: itemSubtitle,
|
||||||
presence: component.peerPresences[peer.id],
|
peer: peer.peer,
|
||||||
selectionState: component.link == nil ? .none : .editing(isSelected: self.selectedItems.contains(peer.id)),
|
selectionState: !canBeSelected ? .none : .editing(isSelected: self.selectedItems.contains(peer.peer.id)),
|
||||||
hasNext: i != component.peers.count - 1,
|
hasNext: i != component.peers.count - 1,
|
||||||
action: { [weak self] peer in
|
action: { [weak self] peer in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !canBeSelected {
|
||||||
|
return
|
||||||
|
}
|
||||||
if self.selectedItems.contains(peer.id) {
|
if self.selectedItems.contains(peer.id) {
|
||||||
self.selectedItems.remove(peer.id)
|
self.selectedItems.remove(peer.id)
|
||||||
} else {
|
} 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)))
|
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))
|
initialContentHeight += min(itemsHeight, floor(singleItemHeight * 2.5))
|
||||||
|
|
||||||
contentHeight += itemsHeight
|
contentHeight += itemsHeight
|
||||||
@ -805,16 +827,16 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
if self.selectedItems.isEmpty {
|
if self.selectedItems.isEmpty {
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
} else if let link = component.link {
|
} 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 _ = 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
|
let text: String
|
||||||
if selectedPeers.count == 1 {
|
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 {
|
} 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 {
|
} 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 }
|
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 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
|
self.scrollContentClippingView.layer.cornerRadius = 10.0
|
||||||
|
|
||||||
@ -906,13 +928,13 @@ private final class SendInviteLinkScreenComponent: Component {
|
|||||||
public class SendInviteLinkScreen: ViewControllerComponentContainer {
|
public class SendInviteLinkScreen: ViewControllerComponentContainer {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let link: String?
|
private let link: String?
|
||||||
private let peers: [EnginePeer]
|
private let peers: [TelegramForbiddenInvitePeer]
|
||||||
|
|
||||||
private var isDismissed: Bool = false
|
private var isDismissed: Bool = false
|
||||||
|
|
||||||
private var presenceDisposable: Disposable?
|
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
|
self.context = context
|
||||||
|
|
||||||
var link = link
|
var link = link
|
||||||
@ -920,6 +942,120 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer {
|
|||||||
link = "https://t.me/\(addressName)"
|
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.link = link
|
||||||
self.peers = peers
|
self.peers = peers
|
||||||
|
|
||||||
@ -930,7 +1066,7 @@ public class SendInviteLinkScreen: ViewControllerComponentContainer {
|
|||||||
self.blocksBackgroundWhenInOverlay = true
|
self.blocksBackgroundWhenInOverlay = true
|
||||||
|
|
||||||
self.presenceDisposable = (context.engine.data.subscribe(EngineDataMap(
|
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
|
|> deliverOnMainQueue).start(next: { [weak self] presences in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
|
|||||||
@ -52,7 +52,7 @@ final class ComposeControllerNode: ASDisplayNode {
|
|||||||
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: {
|
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: {
|
||||||
openCreateNewChannelImpl?()
|
openCreateNewChannelImpl?()
|
||||||
})
|
})
|
||||||
], includeChatList: false, topPeers: .none)), onlyWriteable: false, displayPermissionPlaceholder: false)
|
], includeChatList: false, topPeers: .none)), onlyWriteable: false, isGroupInvitation: false, displayPermissionPlaceholder: false)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
|||||||
@ -83,6 +83,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
private let options: [ContactListAdditionalOption]
|
private let options: [ContactListAdditionalOption]
|
||||||
private let filters: [ContactListFilter]
|
private let filters: [ContactListFilter]
|
||||||
private let onlyWriteable: Bool
|
private let onlyWriteable: Bool
|
||||||
|
private let isGroupInvitation: Bool
|
||||||
private let limit: Int32?
|
private let limit: Int32?
|
||||||
|
|
||||||
init(_ params: ContactMultiselectionControllerParams) {
|
init(_ params: ContactMultiselectionControllerParams) {
|
||||||
@ -94,6 +95,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
self.options = params.options
|
self.options = params.options
|
||||||
self.filters = params.filters
|
self.filters = params.filters
|
||||||
self.onlyWriteable = params.onlyWriteable
|
self.onlyWriteable = params.onlyWriteable
|
||||||
|
self.isGroupInvitation = params.isGroupInvitation
|
||||||
self.limit = params.limit
|
self.limit = params.limit
|
||||||
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
@ -302,7 +304,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func loadDisplayNode() {
|
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)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
})
|
})
|
||||||
switch self.contactsNode.contentNode {
|
switch self.contactsNode.contentNode {
|
||||||
|
|||||||
@ -78,8 +78,9 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let isPeerEnabled: ((EnginePeer) -> Bool)?
|
private let isPeerEnabled: ((EnginePeer) -> Bool)?
|
||||||
private let onlyWriteable: 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.navigationBar = navigationBar
|
||||||
|
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -90,6 +91,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.isPeerEnabled = isPeerEnabled
|
self.isPeerEnabled = isPeerEnabled
|
||||||
self.onlyWriteable = onlyWriteable
|
self.onlyWriteable = onlyWriteable
|
||||||
|
self.isGroupInvitation = isGroupInvitation
|
||||||
|
|
||||||
var proceedImpl: (() -> Void)?
|
var proceedImpl: (() -> Void)?
|
||||||
|
|
||||||
@ -231,7 +233,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
displayTopPeers = .none
|
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)
|
self.contentNode = .contacts(contactListNode)
|
||||||
|
|
||||||
if !selectedPeers.isEmpty {
|
if !selectedPeers.isEmpty {
|
||||||
@ -359,7 +361,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
|||||||
searchChannels: searchChannels,
|
searchChannels: searchChannels,
|
||||||
globalSearch: globalSearch,
|
globalSearch: globalSearch,
|
||||||
displaySavedMessages: displaySavedMessages
|
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
|
searchResultsNode.openPeer = { peer, _ in
|
||||||
self?.tokenListNode.setText("")
|
self?.tokenListNode.setText("")
|
||||||
self?.openPeer?(peer)
|
self?.openPeer?(peer)
|
||||||
|
|||||||
@ -68,7 +68,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
self.filters = filters
|
self.filters = filters
|
||||||
|
|
||||||
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
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)
|
contextActionImpl?(peer, node, gesture, nil)
|
||||||
} : nil, multipleSelection: multipleSelection)
|
} : nil, multipleSelection: multipleSelection)
|
||||||
|
|
||||||
|
|||||||
@ -676,7 +676,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
case .supergroup:
|
case .supergroup:
|
||||||
createSignal = context.engine.peers.createSupergroup(title: title, description: nil)
|
createSignal = context.engine.peers.createSupergroup(title: title, description: nil)
|
||||||
|> map { peerId -> CreateGroupResult? in
|
|> map { peerId -> CreateGroupResult? in
|
||||||
return CreateGroupResult(peerId: peerId, failedToInvitePeerIds: [])
|
return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: []))
|
||||||
}
|
}
|
||||||
|> mapError { error -> CreateGroupError in
|
|> mapError { error -> CreateGroupError in
|
||||||
switch error {
|
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))
|
return context.engine.peers.createSupergroup(title: title, description: nil, location: (location.latitude, location.longitude, address))
|
||||||
|> map { peerId -> CreateGroupResult? in
|
|> map { peerId -> CreateGroupResult? in
|
||||||
return CreateGroupResult(peerId: peerId, failedToInvitePeerIds: [])
|
return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: []))
|
||||||
}
|
}
|
||||||
|> mapError { error -> CreateGroupError in
|
|> mapError { error -> CreateGroupError in
|
||||||
switch error {
|
switch error {
|
||||||
@ -731,7 +731,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
let createGroupSignal: (Bool) -> Signal<CreateGroupResult?, CreateGroupError> = { isForum in
|
let createGroupSignal: (Bool) -> Signal<CreateGroupResult?, CreateGroupError> = { isForum in
|
||||||
return context.engine.peers.createSupergroup(title: title, description: nil, isForum: isForum)
|
return context.engine.peers.createSupergroup(title: title, description: nil, isForum: isForum)
|
||||||
|> map { peerId -> CreateGroupResult? in
|
|> map { peerId -> CreateGroupResult? in
|
||||||
return CreateGroupResult(peerId: peerId, failedToInvitePeerIds: [])
|
return CreateGroupResult(peerId: peerId, result: TelegramInvitePeersResult(forbiddenPeers: []))
|
||||||
}
|
}
|
||||||
|> mapError { error -> CreateGroupError in
|
|> mapError { error -> CreateGroupError in
|
||||||
switch error {
|
switch error {
|
||||||
@ -834,7 +834,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: result.peerId))
|
let controller = ChatControllerImpl(context: context, chatLocation: .peer(id: result.peerId))
|
||||||
replaceControllerImpl?(controller)
|
replaceControllerImpl?(controller)
|
||||||
|
|
||||||
if !result.failedToInvitePeerIds.isEmpty {
|
if !result.result.forbiddenPeers.isEmpty {
|
||||||
context.account.viewTracker.forceUpdateCachedPeerData(peerId: result.peerId)
|
context.account.viewTracker.forceUpdateCachedPeerData(peerId: result.peerId)
|
||||||
let _ = (context.engine.data.subscribe(
|
let _ = (context.engine.data.subscribe(
|
||||||
TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: result.peerId)
|
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)
|
TelegramEngine.EngineData.Item.Peer.Peer(id: result.peerId)
|
||||||
)
|
)
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|> deliverOnMainQueue).start(next: { peer in
|
||||||
let _ = controller
|
|
||||||
let _ = exportedInvitation
|
|
||||||
|
|
||||||
if let peer, let exportedInvitation, let link = exportedInvitation.link {
|
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:)))
|
let inviteScreen = SendInviteLinkScreen(context: context, peer: peer, link: link, peers: result.result.forbiddenPeers)
|
||||||
)
|
controller?.push(inviteScreen)
|
||||||
|> 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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user