mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			153 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			153 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Swift
		
	
import SessionUIKit
 | 
						|
 | 
						|
@objc(LKProfilePictureView)
 | 
						|
public final class ProfilePictureView : UIView {
 | 
						|
    private var hasTappableProfilePicture: Bool = false
 | 
						|
    @objc public var size: CGFloat = 0 // Not an implicitly unwrapped optional due to Obj-C limitations
 | 
						|
    @objc public var useFallbackPicture = false
 | 
						|
    @objc public var publicKey: String!
 | 
						|
    @objc public var additionalPublicKey: String?
 | 
						|
    @objc public var openGroupProfilePicture: UIImage?
 | 
						|
    // Constraints
 | 
						|
    private var imageViewWidthConstraint: NSLayoutConstraint!
 | 
						|
    private var imageViewHeightConstraint: NSLayoutConstraint!
 | 
						|
    private var additionalImageViewWidthConstraint: NSLayoutConstraint!
 | 
						|
    private var additionalImageViewHeightConstraint: NSLayoutConstraint!
 | 
						|
    
 | 
						|
    // MARK: Components
 | 
						|
    private lazy var imageView = getImageView()
 | 
						|
    private lazy var additionalImageView = getImageView()
 | 
						|
    
 | 
						|
    // MARK: Lifecycle
 | 
						|
    public override init(frame: CGRect) {
 | 
						|
        super.init(frame: frame)
 | 
						|
        setUpViewHierarchy()
 | 
						|
    }
 | 
						|
    
