// // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // import Foundation @objc public class ContactShareViewModel: NSObject { @objc public let dbRecord: OWSContact @objc public var avatarImageData: Data? { didSet { self.cachedAvatarImage = nil } } private var cachedAvatarImage: UIImage? @objc public var avatarImage: UIImage? { if self.cachedAvatarImage != nil { return self.cachedAvatarImage } guard let avatarImageData = self.avatarImageData else { return nil } self.cachedAvatarImage = UIImage(data: avatarImageData) return cachedAvatarImage } @objc public required init(contactShareRecord: OWSContact, avatarImageData: Data?) { self.dbRecord = contactShareRecord self.avatarImageData = avatarImageData } @objc public convenience init(contactShareRecord: OWSContact, transaction: YapDatabaseReadTransaction) { if let avatarAttachment = contactShareRecord.avatarAttachment(with: transaction) as? TSAttachmentStream { self.init(contactShareRecord: contactShareRecord, avatarImageData: avatarAttachment.validStillImageData()) } else { self.init(contactShareRecord: contactShareRecord, avatarImageData: nil) } } @objc public func getAvatarImage(diameter: CGFloat, contactsManager: OWSContactsManager) -> UIImage? { if let avatarImage = avatarImage { return avatarImage } var colorSeed = name.displayName let recipientIds = systemContactsWithSignalAccountPhoneNumbers(contactsManager) if let firstRecipientId = recipientIds.first { // Try to use the first signal id as the default // avatar's color seed, so that it is as consistent // as possible with the user's avatar in other views. colorSeed = firstRecipientId } let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: displayName, colorSeed: colorSeed, diameter: UInt(diameter)) // Note: we use buildDefaultImage() and not build() so that contact // share views always reflect the contents of the contact share. // build() might return an avatar from a corresponding system // contact or profile. This could mislead the user into thinking // that an avatar they did not share was in fact included in the // contact share. return avatarBuilder.buildDefaultImage() } // MARK: Delegated -> dbRecord @objc public var name: OWSContactName { get { return dbRecord.name } set { return dbRecord.name = newValue } } @objc public var addresses: [OWSContactAddress] { get { return dbRecord.addresses } set { return dbRecord.addresses = newValue } } @objc public var emails: [OWSContactEmail] { get { return dbRecord.emails } set { dbRecord.emails = newValue } } @objc public var phoneNumbers: [OWSContactPhoneNumber] { get { return dbRecord.phoneNumbers } set { dbRecord.phoneNumbers = newValue } } @objc public func systemContactsWithSignalAccountPhoneNumbers(_ contactsManager: ContactsManagerProtocol) -> [String] { return dbRecord.systemContactsWithSignalAccountPhoneNumbers(contactsManager) } @objc public func systemContactPhoneNumbers(_ contactsManager: ContactsManagerProtocol) -> [String] { return dbRecord.systemContactPhoneNumbers(contactsManager) } @objc public func e164PhoneNumbers() -> [String] { return dbRecord.e164PhoneNumbers() } @objc public var displayName: String { return dbRecord.name.displayName } @objc public var ows_isValid: Bool { return dbRecord.ows_isValid() } @objc public var isProfileAvatar: Bool { return dbRecord.isProfileAvatar } @objc public func copy(withName name: OWSContactName) -> ContactShareViewModel { // TODO move the `copy` logic into the view model? let newDbRecord = dbRecord.copy(with: name) return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImageData: self.avatarImageData) } @objc public func newContact(withName name: OWSContactName) -> ContactShareViewModel { // TODO move the `newContact` logic into the view model? let newDbRecord = dbRecord.newContact(with: name) // If we want to keep the avatar image, the caller will need to re-apply it. return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImageData: nil) } }