| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -306,7 +306,8 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        Logger.debug("\(keyboardStartFrame) -> \(keyboardEndFrame)")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lastObservedKeyboardTop = keyboardEndFrame.size.height
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        currentPageController.updateCaptionViewBottomInset()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let keyboardScenario: KeyboardScenario = bottomToolView.isEditingMediaMessage ? .editingMessage : .editingCaption
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        currentPageController.updateCaptionViewBottomInset(keyboardScenario: keyboardScenario)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    @objc
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -328,9 +329,8 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        Logger.debug("\(keyboardStartFrame) -> \(keyboardEndFrame)")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lastObservedKeyboardTop = keyboardEndFrame.size.height + keyboardStartFrame.minY - keyboardEndFrame.minY
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        currentPageController.updateCaptionViewBottomInset()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lastObservedKeyboardTop = UIScreen.main.bounds.height - keyboardEndFrame.size.height
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        currentPageController.updateCaptionViewBottomInset(keyboardScenario: .hidden)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // MARK: - View Helpers
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -424,7 +424,9 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            // use compact scale when keyboard is popped.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            let scale: AttachmentPrepViewController.AttachmentViewScale = self.isFirstResponder ? .fullsize : .compact
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            pendingPage.setAttachmentViewScale(scale, animated: false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            pendingPage.updateCaptionViewBottomInset()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            let keyboardScenario: KeyboardScenario = bottomToolView.isEditingMediaMessage ? .editingMessage : .hidden
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            pendingPage.updateCaptionViewBottomInset(keyboardScenario: keyboardScenario)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -670,39 +672,13 @@ extension AttachmentApprovalViewController: AttachmentPrepViewControllerDelegate
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        enablePaging()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    var desiredCaptionViewBottomInset: CGFloat {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let safeAreaInset: CGFloat
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if #available(iOS 11, *) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            safeAreaInset = view.safeAreaInsets.bottom
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            safeAreaInset = 0
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    func desiredCaptionViewBottomInset(keyboardScenario: KeyboardScenario) -> CGFloat {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        switch keyboardScenario {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case .hidden, .editingMessage:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return bottomToolView.bounds.height
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        case .editingCaption:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return lastObservedKeyboardTop
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // CaptionView bottom offset scenarios:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // 1. when no keyboard is popped (e.g. initially) to be *just* above the rail
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // 2. when the CaptionView becomes first responder, to be *just* above the keyboard, so the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        //    user can see what they're typing.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // For both these cases we apply the `lastObservedKeyboardTop`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        guard bottomToolView.mediaMessageTextToolbar.textView.isFirstResponder else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            // 3. Immediately after dismissing the CaptionView but before the ViewController
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    regains firstResponder, there is an instant where the inputAccessoryView is
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    not shown, so the lastObservedKeyboardTop is effectively 0. A moment later
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    when the ViewController regains firstResponder, the inputAccessoryView will be
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    presented. Naively, this would result in the CaptionView undesirably bouncing to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    the bottom of the ViewController, and then immediately back up as the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    inputAccessoryView is presented.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    Instead, we position the CaptionView where it will end up, by using
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    `bottomToolView.height`, which will only be greater than
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            //    `lastObserveredKeyboardTop` when the keyboard is not presented.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return max(bottomToolView.bounds.height, lastObservedKeyboardTop) - safeAreaInset
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // 4. when the MessageTextView becomes first responder, the keyboard should shift up
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        //    "in front" of the CaptionView
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return bottomToolView.bounds.height - safeAreaInset
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // MARK: Helpers
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -759,6 +735,10 @@ extension AttachmentApprovalViewController: GalleryRailViewDelegate {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// MARK: - Individual Page
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				enum KeyboardScenario {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    case hidden, editingMessage, editingCaption
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				protocol AttachmentPrepViewControllerDelegate: class {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    func prepViewController(_ prepViewController: AttachmentPrepViewController, didUpdateCaptionForAttachmentItem attachmentItem: SignalAttachmentItem)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -766,7 +746,7 @@ protocol AttachmentPrepViewControllerDelegate: class {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    func prepViewController(_ prepViewController: AttachmentPrepViewController, didBeginEditingCaptionView captionView: CaptionView)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    func prepViewController(_ prepViewController: AttachmentPrepViewController, didEndEditingCaptionView captionView: CaptionView)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    var desiredCaptionViewBottomInset: CGFloat { get }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    func desiredCaptionViewBottomInset(keyboardScenario: KeyboardScenario) -> CGFloat
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarDelegate, OWSVideoPlayerDelegate {
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -931,7 +911,7 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        captionView.delegate = self
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        captionView.autoPinWidthToSuperview()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        captionViewBottomConstraint = captionView.autoPinEdge(toSuperviewMargin: .bottom)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        captionViewBottomConstraint = captionView.autoPinEdge(toSuperviewEdge: .bottom)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    override public func viewWillLayoutSubviews() {
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -948,14 +928,14 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    var hasLaidOutCaptionView: Bool = false
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    var captionViewBottomConstraint: NSLayoutConstraint!
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    func updateCaptionViewBottomInset() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    func updateCaptionViewBottomInset(keyboardScenario: KeyboardScenario) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        guard let prepDelegate = self.prepDelegate else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            owsFailDebug("prepDelegate was unexpectedly nil")
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let changeBlock = {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            let offset: CGFloat = -1 * prepDelegate.desiredCaptionViewBottomInset
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            let offset: CGFloat = -1 * prepDelegate.desiredCaptionViewBottomInset(keyboardScenario: keyboardScenario)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            self.captionViewBottomConstraint.constant = offset
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            self.captionView.superview?.layoutIfNeeded()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -1229,6 +1209,10 @@ class BottomToolView: UIView {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    let mediaMessageTextToolbar: MediaMessageTextToolbar
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    let galleryRailView: GalleryRailView
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    var isEditingMediaMessage: Bool {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return mediaMessageTextToolbar.textView.isFirstResponder
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    let kGalleryRailViewHeight: CGFloat = 72
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    required init(isAddMoreVisible: Bool) {
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |