mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
558 lines
19 KiB
Swift
558 lines
19 KiB
Swift
import Foundation
|
|
import Contacts
|
|
import TelegramCore
|
|
|
|
public final class DeviceContactPhoneNumberData: Equatable {
|
|
public let label: String
|
|
public let value: String
|
|
|
|
public init(label: String, value: String) {
|
|
self.label = label
|
|
self.value = value
|
|
}
|
|
|
|
public static func == (lhs: DeviceContactPhoneNumberData, rhs: DeviceContactPhoneNumberData) -> Bool {
|
|
if lhs.label != rhs.label {
|
|
return false
|
|
}
|
|
if lhs.value != rhs.value {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public final class DeviceContactEmailAddressData: Equatable {
|
|
public let label: String
|
|
public let value: String
|
|
|
|
public init(label: String, value: String) {
|
|
self.label = label
|
|
self.value = value
|
|
}
|
|
|
|
public static func == (lhs: DeviceContactEmailAddressData, rhs: DeviceContactEmailAddressData) -> Bool {
|
|
if lhs.label != rhs.label {
|
|
return false
|
|
}
|
|
if lhs.value != rhs.value {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public final class DeviceContactUrlData: Equatable {
|
|
public let label: String
|
|
public let value: String
|
|
|
|
public init(label: String, value: String) {
|
|
self.label = label
|
|
self.value = value
|
|
}
|
|
|
|
public static func == (lhs: DeviceContactUrlData, rhs: DeviceContactUrlData) -> Bool {
|
|
if lhs.label != rhs.label {
|
|
return false
|
|
}
|
|
if lhs.value != rhs.value {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public final class DeviceContactAddressData: Equatable, Hashable {
|
|
public let label: String
|
|
public let street1: String
|
|
public let street2: String
|
|
public let state: String
|
|
public let city: String
|
|
public let country: String
|
|
public let postcode: String
|
|
|
|
public init(label: String, street1: String, street2: String, state: String, city: String, country: String, postcode: String) {
|
|
self.label = label
|
|
self.street1 = street1
|
|
self.street2 = street2
|
|
self.state = state
|
|
self.city = city
|
|
self.country = country
|
|
self.postcode = postcode
|
|
}
|
|
|
|
public static func == (lhs: DeviceContactAddressData, rhs: DeviceContactAddressData) -> Bool {
|
|
if lhs.label != rhs.label {
|
|
return false
|
|
}
|
|
if lhs.street1 != rhs.street1 {
|
|
return false
|
|
}
|
|
if lhs.street2 != rhs.street2 {
|
|
return false
|
|
}
|
|
if lhs.state != rhs.state {
|
|
return false
|
|
}
|
|
if lhs.city != rhs.city {
|
|
return false
|
|
}
|
|
if lhs.country != rhs.country {
|
|
return false
|
|
}
|
|
if lhs.postcode != rhs.postcode {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
public func hash(into hasher: inout Hasher) {
|
|
hasher.combine(self.label)
|
|
hasher.combine(self.street1)
|
|
hasher.combine(self.street2)
|
|
hasher.combine(self.state)
|
|
hasher.combine(self.city)
|
|
hasher.combine(self.country)
|
|
hasher.combine(self.postcode)
|
|
}
|
|
}
|
|
|
|
public final class DeviceContactSocialProfileData: Equatable, Hashable {
|
|
public let label: String
|
|
public let service: String
|
|
public let username: String
|
|
public let url: String
|
|
|
|
public init(label: String, service: String, username: String, url: String) {
|
|
self.label = label
|
|
self.service = service
|
|
self.username = username
|
|
self.url = url
|
|
}
|
|
|
|
public static func == (lhs: DeviceContactSocialProfileData, rhs: DeviceContactSocialProfileData) -> Bool {
|
|
if lhs.label != rhs.label {
|
|
return false
|
|
}
|
|
if lhs.service != rhs.service {
|
|
return false
|
|
}
|
|
if lhs.username != rhs.username {
|
|
return false
|
|
}
|
|
if lhs.url != rhs.url {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
public func hash(into hasher: inout Hasher) {
|
|
hasher.combine(self.label)
|
|
hasher.combine(self.service)
|
|
hasher.combine(self.username)
|
|
hasher.combine(self.url)
|
|
}
|
|
}
|
|
|
|
public final class DeviceContactInstantMessagingProfileData: Equatable, Hashable {
|
|
public let label: String
|
|
public let service: String
|
|
public let username: String
|
|
|
|
public init(label: String, service: String, username: String) {
|
|
self.label = label
|
|
self.service = service
|
|
self.username = username
|
|
}
|
|
|
|
public static func == (lhs: DeviceContactInstantMessagingProfileData, rhs: DeviceContactInstantMessagingProfileData) -> Bool {
|
|
if lhs.label != rhs.label {
|
|
return false
|
|
}
|
|
if lhs.service != rhs.service {
|
|
return false
|
|
}
|
|
if lhs.username != rhs.username {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
public func hash(into hasher: inout Hasher) {
|
|
hasher.combine(self.label)
|
|
hasher.combine(self.service)
|
|
hasher.combine(self.username)
|
|
}
|
|
}
|
|
|
|
public let phonebookUsernamePathPrefix = "@id"
|
|
private let phonebookUsernamePrefix = "https://t.me/" + phonebookUsernamePathPrefix
|
|
|
|
public extension DeviceContactUrlData {
|
|
convenience init(appProfile: EnginePeer.Id) {
|
|
self.init(label: "Telegram", value: "\(phonebookUsernamePrefix)\(appProfile.id._internalGetInt64Value())")
|
|
}
|
|
}
|
|
|
|
public func parseAppSpecificContactReference(_ value: String) -> EnginePeer.Id? {
|
|
if !value.hasPrefix(phonebookUsernamePrefix) {
|
|
return nil
|
|
}
|
|
let idString = String(value[value.index(value.startIndex, offsetBy: phonebookUsernamePrefix.count)...])
|
|
if let id = Int64(idString) {
|
|
return EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(id))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
public final class DeviceContactBasicData: Equatable {
|
|
public let firstName: String
|
|
public let lastName: String
|
|
public let phoneNumbers: [DeviceContactPhoneNumberData]
|
|
|
|
public init(firstName: String, lastName: String, phoneNumbers: [DeviceContactPhoneNumberData]) {
|
|
self.firstName = firstName
|
|
self.lastName = lastName
|
|
self.phoneNumbers = phoneNumbers
|
|
}
|
|
|
|
public static func ==(lhs: DeviceContactBasicData, rhs: DeviceContactBasicData) -> Bool {
|
|
if lhs.firstName != rhs.firstName {
|
|
return false
|
|
}
|
|
if lhs.lastName != rhs.lastName {
|
|
return false
|
|
}
|
|
if lhs.phoneNumbers != rhs.phoneNumbers {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public final class DeviceContactBasicDataWithReference: Equatable {
|
|
public let stableId: DeviceContactStableId
|
|
public let basicData: DeviceContactBasicData
|
|
|
|
public init(stableId: DeviceContactStableId, basicData: DeviceContactBasicData) {
|
|
self.stableId = stableId
|
|
self.basicData = basicData
|
|
}
|
|
|
|
public static func ==(lhs: DeviceContactBasicDataWithReference, rhs: DeviceContactBasicDataWithReference) -> Bool {
|
|
return lhs.stableId == rhs.stableId && lhs.basicData == rhs.basicData
|
|
}
|
|
}
|
|
|
|
public final class DeviceContactExtendedData: Equatable {
|
|
public let basicData: DeviceContactBasicData
|
|
|
|
public let middleName: String
|
|
public let prefix: String
|
|
public let suffix: String
|
|
public let organization: String
|
|
public let jobTitle: String
|
|
public let department: String
|
|
public let emailAddresses: [DeviceContactEmailAddressData]
|
|
public let urls: [DeviceContactUrlData]
|
|
public let addresses: [DeviceContactAddressData]
|
|
public let birthdayDate: Date?
|
|
public let socialProfiles: [DeviceContactSocialProfileData]
|
|
public let instantMessagingProfiles: [DeviceContactInstantMessagingProfileData]
|
|
public let note: String
|
|
|
|
public init(basicData: DeviceContactBasicData, middleName: String, prefix: String, suffix: String, organization: String, jobTitle: String, department: String, emailAddresses: [DeviceContactEmailAddressData], urls: [DeviceContactUrlData], addresses: [DeviceContactAddressData], birthdayDate: Date?, socialProfiles: [DeviceContactSocialProfileData], instantMessagingProfiles: [DeviceContactInstantMessagingProfileData], note: String) {
|
|
self.basicData = basicData
|
|
self.middleName = middleName
|
|
self.prefix = prefix
|
|
self.suffix = suffix
|
|
self.organization = organization
|
|
self.jobTitle = jobTitle
|
|
self.department = department
|
|
self.emailAddresses = emailAddresses
|
|
self.urls = urls
|
|
self.addresses = addresses
|
|
self.birthdayDate = birthdayDate
|
|
self.socialProfiles = socialProfiles
|
|
self.instantMessagingProfiles = instantMessagingProfiles
|
|
self.note = note
|
|
}
|
|
|
|
public static func ==(lhs: DeviceContactExtendedData, rhs: DeviceContactExtendedData) -> Bool {
|
|
if lhs.basicData != rhs.basicData {
|
|
return false
|
|
}
|
|
if lhs.middleName != rhs.middleName {
|
|
return false
|
|
}
|
|
if lhs.prefix != rhs.prefix {
|
|
return false
|
|
}
|
|
if lhs.suffix != rhs.suffix {
|
|
return false
|
|
}
|
|
if lhs.organization != rhs.organization {
|
|
return false
|
|
}
|
|
if lhs.jobTitle != rhs.jobTitle {
|
|
return false
|
|
}
|
|
if lhs.department != rhs.department {
|
|
return false
|
|
}
|
|
if lhs.emailAddresses != rhs.emailAddresses {
|
|
return false
|
|
}
|
|
if lhs.urls != rhs.urls {
|
|
return false
|
|
}
|
|
if lhs.addresses != rhs.addresses {
|
|
return false
|
|
}
|
|
if lhs.birthdayDate != rhs.birthdayDate {
|
|
return false
|
|
}
|
|
if lhs.socialProfiles != rhs.socialProfiles {
|
|
return false
|
|
}
|
|
if lhs.instantMessagingProfiles != rhs.instantMessagingProfiles {
|
|
return false
|
|
}
|
|
if lhs.note != rhs.note {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public extension DeviceContactExtendedData {
|
|
convenience init?(vcard: Data) {
|
|
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
|
guard let contact = (try? CNContactVCardSerialization.contacts(with: vcard))?.first else {
|
|
return nil
|
|
}
|
|
self.init(contact: contact)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
|
func asMutableCNContact() -> CNMutableContact {
|
|
let contact = CNMutableContact()
|
|
contact.givenName = self.basicData.firstName
|
|
contact.familyName = self.basicData.lastName
|
|
contact.namePrefix = self.prefix
|
|
contact.nameSuffix = self.suffix
|
|
contact.middleName = self.middleName
|
|
contact.phoneNumbers = self.basicData.phoneNumbers.map { phoneNumber -> CNLabeledValue<CNPhoneNumber> in
|
|
return CNLabeledValue<CNPhoneNumber>(label: phoneNumber.label, value: CNPhoneNumber(stringValue: phoneNumber.value))
|
|
}
|
|
contact.emailAddresses = self.emailAddresses.map { email -> CNLabeledValue<NSString> in
|
|
CNLabeledValue<NSString>(label: email.label, value: email.value as NSString)
|
|
}
|
|
contact.urlAddresses = self.urls.map { url -> CNLabeledValue<NSString> in
|
|
CNLabeledValue<NSString>(label: url.label, value: url.value as NSString)
|
|
}
|
|
contact.socialProfiles = self.socialProfiles.map({ profile -> CNLabeledValue<CNSocialProfile> in
|
|
return CNLabeledValue<CNSocialProfile>(label: profile.label, value: CNSocialProfile(urlString: nil, username: profile.username, userIdentifier: nil, service: profile.service))
|
|
})
|
|
contact.instantMessageAddresses = self.instantMessagingProfiles.map({ profile -> CNLabeledValue<CNInstantMessageAddress> in
|
|
return CNLabeledValue<CNInstantMessageAddress>(label: profile.label, value: CNInstantMessageAddress(username: profile.username, service: profile.service))
|
|
})
|
|
contact.postalAddresses = self.addresses.map({ address -> CNLabeledValue<CNPostalAddress> in
|
|
let value = CNMutablePostalAddress()
|
|
value.street = address.street1 + "\n" + address.street2
|
|
value.state = address.state
|
|
value.city = address.city
|
|
value.country = address.country
|
|
value.postalCode = address.postcode
|
|
return CNLabeledValue<CNPostalAddress>(label: address.label, value: value)
|
|
})
|
|
if let birthdayDate = self.birthdayDate {
|
|
contact.birthday = Calendar(identifier: .gregorian).dateComponents([.day, .month, .year], from: birthdayDate)
|
|
}
|
|
return contact
|
|
}
|
|
|
|
func serializedVCard() -> String? {
|
|
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
|
guard let data = try? CNContactVCardSerialization.data(with: [self.asMutableCNContact()]) else {
|
|
return nil
|
|
}
|
|
return String(data: data, encoding: .utf8)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
|
convenience init(contact: CNContact) {
|
|
var phoneNumbers: [DeviceContactPhoneNumberData] = []
|
|
for number in contact.phoneNumbers {
|
|
phoneNumbers.append(DeviceContactPhoneNumberData(label: number.label ?? "", value: number.value.stringValue))
|
|
}
|
|
var emailAddresses: [DeviceContactEmailAddressData] = []
|
|
for email in contact.emailAddresses {
|
|
emailAddresses.append(DeviceContactEmailAddressData(label: email.label ?? "", value: email.value as String))
|
|
}
|
|
|
|
var urls: [DeviceContactUrlData] = []
|
|
for url in contact.urlAddresses {
|
|
urls.append(DeviceContactUrlData(label: url.label ?? "", value: url.value as String))
|
|
}
|
|
|
|
var addresses: [DeviceContactAddressData] = []
|
|
for address in contact.postalAddresses {
|
|
addresses.append(DeviceContactAddressData(label: address.label ?? "", street1: address.value.street, street2: "", state: address.value.state, city: address.value.city, country: address.value.country, postcode: address.value.postalCode))
|
|
}
|
|
|
|
var birthdayDate: Date?
|
|
if let birthday = contact.birthday {
|
|
if let date = birthday.date {
|
|
birthdayDate = date
|
|
}
|
|
}
|
|
var socialProfiles: [DeviceContactSocialProfileData] = []
|
|
for profile in contact.socialProfiles {
|
|
socialProfiles.append(DeviceContactSocialProfileData(label: profile.label ?? "", service: profile.value.service, username: profile.value.username, url: profile.value.urlString))
|
|
}
|
|
|
|
var instantMessagingProfiles: [DeviceContactInstantMessagingProfileData] = []
|
|
for profile in contact.instantMessageAddresses {
|
|
instantMessagingProfiles.append(DeviceContactInstantMessagingProfileData(label: profile.label ?? "", service: profile.value.service, username: profile.value.username))
|
|
}
|
|
|
|
let basicData = DeviceContactBasicData(firstName: contact.givenName, lastName: contact.familyName, phoneNumbers: phoneNumbers)
|
|
self.init(basicData: basicData, middleName: contact.middleName, prefix: contact.namePrefix, suffix: contact.nameSuffix, organization: contact.organizationName, jobTitle: contact.jobTitle, department: contact.departmentName, emailAddresses: emailAddresses, urls: urls, addresses: addresses, birthdayDate: birthdayDate, socialProfiles: socialProfiles, instantMessagingProfiles: instantMessagingProfiles, note: "")
|
|
}
|
|
|
|
var isPrimitive: Bool {
|
|
if self.basicData.phoneNumbers.count > 1 {
|
|
return false
|
|
}
|
|
if !self.organization.isEmpty {
|
|
return false
|
|
}
|
|
if !self.jobTitle.isEmpty {
|
|
return false
|
|
}
|
|
if !self.department.isEmpty {
|
|
return false
|
|
}
|
|
if !self.emailAddresses.isEmpty {
|
|
return false
|
|
}
|
|
if !self.urls.isEmpty {
|
|
return false
|
|
}
|
|
if !self.addresses.isEmpty {
|
|
return false
|
|
}
|
|
if self.birthdayDate != nil {
|
|
return false
|
|
}
|
|
if !self.socialProfiles.isEmpty {
|
|
return false
|
|
}
|
|
if !self.instantMessagingProfiles.isEmpty {
|
|
return false
|
|
}
|
|
if !self.note.isEmpty {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
public extension DeviceContactExtendedData {
|
|
convenience init?(peer: EnginePeer) {
|
|
guard case let .user(user) = peer else {
|
|
return nil
|
|
}
|
|
var phoneNumbers: [DeviceContactPhoneNumberData] = []
|
|
if let phone = user.phone, !phone.isEmpty {
|
|
phoneNumbers.append(DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: phone))
|
|
}
|
|
self.init(basicData: DeviceContactBasicData(firstName: user.firstName ?? "", lastName: user.lastName ?? "", phoneNumbers: phoneNumbers), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
|
|
}
|
|
}
|
|
|
|
public extension DeviceContactAddressData {
|
|
var asPostalAddress: CNPostalAddress {
|
|
let address = CNMutablePostalAddress()
|
|
if !self.street1.isEmpty {
|
|
address.street = self.street1
|
|
}
|
|
if !self.city.isEmpty {
|
|
address.city = self.city
|
|
}
|
|
if !self.state.isEmpty {
|
|
address.state = self.state
|
|
}
|
|
if !self.country.isEmpty {
|
|
address.country = self.country
|
|
}
|
|
if !self.postcode.isEmpty {
|
|
address.postalCode = self.postcode
|
|
}
|
|
return address
|
|
}
|
|
|
|
var dictionary: [String: String] {
|
|
var dictionary: [String: String] = [:]
|
|
if !self.street1.isEmpty {
|
|
dictionary["Street"] = self.street1
|
|
}
|
|
if !self.city.isEmpty {
|
|
dictionary["City"] = self.city
|
|
}
|
|
if !self.state.isEmpty {
|
|
dictionary["State"] = self.state
|
|
}
|
|
if !self.country.isEmpty {
|
|
dictionary["Country"] = self.country
|
|
}
|
|
if !self.postcode.isEmpty {
|
|
dictionary["ZIP"] = self.postcode
|
|
}
|
|
return dictionary
|
|
}
|
|
|
|
var string: String {
|
|
var array: [String] = []
|
|
if !self.street1.isEmpty {
|
|
array.append(self.street1)
|
|
}
|
|
if !self.city.isEmpty {
|
|
array.append(self.city)
|
|
}
|
|
if !self.state.isEmpty {
|
|
array.append(self.state)
|
|
}
|
|
if !self.country.isEmpty {
|
|
array.append(self.country)
|
|
}
|
|
if !self.postcode.isEmpty {
|
|
array.append(self.postcode)
|
|
}
|
|
return array.joined(separator: " ")
|
|
}
|
|
|
|
var displayString: String {
|
|
var array: [String] = []
|
|
if !self.street1.isEmpty {
|
|
array.append(self.street1)
|
|
}
|
|
if !self.city.isEmpty {
|
|
array.append(self.city)
|
|
}
|
|
if !self.state.isEmpty {
|
|
array.append(self.state)
|
|
}
|
|
if !self.country.isEmpty {
|
|
array.append(self.country)
|
|
}
|
|
return array.joined(separator: ", ")
|
|
}
|
|
}
|