Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
overtake 2020-10-08 11:52:16 +03:00
commit fe406cd27c
13 changed files with 248 additions and 17 deletions

View File

@ -229,7 +229,6 @@ static int32_t fixedTimeDifferenceValue = 0;
_useTempAuthKeys = useTempAuthKeys;
#if DEBUG
_tempKeyExpiration = 1 * 60 * 60;
_tempKeyExpiration = 5;
#else
_tempKeyExpiration = 24 * 60 * 60;
#endif

View File

@ -1105,7 +1105,7 @@ func debugRestoreState(basePath:String, name: String) {
private let sharedQueue = Queue(name: "org.telegram.postbox.Postbox")
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<PostboxResult, NoError> {
public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32) -> Signal<PostboxResult, NoError> {
let queue = sharedQueue
return Signal { subscriber in
queue.async {
@ -1177,7 +1177,7 @@ public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration,
let endTime = CFAbsoluteTimeGetCurrent()
print("Postbox load took \((endTime - startTime) * 1000.0) ms")
subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox)))
subscriber.putNext(.postbox(Postbox(queue: queue, basePath: basePath, seedConfiguration: seedConfiguration, valueBox: valueBox, timestampForAbsoluteTimeBasedOperations: timestampForAbsoluteTimeBasedOperations)))
subscriber.putCompletion()
break
}
@ -1330,7 +1330,7 @@ public final class Postbox {
var installedMessageActionsByPeerId: [PeerId: Bag<([StoreMessage], Transaction) -> Void>] = [:]
init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox) {
init(queue: Queue, basePath: String, seedConfiguration: SeedConfiguration, valueBox: SqliteValueBox, timestampForAbsoluteTimeBasedOperations: Int32) {
assert(queue.isCurrent())
let startTime = CFAbsoluteTimeGetCurrent()
@ -1534,6 +1534,9 @@ public final class Postbox {
for id in self.messageHistoryUnsentTable.get() {
transaction.updateMessage(id, update: { message in
if !message.flags.contains(.Failed) {
if message.timestamp + 60 * 10 > timestampForAbsoluteTimeBasedOperations {
return .skip
}
var flags = StoreMessageFlags(message.flags)
flags.remove(.Unsent)
flags.remove(.Sending)

View File

@ -1,3 +1,4 @@
import Foundation
import SwiftSignalKit
import Postbox
@ -67,7 +68,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
public func accountTransaction<T>(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, transaction: @escaping (Transaction) -> T) -> Signal<T, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
return postbox
|> mapToSignal { value -> Signal<T, NoError> in
switch value {

View File

@ -160,7 +160,7 @@ public enum AccountPreferenceEntriesResult {
public func accountPreferenceEntries(rootPath: String, id: AccountRecordId, keys: Set<ValueBoxKey>, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountPreferenceEntriesResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
return postbox
|> mapToSignal { value -> Signal<AccountPreferenceEntriesResult, NoError> in
switch value {
@ -187,7 +187,7 @@ public enum AccountNoticeEntriesResult {
public func accountNoticeEntries(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<AccountNoticeEntriesResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
return postbox
|> mapToSignal { value -> Signal<AccountNoticeEntriesResult, NoError> in
switch value {
@ -208,7 +208,7 @@ public enum LegacyAccessChallengeDataResult {
public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal<LegacyAccessChallengeDataResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
return postbox
|> mapToSignal { value -> Signal<LegacyAccessChallengeDataResult, NoError> in
switch value {
@ -225,7 +225,7 @@ public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecord
public func accountWithId(accountManager: AccountManager, networkArguments: NetworkInitializationArguments, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, supplementary: Bool, rootPath: String, beginWithTestingEnvironment: Bool, backupData: AccountBackupData?, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal<AccountResult, NoError> {
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
let postbox = openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
return postbox
|> mapToSignal { result -> Signal<AccountResult, NoError> in

View File

@ -209,7 +209,7 @@ public func temporaryAccount(manager: AccountManager, rootPath: String, encrypti
return manager.allocatedTemporaryAccountId()
|> mapToSignal { id -> Signal<TemporaryAccount, NoError> in
let path = "\(rootPath)/\(accountRecordIdPathName(id))"
return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters)
return openPostbox(basePath: path + "/postbox", seedConfiguration: telegramPostboxSeedConfiguration, encryptionParameters: encryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970))
|> mapToSignal { result -> Signal<TemporaryAccount, NoError> in
switch result {
case .upgrading:

View File

@ -291,6 +291,7 @@ public final class AccountViewTracker {
private var seenLiveLocationDisposables = DisposableDict<Int32>()
private var updatedUnsupportedMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:]
private var refreshSecretChatMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:]
private var nextUpdatedUnsupportedMediaDisposableId: Int32 = 0
private var updatedUnsupportedMediaDisposables = DisposableDict<Int32>()
@ -631,6 +632,18 @@ public final class AccountViewTracker {
|> runOn(self.queue)
}
public func updateReplyInfoForMessageId(_ id: MessageId, info: UpdatedMessageReplyInfo) {
self.queue.async { [weak self] in
guard let strongSelf = self else {
return
}
guard let current = strongSelf.updatedViewCountMessageIdsAndTimestamps[id] else {
return
}
strongSelf.updatedViewCountMessageIdsAndTimestamps[id] = ViewCountContextState(timestamp: Int32(CFAbsoluteTimeGetCurrent()), clientId: current.clientId, result: ViewCountContextState.ReplyInfo(commentsPeerId: info.commentsPeerId, maxReadIncomingMessageId: info.maxReadIncomingMessageId, maxMessageId: info.maxMessageId))
}
}
public func updateViewCountForMessageIds(messageIds: Set<MessageId>, clientId: Int32) {
self.queue.async {
var addedMessageIds: [MessageId] = []
@ -1037,6 +1050,98 @@ public final class AccountViewTracker {
}
}
public func refreshSecretMediaMediaForMessageIds(messageIds: Set<MessageId>) {
self.queue.async {
var addedMessageIds: [MessageId] = []
let timestamp = Int32(CFAbsoluteTimeGetCurrent())
for messageId in messageIds {
let messageTimestamp = self.refreshSecretChatMediaMessageIdsAndTimestamps[messageId]
if messageTimestamp == nil {
self.refreshSecretChatMediaMessageIdsAndTimestamps[messageId] = timestamp
addedMessageIds.append(messageId)
}
}
if !addedMessageIds.isEmpty {
for (_, messageIds) in messagesIdsGroupedByPeerId(Set(addedMessageIds)) {
let disposableId = self.nextUpdatedUnsupportedMediaDisposableId
self.nextUpdatedUnsupportedMediaDisposableId += 1
if let account = self.account {
let signal = account.postbox.transaction { transaction -> [TelegramMediaFile] in
var result: [TelegramMediaFile] = []
for id in messageIds {
if let message = transaction.getMessage(id) {
for media in message.media {
if let file = media as? TelegramMediaFile, file.isAnimatedSticker {
result.append(file)
}
}
}
}
return result
}
|> mapToSignal { files -> Signal<Void, NoError> in
guard !files.isEmpty else {
return .complete()
}
var stickerPacks = Set<StickerPackReference>()
for file in files {
for attribute in file.attributes {
if case let .Sticker(_, packReferenceValue, _) = attribute, let packReference = packReferenceValue {
if case .id = packReference {
stickerPacks.insert(packReference)
}
}
}
}
var requests: [Signal<Api.messages.StickerSet?, NoError>] = []
for reference in stickerPacks {
if case let .id(id, accessHash) = reference {
requests.append(account.network.request(Api.functions.messages.getStickerSet(stickerset: .inputStickerSetID(id: id, accessHash: accessHash)))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.StickerSet?, NoError> in
return .single(nil)
})
}
}
if requests.isEmpty {
return .complete()
}
return combineLatest(requests)
|> mapToSignal { results -> Signal<Void, NoError> in
return account.postbox.transaction { transaction -> Void in
for result in results {
switch result {
case let .stickerSet(_, _, documents):
for document in documents {
if let file = telegramMediaFileFromApiDocument(document) {
if transaction.getMedia(file.fileId) != nil {
let _ = transaction.updateMedia(file.fileId, update: file)
}
}
}
default:
break
}
}
}
}
}
|> afterDisposed { [weak self] in
self?.queue.async {
self?.updatedUnsupportedMediaDisposables.set(nil, forKey: disposableId)
}
}
self.updatedUnsupportedMediaDisposables.set(signal.start(), forKey: disposableId)
}
}
}
}
}
public func updateMarkAllMentionsSeen(peerId: PeerId) {
self.queue.async {
guard let account = self.account else {

View File

@ -130,6 +130,21 @@ private func requestActivity(postbox: Postbox, network: Network, accountPeerId:
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
return .complete()
}
if let _ = peer as? TelegramUser {
if let presence = transaction.getPeerPresence(peerId: peerId) as? TelegramUserPresence {
switch presence.status {
case .none, .recently, .lastWeek, .lastMonth:
return .complete()
case let .present(statusTimestamp):
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
if statusTimestamp < timestamp {
return .complete()
}
}
} else {
return .complete()
}
}
if let inputPeer = apiInputPeer(peer) {
var flags: Int32 = 0

View File

@ -145,10 +145,12 @@ private class ReplyThreadHistoryContextImpl {
}
var channelMessageId: MessageId?
var replyThreadAttribute: ReplyThreadMessageAttribute?
for attribute in topMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
channelMessageId = attribute.messageId
break
} else if let attribute = attribute as? ReplyThreadMessageAttribute {
replyThreadAttribute = attribute
}
}
@ -195,14 +197,41 @@ private class ReplyThreadHistoryContextImpl {
}
}
let maxReadIncomingMessageId = readInboxMaxId.flatMap { readMaxId in
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
}
if let channelMessageId = channelMessageId, let replyThreadAttribute = replyThreadAttribute {
account.viewTracker.updateReplyInfoForMessageId(channelMessageId, info: AccountViewTracker.UpdatedMessageReplyInfo(
timestamp: Int32(CFAbsoluteTimeGetCurrent()),
commentsPeerId: parsedIndex.id.peerId,
maxReadIncomingMessageId: maxReadIncomingMessageId,
maxMessageId: resolvedMaxMessage
))
transaction.updateMessage(channelMessageId, update: { currentMessage in
var attributes = currentMessage.attributes
loop: for j in 0 ..< attributes.count {
if let attribute = attributes[j] as? ReplyThreadMessageAttribute {
attributes[j] = ReplyThreadMessageAttribute(
count: replyThreadAttribute.count,
latestUsers: attribute.latestUsers,
commentsPeerId: attribute.commentsPeerId,
maxMessageId: replyThreadAttribute.maxMessageId,
maxReadMessageId: replyThreadAttribute.maxReadMessageId
)
}
}
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init), authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
}
return .single(DiscussionMessage(
messageId: parsedIndex.id,
channelMessageId: channelMessageId,
isChannelPost: isChannelPost,
maxMessage: resolvedMaxMessage,
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
},
maxReadIncomingMessageId: maxReadIncomingMessageId,
maxReadOutgoingMessageId: readOutboxMaxId.flatMap { readMaxId in
MessageId(peerId: parsedIndex.id.peerId, namespace: Namespaces.Message.Cloud, id: readMaxId)
}

View File

@ -3068,6 +3068,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
var wasInForeground = true
self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|> distinctUntilChanged
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
@ -3077,8 +3078,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.raiseToListen?.applicationResignedActive()
strongSelf.stopMediaRecorder()
} else {
if !wasInForeground {
strongSelf.chatDisplayNode.recursivelyEnsureDisplaySynchronously(true)
}
}
wasInForeground = value
}
})
if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {

View File

@ -514,6 +514,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
private let messageReactionsProcessingManager = ChatMessageThrottledProcessingManager()
private let seenLiveLocationProcessingManager = ChatMessageThrottledProcessingManager()
private let unsupportedMessageProcessingManager = ChatMessageThrottledProcessingManager()
private let refreshMediaProcessingManager = ChatMessageThrottledProcessingManager()
private let messageMentionProcessingManager = ChatMessageThrottledProcessingManager(delay: 0.2)
let prefetchManager: InChatPrefetchManager
private var currentEarlierPrefetchMessages: [(Message, Media)] = []
@ -605,6 +606,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
self.unsupportedMessageProcessingManager.process = { [weak context] messageIds in
context?.account.viewTracker.updateUnsupportedMediaForMessageIds(messageIds: messageIds)
}
self.refreshMediaProcessingManager.process = { [weak context] messageIds in
context?.account.viewTracker.refreshSecretMediaMediaForMessageIds(messageIds: messageIds)
}
self.messageMentionProcessingManager.process = { [weak context] messageIds in
context?.account.viewTracker.updateMarkMentionsSeenForMessageIds(messageIds: messageIds)
}
@ -1174,6 +1178,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
var messageIdsWithUpdateableReactions: [MessageId] = []
var messageIdsWithLiveLocation: [MessageId] = []
var messageIdsWithUnsupportedMedia: [MessageId] = []
var messageIdsWithRefreshMedia: [MessageId] = []
var messageIdsWithUnseenPersonalMention: [MessageId] = []
var messagesWithPreloadableMediaToEarlier: [(Message, Media)] = []
var messagesWithPreloadableMediaToLater: [(Message, Media)] = []
@ -1192,6 +1197,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
}
}
var contentRequiredValidation = false
var mediaRequiredValidation = false
for attribute in message.attributes {
if attribute is ViewCountMessageAttribute {
if message.id.namespace == Namespaces.Message.Cloud {
@ -1218,6 +1224,24 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
}
} else if let _ = media as? TelegramMediaAction {
isAction = true
} else if let telegramFile = media as? TelegramMediaFile {
if telegramFile.isAnimatedSticker, (message.id.peerId.namespace == Namespaces.Peer.SecretChat || !telegramFile.previewRepresentations.isEmpty), let size = telegramFile.size, size > 0 && size <= 128 * 1024 {
if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
if telegramFile.fileId.namespace == Namespaces.Media.CloudFile {
var isValidated = false
attributes: for attribute in telegramFile.attributes {
if case .hintIsValidated = attribute {
isValidated = true
break attributes
}
}
if !isValidated {
mediaRequiredValidation = true
}
}
}
}
}
}
if !isAction && message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
@ -1226,6 +1250,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if contentRequiredValidation {
messageIdsWithUnsupportedMedia.append(message.id)
}
if mediaRequiredValidation {
messageIdsWithRefreshMedia.append(message.id)
}
if hasUnconsumedMention && !hasUnconsumedContent {
messageIdsWithUnseenPersonalMention.append(message.id)
}
@ -1346,6 +1373,9 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if !messageIdsWithUnsupportedMedia.isEmpty {
self.unsupportedMessageProcessingManager.add(messageIdsWithUnsupportedMedia)
}
if !messageIdsWithRefreshMedia.isEmpty {
self.refreshMediaProcessingManager.add(messageIdsWithRefreshMedia)
}
if !messageIdsWithUnseenPersonalMention.isEmpty {
self.messageMentionProcessingManager.add(messageIdsWithUnseenPersonalMention)
}

View File

@ -1066,7 +1066,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty && !isPreview {
replyMarkup = attribute
} else if let attribute = attribute as? AuthorSignatureMessageAttribute {
if firstMessage.author is TelegramChannel, !attribute.signature.isEmpty {
if let chatPeer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .group = chatPeer.info, firstMessage.author is TelegramChannel, !attribute.signature.isEmpty {
authorRank = .custom(attribute.signature)
}
}

View File

@ -13,6 +13,7 @@ import PresentationDataUtils
import SearchUI
import TelegramPermissionsUI
import AppBundle
import DeviceAccess
public class ComposeController: ViewController {
private let context: AccountContext
@ -183,6 +184,42 @@ public class ComposeController: ViewController {
}
}
self.contactsNode.openCreateContact = { [weak self] in
let _ = (DeviceAccess.authorizationStatus(subject: .contacts)
|> take(1)
|> deliverOnMainQueue).start(next: { status in
guard let strongSelf = self else {
return
}
switch status {
case .allowed:
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
guard let strongSelf = self else {
return
}
if let peer = peer {
DispatchQueue.main.async {
if let navigationController = strongSelf.navigationController as? NavigationController {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id)))
}
}
} else {
(strongSelf.navigationController as? NavigationController)?.replaceAllButRootController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil), animated: true)
}
}), completed: nil, cancelled: nil))
case .notDetermined:
DeviceAccess.authorizeAccess(to: .contacts)
default:
let presentationData = strongSelf.presentationData
strongSelf.present(textAlertController(context: strongSelf.context, title: presentationData.strings.AccessDenied_Title, text: presentationData.strings.Contacts_AccessDeniedError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
self?.context.sharedContext.applicationBindings.openSettings()
})]), in: .window(.root))
}
})
}
self.contactsNode.openCreateNewChannel = { [weak self] in
if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }

View File

@ -27,6 +27,7 @@ final class ComposeControllerNode: ASDisplayNode {
var openCreateNewGroup: (() -> Void)?
var openCreateNewSecretChat: (() -> Void)?
var openCreateContact: (() -> Void)?
var openCreateNewChannel: (() -> Void)?
private var presentationData: PresentationData
@ -39,14 +40,15 @@ final class ComposeControllerNode: ASDisplayNode {
var openCreateNewGroupImpl: (() -> Void)?
var openCreateNewSecretChatImpl: (() -> Void)?
var openCreateContactImpl: (() -> Void)?
var openCreateNewChannelImpl: (() -> Void)?
self.contactListNode = ContactListNode(context: context, presentation: .single(.natural(options: [
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewGroup, icon: .generic(UIImage(bundleImageName: "Contact List/CreateGroupActionIcon")!), action: {
openCreateNewGroupImpl?()
}),
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewEncryptedChat, icon: .generic(UIImage(bundleImageName: "Contact List/CreateSecretChatActionIcon")!), action: {
openCreateNewSecretChatImpl?()
ContactListAdditionalOption(title: self.presentationData.strings.NewContact_Title, icon: .generic(UIImage(bundleImageName: "Contact List/AddMemberIcon")!), action: {
openCreateContactImpl?()
}),
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewChannel, icon: .generic(UIImage(bundleImageName: "Contact List/CreateChannelActionIcon")!), action: {
openCreateNewChannelImpl?()
@ -69,6 +71,10 @@ final class ComposeControllerNode: ASDisplayNode {
openCreateNewSecretChatImpl = { [weak self] in
self?.openCreateNewSecretChat?()
}
openCreateContactImpl = { [weak self] in
self?.contactListNode.listNode.clearHighlightAnimated(true)
self?.openCreateContact?()
}
openCreateNewChannelImpl = { [weak self] in
self?.openCreateNewChannel?()
}