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.
		
		
		
		
		
			
		
			
				
	
	
		
			212 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			212 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Swift
		
	
//
 | 
						|
//  Copyright (c) 2019 Open Whisper Systems. All rights reserved.
 | 
						|
//
 | 
						|
 | 
						|
import Foundation
 | 
						|
import UIKit
 | 
						|
import SessionUIKit
 | 
						|
 | 
						|
protocol AttachmentApprovalInputAccessoryViewDelegate: AnyObject {
 | 
						|
    func attachmentApprovalInputUpdateMediaRail()
 | 
						|
    func attachmentApprovalInputStartEditingCaptions()
 | 
						|
    func attachmentApprovalInputStopEditingCaptions()
 | 
						|
}
 | 
						|
 | 
						|
// MARK: -
 | 
						|
 | 
						|
class AttachmentApprovalInputAccessoryView: UIView {
 | 
						|
 | 
						|
    weak var delegate: AttachmentApprovalInputAccessoryViewDelegate?
 | 
						|
 | 
						|
    let attachmentTextToolbar: AttachmentTextToolbar
 | 
						|
    let attachmentCaptionToolbar: AttachmentCaptionToolbar
 | 
						|
    let galleryRailView: GalleryRailView
 | 
						|
    let currentCaptionLabel = UILabel()
 | 
						|
    let currentCaptionWrapper = UIView()
 | 
						|
 | 
						|
    var isEditingMediaMessage: Bool {
 | 
						|
        return attachmentTextToolbar.textView.isFirstResponder
 | 
						|
    }
 | 
						|
 | 
						|
    private var isEditingCaptions: Bool = false
 | 
						|
    private var currentAttachmentItem: SignalAttachmentItem?
 | 
						|
 | 
						|
    let kGalleryRailViewHeight: CGFloat = 72
 | 
						|
 | 
						|
    required init() {
 | 
						|
        attachmentTextToolbar = AttachmentTextToolbar()
 | 
						|
        attachmentCaptionToolbar = AttachmentCaptionToolbar()
 | 
						|
 | 
						|
        galleryRailView = GalleryRailView()
 | 
						|
        galleryRailView.scrollFocusMode = .keepWithinBounds
 | 
						|
        galleryRailView.autoSetDimension(.height, toSize: kGalleryRailViewHeight)
 | 
						|
 | 
						|
        super.init(frame: .zero)
 | 
						|
 | 
						|
        createContents()
 | 
						|
    }
 | 
						|
 | 
						|
    required init?(coder aDecoder: NSCoder) {
 | 
						|
        fatalError("init(coder:) has not been implemented")
 | 
						|
    }
 | 
						|
 | 
						|
    private func createContents() {
 | 
						|
        // Specifying auto-resizing mask and an intrinsic content size allows proper
 | 
						|
        // sizing when used as an input accessory view.
 | 
						|
        self.autoresizingMask = .flexibleHeight
 | 
						|
        self.translatesAutoresizingMaskIntoConstraints = false
 | 
						|
        self.backgroundColor = .clear
 | 
						|
 | 
						|
        preservesSuperviewLayoutMargins = true
 | 
						|
 | 
						|
        // Use a background view that extends below the keyboard to avoid animation glitches.
 | 
						|
        let backgroundView = UIView()
 | 
						|
        backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
 | 
						|
        addSubview(backgroundView)
 | 
						|
        backgroundView.autoPinEdgesToSuperviewEdges()
 | 
						|
 | 
						|
        currentCaptionLabel.textColor = .white
 | 
						|
        currentCaptionLabel.font = .systemFont(ofSize: Values.mediumFontSize)
 | 
						|
        currentCaptionLabel.numberOfLines = 5
 | 
						|
        currentCaptionLabel.lineBreakMode = .byWordWrapping
 | 
						|
 | 
						|
        currentCaptionWrapper.isUserInteractionEnabled = true
 | 
						|
        currentCaptionWrapper.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(captionTapped)))
 | 
						|
        currentCaptionWrapper.addSubview(currentCaptionLabel)
 | 
						|
        currentCaptionLabel.autoPinEdgesToSuperviewMargins()
 | 
						|
 | 
						|
        attachmentCaptionToolbar.attachmentCaptionToolbarDelegate = self
 | 
						|
 | 
						|
        let stackView = UIStackView(arrangedSubviews: [currentCaptionWrapper, attachmentCaptionToolbar, galleryRailView, attachmentTextToolbar])
 | 
						|
        stackView.axis = .vertical
 | 
						|
 | 
						|
        addSubview(stackView)
 | 
						|
        stackView.autoPinEdge(toSuperviewEdge: .top)
 | 
						|
        stackView.autoPinEdge(toSuperviewEdge: .leading)
 | 
						|
        stackView.autoPinEdge(toSuperviewEdge: .trailing)
 | 
						|
        // We pin to the superview's _margin_.  Otherwise the notch breaks
 | 
						|
        // the layout if you hide the keyboard in the simulator (or if the
 | 
						|
        // user uses an external keyboard).
 | 
						|
        stackView.autoPinEdge(toSuperviewMargin: .bottom)
 | 
						|
        
 | 
						|
        let galleryRailBlockingView: UIView = UIView()
 | 
						|
        galleryRailBlockingView.backgroundColor = backgroundView.backgroundColor
 | 
						|
        stackView.addSubview(galleryRailBlockingView)
 | 
						|
        galleryRailBlockingView.pin(.top, to: .bottom, of: attachmentTextToolbar)
 | 
						|
        galleryRailBlockingView.pin(.left, to: .left, of: stackView)
 | 
						|
        galleryRailBlockingView.pin(.right, to: .right, of: stackView)
 | 
						|
        galleryRailBlockingView.pin(.bottom, to: .bottom, of: stackView)
 | 
						|
    }
 | 
						|
 | 
						|
    // MARK: - Events
 | 
						|
 | 
						|
    @objc func captionTapped(sender: UIGestureRecognizer) {
 | 
						|
        guard sender.state == .recognized else {
 | 
						|
            return
 | 
						|
        }
 | 
						|
        delegate?.attachmentApprovalInputStartEditingCaptions()
 | 
						|
    }
 | 
						|
 | 
						|
    // MARK: 
 | 
						|
 | 
						|
    private var shouldHideControls = false
 | 
						|
 | 
						|
    private func updateContents() {
 | 
						|
        var hasCurrentCaption = false
 | 
						|
        if let currentAttachmentItem = currentAttachmentItem,
 | 
						|
            let captionText = currentAttachmentItem.captionText {
 | 
						|
            hasCurrentCaption = captionText.count > 0
 | 
						|
 | 
						|
            attachmentCaptionToolbar.textView.text = captionText
 | 
						|
            currentCaptionLabel.text = captionText
 | 
						|
        } else {
 | 
						|
            attachmentCaptionToolbar.textView.text = nil
 | 
						|
            currentCaptionLabel.text = nil
 | 
						|
        }
 | 
						|
 | 
						|
        attachmentCaptionToolbar.isHidden = !isEditingCaptions
 | 
						|
        currentCaptionWrapper.isHidden = isEditingCaptions || !hasCurrentCaption
 | 
						|
        attachmentTextToolbar.isHidden = isEditingCaptions
 | 
						|
 | 
						|
        updateFirstResponder()
 | 
						|
 | 
						|
        layoutSubviews()
 | 
						|
    }
 | 
						|
 | 
						|
    private func updateFirstResponder() {
 | 
						|
        if (shouldHideControls) {
 | 
						|
            if attachmentCaptionToolbar.textView.isFirstResponder {
 | 
						|
                attachmentCaptionToolbar.textView.resignFirstResponder()
 | 
						|
            } else if attachmentTextToolbar.textView.isFirstResponder {
 | 
						|
                attachmentTextToolbar.textView.resignFirstResponder()
 | 
						|
            }
 | 
						|
        } else if (isEditingCaptions) {
 | 
						|
            // While editing captions, the keyboard should always remain visible.
 | 
						|
            if !attachmentCaptionToolbar.textView.isFirstResponder {
 | 
						|
                attachmentCaptionToolbar.textView.becomeFirstResponder()
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if attachmentCaptionToolbar.textView.isFirstResponder {
 | 
						|
                attachmentCaptionToolbar.textView.resignFirstResponder()
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // NOTE: We don't automatically make attachmentTextToolbar.textView
 | 
						|
        // first responder;
 | 
						|
    }
 | 
						|
 | 
						|
    public func update(isEditingCaptions: Bool,
 | 
						|
                       currentAttachmentItem: SignalAttachmentItem?,
 | 
						|
                       shouldHideControls: Bool) {
 | 
						|
        // De-bounce
 | 
						|
        guard self.isEditingCaptions != isEditingCaptions ||
 | 
						|
            self.currentAttachmentItem != currentAttachmentItem ||
 | 
						|
            self.shouldHideControls != shouldHideControls else {
 | 
						|
 | 
						|
                updateFirstResponder()
 | 
						|
                return
 | 
						|
        }
 | 
						|
 | 
						|
        self.isEditingCaptions = isEditingCaptions
 | 
						|
        self.currentAttachmentItem = currentAttachmentItem
 | 
						|
        self.shouldHideControls = shouldHideControls
 | 
						|
 | 
						|
        updateContents()
 | 
						|
    }
 | 
						|
 | 
						|
    // MARK: 
 | 
						|
 | 
						|
    override var intrinsicContentSize: CGSize {
 | 
						|
        get {
 | 
						|
            // Since we have `self.autoresizingMask = UIViewAutoresizingFlexibleHeight`, we must specify
 | 
						|
            // an intrinsicContentSize. Specifying CGSize.zero causes the height to be determined by autolayout.
 | 
						|
            return CGSize.zero
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public var hasFirstResponder: Bool {
 | 
						|
        return (isFirstResponder ||
 | 
						|
            attachmentCaptionToolbar.textView.isFirstResponder ||
 | 
						|
            attachmentTextToolbar.textView.isFirstResponder)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// MARK: -
 | 
						|
 | 
						|
extension AttachmentApprovalInputAccessoryView: AttachmentCaptionToolbarDelegate {
 | 
						|
    public func attachmentCaptionToolbarDidEdit(_ attachmentCaptionToolbar: AttachmentCaptionToolbar) {
 | 
						|
        guard let currentAttachmentItem = currentAttachmentItem else {
 | 
						|
            owsFailDebug("Missing currentAttachmentItem.")
 | 
						|
            return
 | 
						|
        }
 | 
						|
 | 
						|
        // TODO: Look at refactoring this behaviour to consolidate attachment mutations
 | 
						|
        currentAttachmentItem.attachment.captionText = attachmentCaptionToolbar.textView.text
 | 
						|
 | 
						|
        delegate?.attachmentApprovalInputUpdateMediaRail()
 | 
						|
    }
 | 
						|
 | 
						|
    public func attachmentCaptionToolbarDidComplete() {
 | 
						|
        delegate?.attachmentApprovalInputStopEditingCaptions()
 | 
						|
    }
 | 
						|
}
 |