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.
		
		
		
		
		
			
		
			
				
	
	
		
			175 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			175 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Swift
		
	
| import NVActivityIndicatorView
 | |
| 
 | |
| final class LinkPreviewView : UIView {
 | |
|     private let viewItem: ConversationViewItem?
 | |
|     private let maxWidth: CGFloat
 | |
|     private let delegate: LinkPreviewViewDelegate
 | |
|     var linkPreviewState: LinkPreviewState? { didSet { update() } }
 | |
|     private lazy var imageViewContainerWidthConstraint = imageView.set(.width, to: 100)
 | |
|     private lazy var imageViewContainerHeightConstraint = imageView.set(.height, to: 100)
 | |
| 
 | |
|     private lazy var sentLinkPreviewTextColor: UIColor = {
 | |
|         let isOutgoing = (viewItem!.interaction.interactionType() == .outgoingMessage)
 | |
|         switch (isOutgoing, AppModeManager.shared.currentAppMode) {
 | |
|         case (true, .dark), (false, .light): return .black
 | |
|         default: return .white
 | |
|         }
 | |
|     }()
 | |
| 
 | |
|     // MARK: UI Components
 | |
|     private lazy var imageView: UIImageView = {
 | |
|         let result = UIImageView()
 | |
|         result.contentMode = .scaleAspectFill
 | |
|         return result
 | |
|     }()
 | |
| 
 | |
|     private lazy var imageViewContainer: UIView = {
 | |
|         let result = UIView()
 | |
|         result.clipsToBounds = true
 | |
|         return result
 | |
|     }()
 | |
| 
 | |
|     private lazy var loader: NVActivityIndicatorView = {
 | |
|         let color: UIColor = isLightMode ? .black : .white
 | |
|         return NVActivityIndicatorView(frame: CGRect.zero, type: .circleStrokeSpin, color: color, padding: nil)
 | |
|     }()
 | |
| 
 | |
|     private lazy var titleLabel: UILabel = {
 | |
|         let result = UILabel()
 | |
|         result.font = .boldSystemFont(ofSize: Values.smallFontSize)
 | |
|         result.numberOfLines = 0
 | |
|         return result
 | |
|     }()
 | |
| 
 | |
|     private lazy var bodyTextViewContainer = UIView()
 | |
| 
 | |
|     private lazy var hStackViewContainer = UIView()
 | |
| 
 | |
|     private lazy var hStackView = UIStackView()
 | |
| 
 | |
|     private lazy var cancelButton: UIButton = {
 | |
|         let result = UIButton(type: .custom)
 | |
|         let tint: UIColor = isLightMode ? .black : .white
 | |
|         result.setImage(UIImage(named: "X")?.withTint(tint), for: UIControl.State.normal)
 | |
|         let cancelButtonSize = LinkPreviewView.cancelButtonSize
 | |
|         result.set(.width, to: cancelButtonSize)
 | |
|         result.set(.height, to: cancelButtonSize)
 | |
|         result.addTarget(self, action: #selector(cancel), for: UIControl.Event.touchUpInside)
 | |
|         return result
 | |
|     }()
 | |
| 
 | |
|     // MARK: Settings
 | |
|     private static let loaderSize: CGFloat = 24
 | |
|     private static let cancelButtonSize: CGFloat = 45
 | |
| 
 | |
|     // MARK: Lifecycle
 | |
|     init(for viewItem: ConversationViewItem?, maxWidth: CGFloat, delegate: LinkPreviewViewDelegate) {
 | |
|         self.viewItem = viewItem
 | |
|         self.maxWidth = maxWidth
 | |
|         self.delegate = delegate
 | |
|         super.init(frame: CGRect.zero)
 | |
|         setUpViewHierarchy()
 | |
|     }
 | |
|     
 | |
|     override init(frame: CGRect) {
 | |
|         preconditionFailure("Use init(for:maxWidth:delegate:) instead.")
 | |
|     }
 | |
|     
 | |
|     required init?(coder: NSCoder) {
 | |
|         preconditionFailure("Use init(for:maxWidth:delegate:) instead.")
 | |
|     }
 | |
|     
 | |
|     private func setUpViewHierarchy() {
 | |
|         // Image view
 | |
|         imageViewContainerWidthConstraint.isActive = true
 | |
|         imageViewContainerHeightConstraint.isActive = true
 | |
|         imageViewContainer.addSubview(imageView)
 | |
|         imageView.pin(to: imageViewContainer)
 | |
|         // Title label
 | |
|         let titleLabelContainer = UIView()
 | |
|         titleLabelContainer.addSubview(titleLabel)
 | |
|         titleLabel.pin(to: titleLabelContainer, withInset: Values.smallSpacing)
 | |
|         // Horizontal stack view
 | |
|         hStackView.addArrangedSubview(imageViewContainer)
 | |
|         hStackView.addArrangedSubview(titleLabelContainer)
 | |
|         hStackView.axis = .horizontal
 | |
|         hStackView.alignment = .center
 | |
|         hStackViewContainer.addSubview(hStackView)
 | |
|         hStackView.pin(to: hStackViewContainer)
 | |
|         // Vertical stack view
 | |
|         let vStackView = UIStackView(arrangedSubviews: [ hStackViewContainer, bodyTextViewContainer ])
 | |
|         vStackView.axis = .vertical
 | |
|         addSubview(vStackView)
 | |
|         vStackView.pin(to: self)
 | |
|         // Loader
 | |
|         addSubview(loader)
 | |
|         let loaderSize = LinkPreviewView.loaderSize
 | |
|         loader.set(.width, to: loaderSize)
 | |
|         loader.set(.height, to: loaderSize)
 | |
|         loader.center(in: self)
 | |
|     }
 | |
| 
 | |
|     // MARK: Updating
 | |
|     private func update() {
 | |
|         cancelButton.removeFromSuperview()
 | |
|         guard let linkPreviewState = linkPreviewState else { return }
 | |
|         var image = linkPreviewState.image()
 | |
|         if image == nil && (linkPreviewState is LinkPreviewDraft || linkPreviewState is LinkPreviewSent) {
 | |
|             image = UIImage(named: "Link")?.withTint(isLightMode ? .black : .white)
 | |
|         }
 | |
|         // Image view
 | |
|         let imageViewContainerSize: CGFloat = (linkPreviewState is LinkPreviewSent) ? 100 : 80
 | |
|         imageViewContainerWidthConstraint.constant = imageViewContainerSize
 | |
|         imageViewContainerHeightConstraint.constant = imageViewContainerSize
 | |
|         imageViewContainer.layer.cornerRadius = (linkPreviewState is LinkPreviewSent) ? 0 : 8
 | |
|         if linkPreviewState is LinkPreviewLoading {
 | |
|             imageViewContainer.backgroundColor = .clear
 | |
|         } else {
 | |
|             imageViewContainer.backgroundColor = isDarkMode ? .black : UIColor.black.withAlphaComponent(0.06)
 | |
|         }
 | |
|         imageView.image = image
 | |
|         imageView.contentMode = (linkPreviewState.image() == nil) ? .center : .scaleAspectFill
 | |
|         // Loader
 | |
|         loader.alpha = (image != nil) ? 0 : 1
 | |
|         if image != nil { loader.stopAnimating() } else { loader.startAnimating() }
 | |
|         // Title
 | |
|         let isSent = (linkPreviewState is LinkPreviewSent)
 | |
|         let isOutgoing = (viewItem?.interaction.interactionType() == .outgoingMessage)
 | |
|         let textColor: UIColor
 | |
|         if isSent && isOutgoing && isLightMode {
 | |
|             textColor = .white
 | |
|         } else {
 | |
|             textColor = isDarkMode ? .white : .black
 | |
|         }
 | |
|         titleLabel.textColor = textColor
 | |
|         titleLabel.text = linkPreviewState.title()
 | |
|         // Horizontal stack view
 | |
|         switch linkPreviewState {
 | |
|         case is LinkPreviewSent: hStackViewContainer.backgroundColor = isDarkMode ? .black : UIColor.black.withAlphaComponent(0.06)
 | |
|         default: hStackViewContainer.backgroundColor = nil
 | |
|         }
 | |
|         // Body text view
 | |
|         bodyTextViewContainer.subviews.forEach { $0.removeFromSuperview() }
 | |
|         if let viewItem = viewItem {
 | |
|             let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: sentLinkPreviewTextColor, searchText: delegate.lastSearchedText, delegate: delegate)
 | |
|             bodyTextViewContainer.addSubview(bodyTextView)
 | |
|             bodyTextView.pin(to: bodyTextViewContainer, withInset: 12)
 | |
|         }
 | |
|         if linkPreviewState is LinkPreviewDraft {
 | |
|             hStackView.addArrangedSubview(cancelButton)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // MARK: Interaction
 | |
|     @objc private func cancel() {
 | |
|         delegate.handleLinkPreviewCanceled()
 | |
|     }
 | |
| }
 | |
| 
 | |
| // MARK: Delegate
 | |
| protocol LinkPreviewViewDelegate : UITextViewDelegate & BodyTextViewDelegate {
 | |
|     var lastSearchedText: String? { get }
 | |
| 
 | |
|     func handleLinkPreviewCanceled()
 | |
| }
 |