session-ios/SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentApprovalInputAcce...

203 lines
7.2 KiB
Swift

//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import UIKit
import SessionUIKit
protocol AttachmentApprovalInputAccessoryViewDelegate: class {
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 = isLightMode ? UIColor.black.withAlphaComponent(0.2) : 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)
}
// 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
}
currentAttachmentItem.attachment.captionText = attachmentCaptionToolbar.textView.text
delegate?.attachmentApprovalInputUpdateMediaRail()
}
public func attachmentCaptionToolbarDidComplete() {
delegate?.attachmentApprovalInputStopEditingCaptions()
}
}