 | 
						|
    public required init?(coder: NSCoder) {
 | 
						|
        super.init(coder: coder)
 | 
						|
        setUpViewHierarchy()
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func setUpViewHierarchy() {
 | 
						|
        // Set up image view
 | 
						|
        addSubview(imageView)
 | 
						|
        imageView.pin(.leading, to: .leading, of: self)
 | 
						|
        imageView.pin(.top, to: .top, of: self)
 | 
						|
        let imageViewSize = CGFloat(Values.mediumProfilePictureSize)
 | 
						|
        imageViewWidthConstraint = imageView.set(.width, to: imageViewSize)
 | 
						|
        imageViewHeightConstraint = imageView.set(.height, to: imageViewSize)
 | 
						|
        // Set up additional image view
 | 
						|
        addSubview(additionalImageView)
 | 
						|
        additionalImageView.pin(.trailing, to: .trailing, of: self)
 | 
						|
        additionalImageView.pin(.bottom, to: .bottom, of: self)
 | 
						|
        let additionalImageViewSize = CGFloat(Values.smallProfilePictureSize)
 | 
						|
        additionalImageViewWidthConstraint = additionalImageView.set(.width, to: additionalImageViewSize)
 | 
						|
        additionalImageViewHeightConstraint = additionalImageView.set(.height, to: additionalImageViewSize)
 | 
						|
        additionalImageView.layer.cornerRadius = additionalImageViewSize / 2
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: Updating
 | 
						|
    @objc(updateForContact:)
 | 
						|
    public func update(for publicKey: String) {
 | 
						|
        openGroupProfilePicture = nil
 | 
						|
        self.publicKey = publicKey
 | 
						|
        additionalPublicKey = nil
 | 
						|
        useFallbackPicture = false
 | 
						|
        update()
 | 
						|
    }
 | 
						|
 | 
						|
    @objc(updateForThread:)
 | 
						|
    public func update(for thread: TSThread) {
 | 
						|
        openGroupProfilePicture = nil
 | 
						|
        if let thread = thread as? TSGroupThread {
 | 
						|
            if let openGroupProfilePicture = thread.groupModel.groupImage { // An open group with a profile picture
 | 
						|
                self.openGroupProfilePicture = openGroupProfilePicture
 | 
						|
                useFallbackPicture = false
 | 
						|
                hasTappableProfilePicture = true
 | 
						|
            } else if thread.groupModel.groupType == .openGroup { // An open group without a profile picture or an RSS feed
 | 
						|
                publicKey = ""
 | 
						|
                useFallbackPicture = true
 | 
						|
            } else { // A closed group
 | 
						|
                var users = Set(thread.groupModel.groupMemberIds)
 | 
						|
                users.remove(getUserHexEncodedPublicKey())
 | 
						|
                var randomUsers = users.sorted() // Sort to provide a level of stability
 | 
						|
                if users.count == 1 {
 | 
						|
                    randomUsers.insert(getUserHexEncodedPublicKey(), at: 0) // Ensure the current user is at the back visually
 | 
						|
                }
 | 
						|
                publicKey = randomUsers.count >= 1 ? randomUsers[0] : ""
 | 
						|
                additionalPublicKey = randomUsers.count >= 2 ? randomUsers[1] : ""
 | 
						|
                useFallbackPicture = false
 | 
						|
            }
 | 
						|
            update()
 | 
						|
        } else { // A one-to-one chat
 | 
						|
            let thread = thread as! TSContactThread
 | 
						|
            update(for: thread.contactSessionID())
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    @objc public func update() {
 | 
						|
        AssertIsOnMainThread()
 | 
						|
        func getProfilePicture(of size: CGFloat, for publicKey: String) -> UIImage? {
 | 
						|
            guard !publicKey.isEmpty else { return nil }
 | 
						|
            if let profilePicture = OWSProfileManager.shared().profileAvatar(forRecipientId: publicKey) {
 | 
						|
                hasTappableProfilePicture = true
 | 
						|
                return profilePicture
 | 
						|
            } else {
 | 
						|
                hasTappableProfilePicture = false
 | 
						|
                // TODO: Pass in context?
 | 
						|
                let displayName = Storage.shared.getContact(with: publicKey)?.name ?? publicKey
 | 
						|
                return Identicon.generatePlaceholderIcon(seed: publicKey, text: displayName, size: size)
 | 
						|
            }
 | 
						|
        }
 | 
						|
        let size: CGFloat
 | 
						|
        if let additionalPublicKey = additionalPublicKey, !useFallbackPicture, openGroupProfilePicture == nil {
 | 
						|
            if self.size == 40 {
 | 
						|
                size = 32
 | 
						|
            } else if self.size == Values.largeProfilePictureSize {
 | 
						|
                size = 56
 | 
						|
            } else {
 | 
						|
                size = Values.smallProfilePictureSize
 | 
						|
            }
 | 
						|
            imageViewWidthConstraint.constant = size
 | 
						|
            imageViewHeightConstraint.constant = size
 | 
						|
            additionalImageViewWidthConstraint.constant = size
 | 
						|
            additionalImageViewHeightConstraint.constant = size
 | 
						|
            additionalImageView.isHidden = false
 | 
						|
            additionalImageView.image = getProfilePicture(of: size, for: additionalPublicKey)
 | 
						|
        } else {
 | 
						|
            size = self.size
 | 
						|
            imageViewWidthConstraint.constant = size
 | 
						|
            imageViewHeightConstraint.constant = size
 | 
						|
            additionalImageView.isHidden = true
 | 
						|
            additionalImageView.image = nil
 | 
						|
        }
 | 
						|
        guard publicKey != nil || openGroupProfilePicture != nil else { return }
 | 
						|
        imageView.image = useFallbackPicture ? nil : (openGroupProfilePicture ?? getProfilePicture(of: size, for: publicKey))
 | 
						|
        imageView.backgroundColor = useFallbackPicture ? UIColor(rgbHex: 0x353535) : Colors.unimportant
 | 
						|
        imageView.layer.cornerRadius = size / 2
 | 
						|
        additionalImageView.layer.cornerRadius = size / 2
 | 
						|
        imageView.contentMode = useFallbackPicture ? .center : .scaleAspectFit
 | 
						|
        if useFallbackPicture {
 | 
						|
            switch size {
 | 
						|
            case Values.smallProfilePictureSize..<Values.mediumProfilePictureSize: imageView.image = #imageLiteral(resourceName: "SessionWhite16")
 | 
						|
            case Values.mediumProfilePictureSize..<Values.largeProfilePictureSize: imageView.image = #imageLiteral(resourceName: "SessionWhite24")
 | 
						|
            default: imageView.image = #imageLiteral(resourceName: "SessionWhite40")
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    // MARK: Convenience
 | 
						|
    private func getImageView() -> UIImageView {
 | 
						|
        let result = UIImageView()
 | 
						|
        result.layer.masksToBounds = true
 | 
						|
        result.backgroundColor = Colors.unimportant
 | 
						|
        result.contentMode = .scaleAspectFit
 | 
						|
        return result
 | 
						|
    }
 | 
						|
    
 | 
						|
    @objc public func getProfilePicture() -> UIImage? {
 | 
						|
        return hasTappableProfilePicture ? imageView.image : nil
 | 
						|
    }
 | 
						|
}
 |