diff --git a/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift b/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift index 8b7157891c..96293c04de 100644 --- a/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift +++ b/Signal/ConversationView/ConversationViewController+ConversationInputToolbarDelegate.swift @@ -424,6 +424,10 @@ extension ConversationViewController: ConversationInputToolbarDelegate { let didAddToProfileWhitelist = ThreadUtil.addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction(self.thread) + let hasViewOnceAttachment = attachments.contains(where: { $0.isViewOnceAttachment }) + owsPrecondition(!hasViewOnceAttachment || messageBody == nil) + owsPrecondition(!hasViewOnceAttachment || inputToolbar.quotedReplyDraft == nil) + ThreadUtil.enqueueMessage( body: messageBody, mediaAttachments: attachments, @@ -522,14 +526,10 @@ extension ConversationViewController: ConversationInputToolbarDelegate { dismissKeyBoard() - var options = AttachmentApprovalViewControllerOptions() - if inputToolbar?.quotedReplyDraft != nil { - options.insert(.disallowViewOnce) - } let pickerModal = SendMediaNavigationController.showingApprovalWithPickedLibraryMedia( asset: asset, attachment: attachment, - options: options, + hasQuotedReplyDraft: inputToolbar?.quotedReplyDraft != nil, delegate: self, dataSource: self ) @@ -556,18 +556,7 @@ public extension ConversationViewController { message: errorMessage) } - func showApprovalDialog(forAttachment attachment: SignalAttachment?) { - AssertIsOnMainThread() - - guard let attachment = attachment else { - owsFailDebug("attachment was unexpectedly nil") - showErrorAlert(forAttachment: attachment) - return - } - showApprovalDialog(forAttachments: [ attachment ]) - } - - func showApprovalDialog(forAttachments attachments: [SignalAttachment]) { + func showApprovalDialog(forAttachment attachment: SignalAttachment) { AssertIsOnMainThread() guard hasViewWillAppearEverBegun else { @@ -580,8 +569,9 @@ public extension ConversationViewController { } let modal = AttachmentApprovalViewController.wrappedInNavController( - attachments: attachments, + attachments: [attachment], initialMessageBody: inputToolbar.messageBodyForSending, + hasQuotedReplyDraft: inputToolbar.quotedReplyDraft != nil, approvalDelegate: self, approvalDataSource: self, stickerSheetDelegate: self @@ -654,7 +644,9 @@ fileprivate extension ConversationViewController { // be silent. } - let pickerModal = SendMediaNavigationController.showingCameraFirst() + let pickerModal = SendMediaNavigationController.showingCameraFirst( + hasQuotedReplyDraft: self.inputToolbar?.quotedReplyDraft != nil, + ) pickerModal.sendMediaNavDelegate = self pickerModal.sendMediaNavDataSource = self pickerModal.modalPresentationStyle = .overFullScreen @@ -678,7 +670,9 @@ fileprivate extension ConversationViewController { func chooseFromLibrary() { AssertIsOnMainThread() - let pickerModal = SendMediaNavigationController.showingNativePicker() + let pickerModal = SendMediaNavigationController.showingNativePicker( + hasQuotedReplyDraft: inputToolbar?.quotedReplyDraft != nil, + ) pickerModal.sendMediaNavDelegate = self pickerModal.sendMediaNavDataSource = self @@ -692,7 +686,10 @@ fileprivate extension ConversationViewController { public extension ConversationViewController { func showGifPicker() { - let gifModal = GifPickerNavigationViewController(initialMessageBody: inputToolbar?.messageBodyForSending) + let gifModal = GifPickerNavigationViewController( + initialMessageBody: inputToolbar?.messageBodyForSending, + hasQuotedReplyDraft: inputToolbar?.quotedReplyDraft != nil, + ) gifModal.approvalDelegate = self gifModal.approvalDataSource = self gifModal.presentationController?.delegate = self diff --git a/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift b/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift index c6f8729607..9fd7da1c14 100644 --- a/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift +++ b/Signal/src/ViewControllers/GifPicker/GifPickerViewController.swift @@ -11,7 +11,9 @@ class GifPickerNavigationViewController: OWSNavigationController { weak var approvalDelegate: AttachmentApprovalViewControllerDelegate? weak var approvalDataSource: AttachmentApprovalViewControllerDataSource? + private var initialMessageBody: MessageBody? + private let hasQuotedReplyDraft: Bool lazy var gifPickerViewController: GifPickerViewController = { let gifPickerViewController = GifPickerViewController() @@ -19,8 +21,9 @@ class GifPickerNavigationViewController: OWSNavigationController { return gifPickerViewController }() - init(initialMessageBody: MessageBody?) { + init(initialMessageBody: MessageBody?, hasQuotedReplyDraft: Bool) { self.initialMessageBody = initialMessageBody + self.hasQuotedReplyDraft = hasQuotedReplyDraft super.init() pushViewController(gifPickerViewController, animated: false) } @@ -31,7 +34,10 @@ extension GifPickerNavigationViewController: GifPickerViewControllerDelegate { AssertIsOnMainThread() let attachmentApprovalItem = AttachmentApprovalItem(attachment: attachment, canSave: false) - let attachmentApproval = AttachmentApprovalViewController(options: [], attachmentApprovalItems: [attachmentApprovalItem]) + let attachmentApproval = AttachmentApprovalViewController( + options: self.hasQuotedReplyDraft ? [.disallowViewOnce] : [], + attachmentApprovalItems: [attachmentApprovalItem], + ) attachmentApproval.setMessageBody(initialMessageBody, txProvider: DependenciesBridge.shared.db.readTxProvider) attachmentApproval.approvalDelegate = self attachmentApproval.approvalDataSource = self diff --git a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift index f450100b81..bc8115b688 100644 --- a/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift +++ b/Signal/src/ViewControllers/HomeView/Chat List/ChatListViewController+Camera.swift @@ -25,7 +25,7 @@ extension ChatListViewController: CameraFirstCaptureDelegate { Logger.warn("Proceeding with no microphone access.") } - let cameraModal = CameraFirstCaptureNavigationController.cameraFirstModal(delegate: self) + let cameraModal = CameraFirstCaptureNavigationController.cameraFirstModal(hasQuotedReplyDraft: false, delegate: self) cameraModal.modalPresentationStyle = .overFullScreen // Defer hiding status bar until modal is fully onscreen diff --git a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift index 646e76a84f..1a8696ffdd 100644 --- a/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift +++ b/Signal/src/ViewControllers/HomeView/Stories/StoriesViewController.swift @@ -240,7 +240,11 @@ class StoriesViewController: OWSViewController, StoryListDataSourceDelegate, Hom Logger.warn("proceeding, though mic permission denied.") } - let modal = CameraFirstCaptureNavigationController.cameraFirstModal(storiesOnly: true, delegate: self) + let modal = CameraFirstCaptureNavigationController.cameraFirstModal( + storiesOnly: true, + hasQuotedReplyDraft: false, + delegate: self, + ) self.presentFullScreen(modal, animated: true) } } diff --git a/Signal/src/ViewControllers/Photos/SendMediaNavigationController.swift b/Signal/src/ViewControllers/Photos/SendMediaNavigationController.swift index b804525982..e40e03f5d3 100644 --- a/Signal/src/ViewControllers/Photos/SendMediaNavigationController.swift +++ b/Signal/src/ViewControllers/Photos/SendMediaNavigationController.swift @@ -44,8 +44,12 @@ class CameraFirstCaptureNavigationController: SendMediaNavigationController { private var cameraFirstCaptureSendFlow: CameraFirstCaptureSendFlow! - class func cameraFirstModal(storiesOnly: Bool = false, delegate: CameraFirstCaptureDelegate) -> CameraFirstCaptureNavigationController { - let navController = CameraFirstCaptureNavigationController() + class func cameraFirstModal( + storiesOnly: Bool = false, + hasQuotedReplyDraft: Bool, + delegate: CameraFirstCaptureDelegate, + ) -> CameraFirstCaptureNavigationController { + let navController = CameraFirstCaptureNavigationController(hasQuotedReplyDraft: hasQuotedReplyDraft) navController.setViewControllers([navController.captureViewController], animated: false) let cameraFirstCaptureSendFlow = CameraFirstCaptureSendFlow(storiesOnly: storiesOnly, delegate: delegate) @@ -68,6 +72,13 @@ class SendMediaNavigationController: OWSNavigationController { fileprivate var canSendToStories: Bool { false } fileprivate var storiesOnly: Bool = false + private let hasQuotedReplyDraft: Bool + + fileprivate init(hasQuotedReplyDraft: Bool) { + self.hasQuotedReplyDraft = hasQuotedReplyDraft + super.init() + } + // MARK: - Overrides override var childForStatusBarStyle: UIViewController? { @@ -79,8 +90,8 @@ class SendMediaNavigationController: OWSNavigationController { weak var sendMediaNavDelegate: SendMediaNavDelegate? weak var sendMediaNavDataSource: SendMediaNavDataSource? - class func showingCameraFirst() -> SendMediaNavigationController { - let navController = SendMediaNavigationController() + class func showingCameraFirst(hasQuotedReplyDraft: Bool) -> SendMediaNavigationController { + let navController = SendMediaNavigationController(hasQuotedReplyDraft: hasQuotedReplyDraft) navController.setViewControllers([navController.captureViewController], animated: false) return navController } @@ -95,13 +106,14 @@ class SendMediaNavigationController: OWSNavigationController { return config } - class func showingNativePicker() -> SendMediaNavigationController { + class func showingNativePicker(hasQuotedReplyDraft: Bool) -> SendMediaNavigationController { // We want to present the photo picker in a sheet and then have the // editor appear behind it after you select photos, so present this // navigation controller as transparent with an empty view, then when // you select photos, `showApprovalViewController` will make it appear // behind the dismissing sheet and transition to the editor. - let navController = SendMediaNavigationController(rootViewController: UIViewController()) + let navController = SendMediaNavigationController(hasQuotedReplyDraft: hasQuotedReplyDraft) + navController.pushViewController(UIViewController(), animated: false) navController.view.layer.opacity = 0 navController.modalPresentationStyle = .overCurrentContext @@ -127,18 +139,17 @@ class SendMediaNavigationController: OWSNavigationController { class func showingApprovalWithPickedLibraryMedia( asset: PHAsset, attachment: SignalAttachment, - options: AttachmentApprovalViewControllerOptions = .init(), + hasQuotedReplyDraft: Bool, delegate: SendMediaNavDelegate, dataSource: SendMediaNavDataSource ) -> SendMediaNavigationController { - let navController = SendMediaNavigationController() + let navController = SendMediaNavigationController(hasQuotedReplyDraft: hasQuotedReplyDraft) navController.sendMediaNavDelegate = delegate navController.sendMediaNavDataSource = dataSource navController.modalPresentationStyle = .overCurrentContext let approvalItem = AttachmentApprovalItem(attachment: attachment, canSave: false) - let libraryMedia = MediaLibraryAttachment(asset: asset, - attachmentApprovalItemPromise: .value(approvalItem)) + let libraryMedia = MediaLibraryAttachment(asset: asset, attachmentApprovalItemPromise: .value(approvalItem)) navController.attachmentDrafts.append(.picker(attachment: libraryMedia)) navController.showApprovalViewController( @@ -199,6 +210,9 @@ class SendMediaNavigationController: OWSNavigationController { if canSendToStories, storiesOnly { options.insert(.disallowViewOnce) } + if hasQuotedReplyDraft { + options.insert(.disallowViewOnce) + } let approvalViewController = AttachmentApprovalViewController(options: options, attachmentApprovalItems: attachmentApprovalItems) approvalViewController.approvalDelegate = self approvalViewController.approvalDataSource = self diff --git a/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift b/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift index cf5ca2ce2b..ffa1611596 100644 --- a/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift +++ b/SignalUI/AttachmentApproval/AttachmentApprovalViewController.swift @@ -120,8 +120,7 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC private var observingKeyboardNotifications = false private var keyboardHeight: CGFloat = 0 - public init(options: AttachmentApprovalViewControllerOptions, - attachmentApprovalItems: [AttachmentApprovalItem]) { + public init(options: AttachmentApprovalViewControllerOptions, attachmentApprovalItems: [AttachmentApprovalItem]) { assert(attachmentApprovalItems.count > 0) self.receivedOptions = options @@ -161,13 +160,19 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC public class func wrappedInNavController( attachments: [SignalAttachment], initialMessageBody: MessageBody?, + hasQuotedReplyDraft: Bool, approvalDelegate: AttachmentApprovalViewControllerDelegate, approvalDataSource: AttachmentApprovalViewControllerDataSource, stickerSheetDelegate: StickerPickerSheetDelegate? ) -> OWSNavigationController { let attachmentApprovalItems = attachments.map { AttachmentApprovalItem(attachment: $0, canSave: false) } - let vc = AttachmentApprovalViewController(options: [.hasCancel], attachmentApprovalItems: attachmentApprovalItems) + var options: AttachmentApprovalViewControllerOptions = [] + options.insert(.hasCancel) + if hasQuotedReplyDraft { + options.insert(.disallowViewOnce) + } + let vc = AttachmentApprovalViewController(options: options, attachmentApprovalItems: attachmentApprovalItems) vc.setMessageBody(initialMessageBody, txProvider: DependenciesBridge.shared.db.readTxProvider) vc.approvalDelegate = approvalDelegate vc.approvalDataSource = approvalDataSource