Multi-approval

pull/1/head
Michael Kirk 6 years ago
parent b519baf37d
commit 7cef41f8e2

@ -2681,9 +2681,7 @@ typedef enum : NSUInteger {
- (void)imagePicker:(OWSImagePickerGridController *)imagePicker
didPickImageAttachments:(NSArray<SignalAttachment *> *)attachments
{
// TODO support approving multiple attachments.
SignalAttachment *firstAttachment = attachments.firstObject;
[self tryToSendAttachmentIfApproved:firstAttachment];
[self tryToSendAttachmentsIfApproved:attachments];
}
/*
@ -2770,22 +2768,21 @@ typedef enum : NSUInteger {
}
}
- (void)sendMessageAttachment:(SignalAttachment *)attachment
- (void)sendMessageAttachments:(NSArray<SignalAttachment *> *)attachments
{
OWSAssertIsOnMainThread();
// TODO: Should we assume non-nil or should we check for non-nil?
OWSAssertDebug(attachment != nil);
OWSAssertDebug(![attachment hasError]);
OWSAssertDebug([attachment mimeType].length > 0);
OWSLogVerbose(@"Sending attachment. Size in bytes: %lu, contentType: %@",
(unsigned long)[attachment dataLength],
[attachment mimeType]);
for (SignalAttachment *attachment in attachments) {
// TODO: Should we assume non-nil or should we check for non-nil?
OWSAssertDebug(attachment != nil);
OWSAssertDebug(![attachment hasError]);
OWSAssertDebug([attachment mimeType].length > 0);
}
BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread];
TSOutgoingMessage *message = [ThreadUtil enqueueMessageWithAttachment:attachment
inThread:self.thread
quotedReplyModel:self.inputToolbar.quotedReply];
TSOutgoingMessage *message = [ThreadUtil enqueueMessageWithAttachments:attachments
messageBody:nil
inThread:self.thread
quotedReplyModel:self.inputToolbar.quotedReply];
[self messageWasSent:message];
@ -3447,11 +3444,32 @@ typedef enum : NSUInteger {
- (void)tryToSendAttachmentIfApproved:(SignalAttachment *_Nullable)attachment
{
[self tryToSendAttachmentIfApproved:attachment skipApprovalDialog:NO];
if (attachment == nil) {
OWSLogWarn(@"Missing attachment");
[self showErrorAlertForAttachment:nil];
return;
}
[self tryToSendAttachmentsIfApproved:@[ attachment ]];
}
- (void)tryToSendAttachmentIfApproved:(SignalAttachment *_Nullable)attachment
skipApprovalDialog:(BOOL)skipApprovalDialog
{
if (attachment == nil) {
OWSLogWarn(@"Missing attachment");
[self showErrorAlertForAttachment:nil];
return;
}
[self tryToSendAttachmentsIfApproved:@[ attachment ] skipApprovalDialog:skipApprovalDialog];
}
- (void)tryToSendAttachmentsIfApproved:(NSArray<SignalAttachment *> *)attachments
{
[self tryToSendAttachmentsIfApproved:attachments skipApprovalDialog:NO];
}
- (void)tryToSendAttachmentsIfApproved:(NSArray<SignalAttachment *> *)attachments
skipApprovalDialog:(BOOL)skipApprovalDialog
{
OWSLogError(@"");
@ -3460,7 +3478,7 @@ typedef enum : NSUInteger {
if ([self isBlockedConversation]) {
[self showUnblockConversationUI:^(BOOL isBlocked) {
if (!isBlocked) {
[weakSelf tryToSendAttachmentIfApproved:attachment];
[weakSelf tryToSendAttachmentsIfApproved:attachments];
}
}];
return;
@ -3471,21 +3489,27 @@ typedef enum : NSUInteger {
completion:^(BOOL didConfirmIdentity) {
if (didConfirmIdentity) {
[weakSelf
tryToSendAttachmentIfApproved:attachment];
tryToSendAttachmentsIfApproved:attachments];
}
}];
if (didShowSNAlert) {
return;
}
if (attachment == nil || [attachment hasError]) {
OWSLogWarn(@"Invalid attachment: %@.", attachment ? [attachment errorName] : @"Missing data");
[self showErrorAlertForAttachment:attachment];
} else if (skipApprovalDialog) {
[self sendMessageAttachment:attachment];
for (SignalAttachment *attachment in attachments) {
if ([attachment hasError]) {
OWSLogWarn(@"Invalid attachment: %@.", attachment ? [attachment errorName] : @"Missing data");
[self showErrorAlertForAttachment:attachment];
return;
}
}
if (skipApprovalDialog) {
[self sendMessageAttachments:attachments];
} else {
OWSNavigationController *modal =
[AttachmentApprovalViewController wrappedInNavControllerWithAttachment:attachment delegate:self];
[AttachmentApprovalViewController wrappedInNavControllerWithAttachments:attachments
approvalDelegate:self];
[self presentViewController:modal animated:YES completion:nil];
}
});
@ -3595,9 +3619,10 @@ typedef enum : NSUInteger {
[self updateNavigationBarSubtitleLabel];
}
- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval didApproveAttachment:(SignalAttachment * _Nonnull)attachment
- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval
didApproveAttachments:(NSArray<SignalAttachment *> *)attachments
{
[self sendMessageAttachment:attachment];
[self sendMessageAttachments:attachments];
[self dismissViewControllerAnimated:YES completion:nil];
// We always want to scroll to the bottom of the conversation after the local user
// sends a message. Normally, this is taken care of in yapDatabaseModified:, but
@ -3606,7 +3631,8 @@ typedef enum : NSUInteger {
[self scrollToBottomAnimated:YES];
}
- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval didCancelAttachment:(SignalAttachment * _Nonnull)attachment
- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval
didCancelAttachments:(NSArray<SignalAttachment *> *)attachment
{
[self dismissViewControllerAnimated:YES completion:nil];
}

@ -8,31 +8,22 @@ import MediaPlayer
@objc
public protocol AttachmentApprovalViewControllerDelegate: class {
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didApproveAttachment attachment: SignalAttachment)
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didCancelAttachment attachment: SignalAttachment)
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didApproveAttachments attachments: [SignalAttachment])
func attachmentApproval(_ attachmentApproval: AttachmentApprovalViewController, didCancelAttachments attachments: [SignalAttachment])
}
@objc
public class AttachmentApprovalViewController: OWSViewController, CaptioningToolbarDelegate, PlayerProgressBarDelegate, OWSVideoPlayerDelegate {
weak var delegate: AttachmentApprovalViewControllerDelegate?
struct SignalAttachmentItem: Hashable {
let attachment: SignalAttachment
}
// We sometimes shrink the attachment view so that it remains somewhat visible
// when the keyboard is presented.
enum AttachmentViewScale {
case fullsize, compact
}
@objc
public class AttachmentApprovalViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, CaptioningToolbarDelegate {
// MARK: Properties
let attachment: SignalAttachment
private var videoPlayer: OWSVideoPlayer?
weak var approvalDelegate: AttachmentApprovalViewControllerDelegate?
private(set) var bottomToolbar: UIView!
private(set) var mediaMessageView: MediaMessageView!
private(set) var scrollView: UIScrollView!
private(set) var contentContainer: UIView!
private(set) var playVideoButton: UIView?
private(set) var captioningToolbar: CaptioningToolbar!
// MARK: Initializers
@ -41,29 +32,23 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
notImplemented()
}
@objc
required public init(attachment: SignalAttachment, delegate: AttachmentApprovalViewControllerDelegate) {
assert(!attachment.hasError)
self.attachment = attachment
self.delegate = delegate
super.init(nibName: nil, bundle: nil)
}
// MARK: View Lifecycle
override public func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = nil
let kSpacingBetweenItems: CGFloat = 20
let cancelButton = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(cancelPressed))
cancelButton.tintColor = .white
self.navigationItem.leftBarButtonItem = cancelButton
@objc
required public init(attachments: [SignalAttachment]) {
assert(attachments.count > 0)
self.attachmentItems = attachments.map { SignalAttachmentItem(attachment: $0 )}
super.init(transitionStyle: .scroll,
navigationOrientation: .horizontal,
options: [UIPageViewControllerOptionInterPageSpacingKey: kSpacingBetweenItems])
self.dataSource = self
self.delegate = self
}
@objc
public class func wrappedInNavController(attachment: SignalAttachment, delegate: AttachmentApprovalViewControllerDelegate) -> OWSNavigationController {
let vc = AttachmentApprovalViewController(attachment: attachment, delegate: delegate)
public class func wrappedInNavController(attachments: [SignalAttachment], approvalDelegate: AttachmentApprovalViewControllerDelegate) -> OWSNavigationController {
let vc = AttachmentApprovalViewController(attachments: attachments)
vc.approvalDelegate = approvalDelegate
let navController = OWSNavigationController(rootViewController: vc)
guard let navigationBar = navController.navigationBar as? OWSNavigationBar else {
@ -75,12 +60,35 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
return navController
}
override public func viewWillLayoutSubviews() {
Logger.debug("")
super.viewWillLayoutSubviews()
// MARK: View Lifecycle
// e.g. if flipping to/from landscape
updateMinZoomScaleForSize(view.bounds.size)
override public func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .black
disablePagingIfNecessary()
// Bottom Toolbar
let captioningToolbar = CaptioningToolbar()
captioningToolbar.captioningToolbarDelegate = self
self.captioningToolbar = captioningToolbar
// Navigation
self.navigationItem.title = nil
let cancelButton = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(cancelPressed))
cancelButton.tintColor = .white
self.navigationItem.leftBarButtonItem = cancelButton
guard let firstItem = attachmentItems.first else {
owsFailDebug("firstItem was unexpectedly nil")
return
}
self.setCurrentItem(firstItem, direction: .forward, animated: false)
}
override public func viewWillAppear(_ animated: Bool) {
@ -105,10 +113,277 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
CurrentAppContext().setStatusBarHidden(false, animated: false)
}
// MARK: - Create Views
override public var inputAccessoryView: UIView? {
self.captioningToolbar.layoutIfNeeded()
return self.captioningToolbar
}
override public var canBecomeFirstResponder: Bool {
return true
}
// MARK: - View Helpers
public override func loadView() {
var pagerScrollView: UIScrollView?
// This is kind of a hack. Since we don't have first class access to the superview's `scrollView`
// we traverse the view hierarchy until we find it, then disable scrolling if there's only one
// item. This avoids an unpleasant "bounce" which doesn't make sense in the context of a single item.
fileprivate func disablePagingIfNecessary() {
for view in self.view.subviews {
if let pagerScrollView = view as? UIScrollView {
self.pagerScrollView = pagerScrollView
break
}
}
guard let pagerScrollView = self.pagerScrollView else {
owsFailDebug("pagerScrollView was unexpectedly nil")
return
}
pagerScrollView.isScrollEnabled = attachmentItems.count > 1
}
private func makeClearToolbar() -> UIToolbar {
let toolbar = UIToolbar()
toolbar.backgroundColor = UIColor.clear
// Making a toolbar transparent requires setting an empty uiimage
toolbar.setBackgroundImage(UIImage(), forToolbarPosition: .any, barMetrics: .default)
// hide 1px top-border
toolbar.clipsToBounds = true
return toolbar
}
// MARK: UIPageViewControllerDelegate
public func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
Logger.debug("")
assert(pendingViewControllers.count == 1)
pendingViewControllers.forEach { viewController in
guard let pendingPage = viewController as? AttachmentPrepViewController else {
owsFailDebug("unexpected viewController: \(viewController)")
return
}
// use compact scale when keyboard is popped.
let scale: AttachmentPrepViewController.AttachmentViewScale = self.isFirstResponder ? .fullsize : .compact
pendingPage.setAttachmentViewScale(scale, animated: false)
}
}
public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted: Bool) {
Logger.debug("")
assert(previousViewControllers.count == 1)
previousViewControllers.forEach { viewController in
guard let previousPage = viewController as? AttachmentPrepViewController else {
owsFailDebug("unexpected viewController: \(viewController)")
return
}
if transitionCompleted {
UIView.transition(with: self.captioningToolbar,
duration: 0.1,
options: .transitionCrossDissolve,
animations: {
self.captioningToolbar.captionText = self.currentViewController.attachment.captionText
},
completion: nil)
previousPage.zoomOut(animated: false)
}
}
}
// MARK: UIPageViewControllerDataSource
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let currentViewController = viewController as? AttachmentPrepViewController else {
owsFailDebug("unexpected viewController: \(viewController)")
return nil
}
let currentItem = currentViewController.attachmentItem
guard let previousItem = attachmentItem(before: currentItem) else {
return nil
}
guard let previousPage: AttachmentPrepViewController = buildPage(item: previousItem) else {
return nil
}
return previousPage
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
Logger.debug("")
guard let currentViewController = viewController as? AttachmentPrepViewController else {
owsFailDebug("unexpected viewController: \(viewController)")
return nil
}
let currentItem = currentViewController.attachmentItem
guard let nextItem = attachmentItem(after: currentItem) else {
return nil
}
guard let nextPage: AttachmentPrepViewController = buildPage(item: nextItem) else {
return nil
}
return nextPage
}
public var currentViewController: AttachmentPrepViewController {
return viewControllers!.first as! AttachmentPrepViewController
}
var currentItem: SignalAttachmentItem! {
get {
return currentViewController.attachmentItem
}
set {
setCurrentItem(newValue, direction: .forward, animated: false)
}
}
private var cachedPages: [SignalAttachmentItem: AttachmentPrepViewController] = [:]
private func buildPage(item: SignalAttachmentItem) -> AttachmentPrepViewController? {
if let cachedPage = cachedPages[item] {
Logger.debug("cache hit.")
return cachedPage
}
Logger.debug("cache miss.")
let viewController = AttachmentPrepViewController(attachmentItem: item)
cachedPages[item] = viewController
return viewController
}
private func setCurrentItem(_ item: SignalAttachmentItem, direction: UIPageViewControllerNavigationDirection, animated isAnimated: Bool) {
guard let page = self.buildPage(item: item) else {
owsFailDebug("unexpetedly unable to build new page")
return
}
self.setViewControllers([page], direction: direction, animated: isAnimated, completion: nil)
// TODO update rail
}
let attachmentItems: [SignalAttachmentItem]
var attachments: [SignalAttachment] {
return attachmentItems.map { $0.attachment }
}
func attachmentItem(before currentItem: SignalAttachmentItem) -> SignalAttachmentItem? {
guard let currentIndex = attachmentItems.index(of: currentItem) else {
owsFailDebug("currentIndex was unexpectedly nil")
return nil
}
let index: Int = attachmentItems.index(before: currentIndex)
guard let previousItem = attachmentItems[safe: index] else {
// already at first item
return nil
}
return previousItem
}
func attachmentItem(after currentItem: SignalAttachmentItem) -> SignalAttachmentItem? {
guard let currentIndex = attachmentItems.index(of: currentItem) else {
owsFailDebug("currentIndex was unexpectedly nil")
return nil
}
let index: Int = attachmentItems.index(after: currentIndex)
guard let nextItem = attachmentItems[safe: index] else {
// already at last item
return nil
}
return nextItem
}
// MARK: - Event Handlers
@objc func cancelPressed(sender: UIButton) {
self.approvalDelegate?.attachmentApproval(self, didCancelAttachments: attachments)
}
// MARK: CaptioningToolbarDelegate
var currentPageController: AttachmentPrepViewController {
return viewControllers!.first as! AttachmentPrepViewController
}
func captioningToolbarDidBeginEditing(_ captioningToolbar: CaptioningToolbar) {
currentPageController.setAttachmentViewScale(.compact, animated: true)
}
func captioningToolbarDidEndEditing(_ captioningToolbar: CaptioningToolbar) {
currentPageController.setAttachmentViewScale(.fullsize, animated: true)
}
func captioningToolbarDidTapSend(_ captioningToolbar: CaptioningToolbar) {
// Toolbar flickers in and out if there are errors
// and remains visible momentarily after share extension is dismissed.
// It's easiest to just hide it at this point since we're done with it.
currentViewController.shouldAllowAttachmentViewResizing = false
captioningToolbar.isUserInteractionEnabled = false
captioningToolbar.isHidden = true
approvalDelegate?.attachmentApproval(self, didApproveAttachments: attachments)
}
func captioningToolbar(_ captioningToolbar: CaptioningToolbar, textViewDidChange textView: UITextView) {
currentItem.attachment.captionText = textView.text
}
}
public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarDelegate, OWSVideoPlayerDelegate {
// We sometimes shrink the attachment view so that it remains somewhat visible
// when the keyboard is presented.
enum AttachmentViewScale {
case fullsize, compact
}
// MARK: Properties
let attachmentItem: SignalAttachmentItem
var attachment: SignalAttachment {
return attachmentItem.attachment
}
private var videoPlayer: OWSVideoPlayer?
private(set) var mediaMessageView: MediaMessageView!
private(set) var scrollView: UIScrollView!
private(set) var contentContainer: UIView!
private(set) var playVideoButton: UIView?
// MARK: Initializers
init(attachmentItem: SignalAttachmentItem) {
self.attachmentItem = attachmentItem
super.init(nibName: nil, bundle: nil)
assert(!attachment.hasError)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: View Lifecycle
override public func loadView() {
self.view = UIView()
self.mediaMessageView = MediaMessageView(attachment: attachment, mode: .attachmentApproval)
@ -160,11 +435,6 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
topGradient.autoSetDimension(.height, toSize: ScaleFromIPhone5(60))
}
// Bottom Toolbar
let captioningToolbar = CaptioningToolbar()
captioningToolbar.captioningToolbarDelegate = self
self.bottomToolbar = captioningToolbar
// Hide the play button embedded in the MediaView and replace it with our own.
// This allows us to zoom in on the media view without zooming in on the button
if attachment.isVideo {
@ -215,32 +485,21 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
}
}
@objc public func didTapPlayerView(_ gestureRecognizer: UIGestureRecognizer) {
assert(self.videoPlayer != nil)
self.pauseVideo()
}
override public func viewWillLayoutSubviews() {
Logger.debug("")
super.viewWillLayoutSubviews()
override public var inputAccessoryView: UIView? {
self.bottomToolbar.layoutIfNeeded()
return self.bottomToolbar
}
// e.g. if flipping to/from landscape
updateMinZoomScaleForSize(view.bounds.size)
override public var canBecomeFirstResponder: Bool {
return true
ensureAttachmentViewScale(animated: false)
}
private func makeClearToolbar() -> UIToolbar {
let toolbar = UIToolbar()
toolbar.backgroundColor = UIColor.clear
// Making a toolbar transparent requires setting an empty uiimage
toolbar.setBackgroundImage(UIImage(), forToolbarPosition: .any, barMetrics: .default)
// hide 1px top-border
toolbar.clipsToBounds = true
// MARK:
return toolbar
@objc public func didTapPlayerView(_ gestureRecognizer: UIGestureRecognizer) {
assert(self.videoPlayer != nil)
self.pauseVideo()
}
// MARK: - Event Handlers
@ -250,24 +509,6 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
self.playVideo()
}
@objc func cancelPressed(sender: UIButton) {
self.delegate?.attachmentApproval(self, didCancelAttachment: attachment)
}
// MARK: CaptioningToolbarDelegate
func captioningToolbarDidBeginEditing(_ captioningToolbar: CaptioningToolbar) {
self.scaleAttachmentView(.compact)
}
func captioningToolbarDidEndEditing(_ captioningToolbar: CaptioningToolbar) {
self.scaleAttachmentView(.fullsize)
}
func captioningToolbarDidTapSend(_ captioningToolbar: CaptioningToolbar, captionText: String?) {
self.approveAttachment(captionText: captionText)
}
// MARK: Video
private func playVideo() {
@ -351,39 +592,46 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
return attachment.isImage || attachment.isVideo
}
private func approveAttachment(captionText: String?) {
// Toolbar flickers in and out if there are errors
// and remains visible momentarily after share extension is dismissed.
// It's easiest to just hide it at this point since we're done with it.
shouldAllowAttachmentViewResizing = false
bottomToolbar.isUserInteractionEnabled = false
bottomToolbar.isHidden = true
attachment.captionText = captionText
delegate?.attachmentApproval(self, didApproveAttachment: attachment)
func zoomOut(animated: Bool) {
if self.scrollView.zoomScale != self.scrollView.minimumZoomScale {
self.scrollView.setZoomScale(self.scrollView.minimumZoomScale, animated: animated)
}
}
// When the keyboard is popped, it can obscure the attachment view.
// so we sometimes allow resizing the attachment.
private var shouldAllowAttachmentViewResizing: Bool = true
var shouldAllowAttachmentViewResizing: Bool = true
var attachmentViewScale: AttachmentViewScale = .fullsize
fileprivate func setAttachmentViewScale(_ attachmentViewScale: AttachmentViewScale, animated: Bool) {
self.attachmentViewScale = attachmentViewScale
ensureAttachmentViewScale(animated: animated)
}
private func scaleAttachmentView(_ fit: AttachmentViewScale) {
func ensureAttachmentViewScale(animated: Bool) {
let animationDuration = animated ? 0.2 : 0
guard shouldAllowAttachmentViewResizing else {
if self.contentContainer.transform != CGAffineTransform.identity {
UIView.animate(withDuration: 0.2) {
UIView.animate(withDuration: animationDuration) {
self.contentContainer.transform = CGAffineTransform.identity
}
}
return
}
switch fit {
switch attachmentViewScale {
case .fullsize:
UIView.animate(withDuration: 0.2) {
guard self.contentContainer.transform != .identity else {
return
}
UIView.animate(withDuration: animationDuration) {
self.contentContainer.transform = CGAffineTransform.identity
}
case .compact:
UIView.animate(withDuration: 0.2) {
guard self.contentContainer.transform == .identity else {
return
}
UIView.animate(withDuration: animationDuration) {
let kScaleFactor: CGFloat = 0.7
let scale = CGAffineTransform(scaleX: kScaleFactor, y: kScaleFactor)
@ -400,7 +648,7 @@ public class AttachmentApprovalViewController: OWSViewController, CaptioningTool
}
}
extension AttachmentApprovalViewController: UIScrollViewDelegate {
extension AttachmentPrepViewController: UIScrollViewDelegate {
public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
if isZoomable {
@ -469,9 +717,10 @@ extension AttachmentApprovalViewController: UIScrollViewDelegate {
}
protocol CaptioningToolbarDelegate: class {
func captioningToolbarDidTapSend(_ captioningToolbar: CaptioningToolbar, captionText: String?)
func captioningToolbarDidTapSend(_ captioningToolbar: CaptioningToolbar)
func captioningToolbarDidBeginEditing(_ captioningToolbar: CaptioningToolbar)
func captioningToolbarDidEndEditing(_ captioningToolbar: CaptioningToolbar)
func captioningToolbar(_ captioningToolbar: CaptioningToolbar, textViewDidChange: UITextView)
}
class CaptioningToolbar: UIView, UITextViewDelegate {
@ -479,6 +728,12 @@ class CaptioningToolbar: UIView, UITextViewDelegate {
weak var captioningToolbarDelegate: CaptioningToolbarDelegate?
private let sendButton: UIButton
private let textView: UITextView
var captionText: String? {
get { return self.textView.text }
set { self.textView.text = newValue }
}
private let bottomGradient: GradientView
private let lengthLimitLabel: UILabel
@ -493,10 +748,6 @@ class CaptioningToolbar: UIView, UITextViewDelegate {
var textViewHeightConstraint: NSLayoutConstraint!
var textViewHeight: CGFloat
required init?(coder aDecoder: NSCoder) {
notImplemented()
}
class MessageTextView: UITextView {
// When creating new lines, contentOffset is animated, but because because
// we are simultaneously resizing the text view, this can cause the
@ -515,6 +766,8 @@ class CaptioningToolbar: UIView, UITextViewDelegate {
}
}
// MARK: Initializers
init() {
self.sendButton = UIButton(type: .system)
self.bottomGradient = GradientView(from: UIColor.clear, to: UIColor.black)
@ -626,14 +879,21 @@ class CaptioningToolbar: UIView, UITextViewDelegate {
bottomGradient.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .top)
}
required init?(coder aDecoder: NSCoder) {
notImplemented()
}
// MARK:
@objc func didTapSend() {
self.captioningToolbarDelegate?.captioningToolbarDidTapSend(self, captionText: self.textView.text)
self.captioningToolbarDelegate?.captioningToolbarDidTapSend(self)
}
// MARK: - UITextViewDelegate
public func textViewDidChange(_ textView: UITextView) {
updateHeight(textView: textView)
self.captioningToolbarDelegate?.captioningToolbar(self, textViewDidChange: textView)
}
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

@ -163,8 +163,10 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion);
[self.navigationController pushViewController:approvalVC animated:YES];
} else {
// TODO ALBUMS - send album via SAE
OWSNavigationController *approvalModal =
[AttachmentApprovalViewController wrappedInNavControllerWithAttachment:self.attachment delegate:self];
[AttachmentApprovalViewController wrappedInNavControllerWithAttachments:@[ self.attachment ]
approvalDelegate:self];
[self presentViewController:approvalModal animated:YES completion:nil];
}
}
@ -229,7 +231,7 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion);
#pragma mark - AttachmentApprovalViewControllerDelegate
- (void)attachmentApproval:(AttachmentApprovalViewController *)approvalViewController
didApproveAttachment:(SignalAttachment *)attachment
didApproveAttachments:(NSArray<SignalAttachment *> *)attachments
{
[ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread];
[self tryToSendMessageWithBlock:^(SendCompletionBlock sendCompletion) {
@ -239,7 +241,8 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion);
// DURABLE CLEANUP - SAE uses non-durable sending to make sure the app is running long enough to complete
// the sending operation. Alternatively, we could use a durable send, but do more to make sure the
// SAE runs as long as it needs.
outgoingMessage = [ThreadUtil sendMessageNonDurablyWithAttachment:attachment
// TODO ALBUMS - send album via SAE
outgoingMessage = [ThreadUtil sendMessageNonDurablyWithAttachment:attachments.firstObject
inThread:self.thread
quotedReplyModel:nil
messageSender:self.messageSender
@ -254,7 +257,7 @@ typedef void (^SendMessageBlock)(SendCompletionBlock completion);
}
- (void)attachmentApproval:(AttachmentApprovalViewController *)attachmentApproval
didCancelAttachment:(SignalAttachment *)attachment
didCancelAttachments:(NSArray<SignalAttachment *> *)attachment
{
[self cancelShareExperience];
}

Loading…
Cancel
Save