diff --git a/submodules/MtProtoKit/Sources/MTContext.m b/submodules/MtProtoKit/Sources/MTContext.m index e84716e92a..4f386ee9bc 100644 --- a/submodules/MtProtoKit/Sources/MTContext.m +++ b/submodules/MtProtoKit/Sources/MTContext.m @@ -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 diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 569a26d0d6..a91d918a88 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -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 { +public func openPostbox(basePath: String, seedConfiguration: SeedConfiguration, encryptionParameters: ValueBoxEncryptionParameters, timestampForAbsoluteTimeBasedOperations: Int32) -> Signal { 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) diff --git a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift index 3de0c39007..6faaa9e537 100644 --- a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift +++ b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift @@ -1,3 +1,4 @@ +import Foundation import SwiftSignalKit import Postbox @@ -67,7 +68,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { public func accountTransaction(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters, transaction: @escaping (Transaction) -> T) -> Signal { 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 in switch value { diff --git a/submodules/TelegramCore/Sources/Account.swift b/submodules/TelegramCore/Sources/Account.swift index 94774cb560..f99d7bb4ea 100644 --- a/submodules/TelegramCore/Sources/Account.swift +++ b/submodules/TelegramCore/Sources/Account.swift @@ -160,7 +160,7 @@ public enum AccountPreferenceEntriesResult { public func accountPreferenceEntries(rootPath: String, id: AccountRecordId, keys: Set, encryptionParameters: ValueBoxEncryptionParameters) -> Signal { 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 in switch value { @@ -187,7 +187,7 @@ public enum AccountNoticeEntriesResult { public func accountNoticeEntries(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal { 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 in switch value { @@ -208,7 +208,7 @@ public enum LegacyAccessChallengeDataResult { public func accountLegacyAccessChallengeData(rootPath: String, id: AccountRecordId, encryptionParameters: ValueBoxEncryptionParameters) -> Signal { 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 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 { 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 in diff --git a/submodules/TelegramCore/Sources/AccountManager.swift b/submodules/TelegramCore/Sources/AccountManager.swift index 859e8d3cff..038a86654c 100644 --- a/submodules/TelegramCore/Sources/AccountManager.swift +++ b/submodules/TelegramCore/Sources/AccountManager.swift @@ -209,7 +209,7 @@ public func temporaryAccount(manager: AccountManager, rootPath: String, encrypti return manager.allocatedTemporaryAccountId() |> mapToSignal { id -> Signal 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 in switch result { case .upgrading: diff --git a/submodules/TelegramCore/Sources/AccountViewTracker.swift b/submodules/TelegramCore/Sources/AccountViewTracker.swift index 9c4e78111e..cf87f6e9d8 100644 --- a/submodules/TelegramCore/Sources/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/AccountViewTracker.swift @@ -291,6 +291,7 @@ public final class AccountViewTracker { private var seenLiveLocationDisposables = DisposableDict() private var updatedUnsupportedMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:] + private var refreshSecretChatMediaMessageIdsAndTimestamps: [MessageId: Int32] = [:] private var nextUpdatedUnsupportedMediaDisposableId: Int32 = 0 private var updatedUnsupportedMediaDisposables = DisposableDict() @@ -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, clientId: Int32) { self.queue.async { var addedMessageIds: [MessageId] = [] @@ -1037,6 +1050,98 @@ public final class AccountViewTracker { } } + public func refreshSecretMediaMediaForMessageIds(messageIds: Set) { + 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 in + guard !files.isEmpty else { + return .complete() + } + + var stickerPacks = Set() + 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] = [] + 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 in + return .single(nil) + }) + } + } + if requests.isEmpty { + return .complete() + } + + return combineLatest(requests) + |> mapToSignal { results -> Signal 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 { diff --git a/submodules/TelegramCore/Sources/ManagedLocalInputActivities.swift b/submodules/TelegramCore/Sources/ManagedLocalInputActivities.swift index af75a65438..f54632ced1 100644 --- a/submodules/TelegramCore/Sources/ManagedLocalInputActivities.swift +++ b/submodules/TelegramCore/Sources/ManagedLocalInputActivities.swift @@ -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 diff --git a/submodules/TelegramCore/Sources/ReplyThreadHistory.swift b/submodules/TelegramCore/Sources/ReplyThreadHistory.swift index d35538ba1f..c9626ed73d 100644 --- a/submodules/TelegramCore/Sources/ReplyThreadHistory.swift +++ b/submodules/TelegramCore/Sources/ReplyThreadHistory.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 389eff8883..d1663b6355 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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,7 +3078,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.raiseToListen?.applicationResignedActive() strongSelf.stopMediaRecorder() + } else { + if !wasInForeground { + strongSelf.chatDisplayNode.recursivelyEnsureDisplaySynchronously(true) + } } + wasInForeground = value } }) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 37ed46c24a..27d97c4b01 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 0a7ef476ec..5dc98779c2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -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) } } diff --git a/submodules/TelegramUI/Sources/ComposeController.swift b/submodules/TelegramUI/Sources/ComposeController.swift index c76dbaae28..8522384ec6 100644 --- a/submodules/TelegramUI/Sources/ComposeController.swift +++ b/submodules/TelegramUI/Sources/ComposeController.swift @@ -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: "_$!!$_", 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 } diff --git a/submodules/TelegramUI/Sources/ComposeControllerNode.swift b/submodules/TelegramUI/Sources/ComposeControllerNode.swift index 3ed319633c..473daf26c4 100644 --- a/submodules/TelegramUI/Sources/ComposeControllerNode.swift +++ b/submodules/TelegramUI/Sources/ComposeControllerNode.swift @@ -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?() }