Disable view once when a draft quoted reply exists

This commit is contained in:
Max Radermacher 2025-07-09 13:39:58 -05:00 committed by GitHub
parent c05053ed2a
commit ebdee843b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 64 additions and 38 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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