Merge commit '42673dc7f0ba37737573aa5ad95df3d916a57ceb'

This commit is contained in:
Peter 2019-08-01 01:45:58 +03:00
commit 74943cd1b4
6 changed files with 161 additions and 41 deletions

View File

@ -12,13 +12,17 @@ struct MatchingDeviceContact {
let phoneNumbers: [String]
}
func matchingDeviceContacts(stableIds: [String]) -> Signal<[MatchingDeviceContact], NoError> {
enum IntentContactsError {
case generic
}
func matchingDeviceContacts(stableIds: [String]) -> Signal<[MatchingDeviceContact], IntentContactsError> {
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
return .single([])
return .fail(.generic)
}
let store = CNContactStore()
guard let contacts = try? store.unifiedContacts(matching: CNContact.predicateForContacts(withIdentifiers: stableIds), keysToFetch: [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactPhoneNumbersKey as CNKeyDescriptor]) else {
return .single([])
return .fail(.generic)
}
return .single(contacts.map({ contact in
@ -34,19 +38,58 @@ func matchingDeviceContacts(stableIds: [String]) -> Signal<[MatchingDeviceContac
}))
}
private func matchPhoneNumbers(_ lhs: String, _ rhs: String) -> Bool {
if lhs.count < 10 && lhs.count == rhs.count {
return lhs == rhs
} else if lhs.count >= 10 && rhs.count >= 10 && lhs.suffix(10) == rhs.suffix(10) {
return true
} else {
return false
}
}
func matchingCloudContacts(postbox: Postbox, contacts: [MatchingDeviceContact]) -> Signal<[(String, TelegramUser)], NoError> {
return postbox.transaction { transaction -> [(String, TelegramUser)] in
var result: [(String, TelegramUser)] = []
outer: for peerId in transaction.getContactPeerIds() {
if let peer = transaction.getPeer(peerId) as? TelegramUser, let phone = peer.phone {
if let peer = transaction.getPeer(peerId) as? TelegramUser, let peerPhoneNumber = peer.phone {
for contact in contacts {
for phoneNumber in contact.phoneNumbers {
if arePhoneNumbersEqual(phoneNumber, phone) {
if matchPhoneNumbers(phoneNumber, peerPhoneNumber) {
result.append((contact.stableId, peer))
continue outer
}
}
}
// var parsedPhoneNumbers: [String: ParsedPhoneNumber] = [:]
// let parsedPeerPhoneNumber: ParsedPhoneNumber?
// if let number = parsedPhoneNumbers[peerPhoneNumber] {
// parsedPeerPhoneNumber = number
// } else if let number = ParsedPhoneNumber(string: peerPhoneNumber) {
// parsedPeerPhoneNumber = number
// parsedPhoneNumbers[peerPhoneNumber] = number
// } else {
// parsedPeerPhoneNumber = nil
// }
//
// for contact in contacts {
// for phoneNumber in contact.phoneNumbers {
// let parsedPhoneNumber: ParsedPhoneNumber?
// if let number = parsedPhoneNumbers[phoneNumber] {
// parsedPhoneNumber = number
// } else if let number = ParsedPhoneNumber(string: phoneNumber) {
// parsedPhoneNumber = number
// parsedPhoneNumbers[phoneNumber] = number
// } else {
// parsedPhoneNumber = nil
// }
//
// if parsedPeerPhoneNumber == parsedPhoneNumber {
// result.append((contact.stableId, peer))
// continue outer
// }
// }
// }
}
}
return result

View File

@ -4,6 +4,7 @@ import TelegramCore
import Postbox
import SwiftSignalKit
import BuildConfig
import Contacts
private var accountCache: Account?
@ -47,7 +48,7 @@ enum IntentHandlingError {
}
class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling, INSearchCallHistoryIntentHandling {
private let accountPromise = Promise<Account>()
private let accountPromise = Promise<Account?>()
private let resolvePersonsDisposable = MetaDisposable()
private let actionDisposable = MetaDisposable()
@ -86,7 +87,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
let account: Signal<Account, NoError>
let account: Signal<Account?, NoError>
if let accountCache = accountCache {
account = .single(accountCache)
} else {
@ -97,7 +98,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: .single(buildConfig.bundleData(withAppToken: nil))), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters)
|> mapToSignal { account -> Signal<Account, NoError> in
|> mapToSignal { account -> Signal<Account?, NoError> in
if let account = account {
switch account {
case .upgrading:
@ -117,12 +118,9 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
return .complete()
}
} else {
return .complete()
return .single(nil)
}
}
|> afterNext { account in
account.resetStateManagement()
}
|> take(1)
}
self.accountPromise.set(account)
@ -138,6 +136,11 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
private func resolve(persons: [INPerson]?, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
completion([INPersonResolutionResult.notRequired()])
return
}
guard let initialPersons = persons, !initialPersons.isEmpty else {
completion([INPersonResolutionResult.needsValue()])
return
@ -205,8 +208,14 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|> take(1)
|> mapToSignal { matchedContacts in
return account
|> mapToSignal { account in
|> introduceError(IntentContactsError.self)
|> mapToSignal { account -> Signal<[(String, TelegramUser)], IntentContactsError> in
if let account = account {
return matchingCloudContacts(postbox: account.postbox, contacts: matchedContacts)
|> introduceError(IntentContactsError.self)
} else {
return .fail(.generic)
}
}
}
self.resolvePersonsDisposable.set((signal
@ -219,6 +228,8 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
return INPersonResolutionResult.success(with: person)
})
}
}, error: { error in
completion([INPersonResolutionResult.unsupported()])
}))
}
@ -230,8 +241,11 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
let account = self.accountPromise.get()
let signal = account
|> mapToSignal { account -> Signal<INPerson?, NoError> in
|> introduceError(IntentHandlingError.self)
|> mapToSignal { account -> Signal<INPerson?, IntentHandlingError> in
if let account = account {
return matchingCloudContact(postbox: account.postbox, peerId: PeerId(peerId))
|> introduceError(IntentHandlingError.self)
|> map { user -> INPerson? in
if let user = user {
return personWithUser(stableId: "tg\(peerId)", user: user)
@ -239,6 +253,9 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
return nil
}
}
} else {
return .fail(.generic)
}
}
self.resolvePersonsDisposable.set((signal
@ -248,6 +265,8 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
} else {
completion([INPersonResolutionResult.needsValue()])
}
}, error: { error in
completion([INPersonResolutionResult.notRequired()])
}))
} else {
self.resolve(persons: intent.recipients, with: completion)
@ -258,6 +277,10 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
func resolveContent(for intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
completion(INStringResolutionResult.notRequired())
return
}
if let text = intent.content, !text.isEmpty {
completion(INStringResolutionResult.success(with: text))
} else {
@ -267,6 +290,11 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
func confirm(intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
let response = INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
completion(response)
return
}
let response = INSendMessageIntentResponse(code: .ready, userActivity: userActivity)
completion(response)
}
@ -278,6 +306,9 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
return .generic
}
|> mapToSignal { account -> Signal<Void, IntentHandlingError> in
guard let account = account else {
return .fail(.generic)
}
guard let recipient = intent.recipients?.first, let customIdentifier = recipient.customIdentifier, customIdentifier.hasPrefix("tg") else {
return .fail(.generic)
}
@ -305,7 +336,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
}
|> deliverOnMainQueue).start(error: { _ in
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
let response = INSendMessageIntentResponse(code: .failure, userActivity: userActivity)
let response = INSendMessageIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
completion(response)
}, completed: {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
@ -325,20 +356,29 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|> take(1)
|> introduceError(IntentHandlingError.self)
|> mapToSignal { account -> Signal<[INMessage], IntentHandlingError> in
guard let account = account else {
return .fail(.generic)
}
account.shouldBeServiceTaskMaster.set(.single(.now))
account.resetStateManagement()
return account.stateManager.pollStateUpdateCompletion()
let completion: Signal<Void, NoError> = account.stateManager.pollStateUpdateCompletion()
|> map { _ in
return Void()
}
return (completion |> timeout(4.0, queue: Queue.mainQueue(), alternate: .single(Void())))
|> introduceError(IntentHandlingError.self)
|> take(1)
|> mapToSignal { _ -> Signal<[INMessage], IntentHandlingError> in
return unreadMessages(account: account)
|> introduceError(IntentHandlingError.self)
}
|> afterDisposed {
account.shouldBeServiceTaskMaster.set(.single(.never))
}
}
}
|> deliverOnMainQueue).start(next: { messages in
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchForMessagesIntent.self))
let response = INSearchForMessagesIntentResponse(code: .success, userActivity: userActivity)
@ -346,7 +386,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
completion(response)
}, error: { _ in
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchForMessagesIntent.self))
let response = INSearchForMessagesIntentResponse(code: .failure, userActivity: userActivity)
let response = INSearchForMessagesIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
completion(response)
}))
}
@ -373,8 +413,11 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
return .generic
}
|> mapToSignal { account -> Signal<Void, IntentHandlingError> in
var signals: [Signal<Void, IntentHandlingError>] = []
guard let account = account else {
return .fail(.generic)
}
var signals: [Signal<Void, IntentHandlingError>] = []
var maxMessageIdsToApply: [PeerId: MessageId] = [:]
if let identifiers = intent.identifiers {
for identifier in identifiers {
@ -478,6 +521,10 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|> take(1)
|> introduceError(IntentHandlingError.self)
|> mapToSignal { account -> Signal<[CallRecord], IntentHandlingError> in
guard let account = account else {
return .fail(.generic)
}
account.shouldBeServiceTaskMaster.set(.single(.now))
return missedCalls(account: account)
|> introduceError(IntentHandlingError.self)
@ -497,7 +544,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
completion(response)
}, error: { _ in
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchCallHistoryIntent.self))
let response = INSearchCallHistoryIntentResponse(code: .failure, userActivity: userActivity)
let response = INSearchCallHistoryIntentResponse(code: .failureRequiringAppLaunch, userActivity: userActivity)
completion(response)
}))
}

View File

@ -13,6 +13,10 @@ func unreadMessages(account: Account) -> Signal<[INMessage], NoError> {
var signals: [Signal<[INMessage], NoError>] = []
for entry in view.0.entries {
if case let .MessageEntry(index, _, readState, notificationSettings, _, _, _, _) = entry {
if index.messageIndex.id.peerId.namespace != Namespaces.Peer.CloudUser {
continue
}
var hasUnread = false
var fixedCombinedReadStates: MessageHistoryViewReadState?
if let readState = readState {
@ -133,7 +137,7 @@ private func callWithTelegramMessage(_ telegramMessage: Message, account: Accoun
}
private func messageWithTelegramMessage(_ telegramMessage: Message, account: Account) -> INMessage? {
guard let author = telegramMessage.author, let user = telegramMessage.peers[author.id] as? TelegramUser else {
guard let author = telegramMessage.author, let user = telegramMessage.peers[author.id] as? TelegramUser, user.id.id != 777000 else {
return nil
}
@ -195,8 +199,15 @@ private func messageWithTelegramMessage(_ telegramMessage: Message, account: Acc
}
}
if telegramMessage.text.isEmpty && messageType == .text {
return nil
}
message = INMessage(identifier: identifier, conversationIdentifier: "\(telegramMessage.id.peerId.toInt64())", content: telegramMessage.text, dateSent: date, sender: sender, recipients: [], groupName: nil, messageType: messageType)
} else {
if telegramMessage.text.isEmpty {
return nil
}
message = INMessage(identifier: identifier, content: telegramMessage.text, dateSent: date, sender: sender, recipients: [])
}

View File

@ -1304,7 +1304,10 @@ public class Account {
self.managedOperationsDisposable.add(managedPendingPeerNotificationSettings(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedSynchronizeAppLogEventsOperations(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedNotificationSettingsBehaviors(postbox: self.postbox).start())
if !self.supplementary {
self.managedOperationsDisposable.add(managedAnimatedEmojiUpdates(postbox: self.postbox, network: self.network).start())
}
let mediaBox = postbox.mediaBox
self.storageSettingsDisposable = accountManager.sharedData(keys: [SharedDataKeys.cacheStorageSettings]).start(next: { [weak mediaBox] sharedData in

View File

@ -16,11 +16,27 @@ public func isViablePhoneNumber(_ string: String) -> Bool {
return phoneNumberUtil.isViablePhoneNumber(string)
}
public func arePhoneNumbersEqual(_ lhs: String, _ rhs: String) -> Bool {
let result = phoneNumberUtil.isNumberMatch(lhs as NSString, second: rhs as NSString, error: nil)
public class ParsedPhoneNumber: Equatable {
let rawPhoneNumber: NBPhoneNumber?
public init?(string: String) {
if let number = try? phoneNumberUtil.parse(string, defaultRegion: NB_UNKNOWN_REGION) {
self.rawPhoneNumber = number
} else {
return nil
}
}
public static func == (lhs: ParsedPhoneNumber, rhs: ParsedPhoneNumber) -> Bool {
var error: NSError?
let result = phoneNumberUtil.isNumberMatch(lhs.rawPhoneNumber, second: rhs.rawPhoneNumber, error: &error)
if error != nil {
return false
}
if result != .NO_MATCH && result != .NOT_A_NUMBER {
return true
} else {
return false
}
}
}

View File

@ -679,12 +679,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
hapticFeedback.impact(.heavy)
Queue.mainQueue().after(0.2) {
hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.74) {
hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.78) {
hapticFeedback.impact(.heavy)
Queue.mainQueue().after(0.2) {
hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.74) {
hapticFeedback.impact(.medium)
Queue.mainQueue().after(0.78) {
hapticFeedback.impact(.heavy)
Queue.mainQueue().after(0.2) {
hapticFeedback.impact(.medium)
}