202 lines
7.6 KiB
Swift
202 lines
7.6 KiB
Swift
//
|
|
// Copyright 2019 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import SignalServiceKit
|
|
import SignalUI
|
|
|
|
protocol CameraFirstCaptureDelegate: AnyObject {
|
|
func cameraFirstCaptureSendFlowDidComplete(_ cameraFirstCaptureSendFlow: CameraFirstCaptureSendFlow)
|
|
func cameraFirstCaptureSendFlowDidCancel(_ cameraFirstCaptureSendFlow: CameraFirstCaptureSendFlow)
|
|
}
|
|
|
|
class CameraFirstCaptureSendFlow: Dependencies {
|
|
|
|
private weak var delegate: CameraFirstCaptureDelegate?
|
|
|
|
private var approvedAttachments: [SignalAttachment]?
|
|
private var approvalMessageBody: MessageBody?
|
|
private var textAttachment: UnsentTextAttachment?
|
|
|
|
private var mentionCandidates: [SignalServiceAddress] = []
|
|
|
|
private let selection = ConversationPickerSelection()
|
|
private var selectedConversations: [ConversationItem] { selection.conversations }
|
|
|
|
private let storiesOnly: Bool
|
|
private var showsStoriesInPicker = true
|
|
|
|
init(storiesOnly: Bool, delegate: CameraFirstCaptureDelegate) {
|
|
self.storiesOnly = storiesOnly
|
|
self.delegate = delegate
|
|
}
|
|
|
|
private func updateMentionCandidates() {
|
|
AssertIsOnMainThread()
|
|
|
|
guard selectedConversations.count == 1,
|
|
case .group(let groupThreadId) = selectedConversations.first?.messageRecipient else {
|
|
mentionCandidates = []
|
|
return
|
|
}
|
|
|
|
let groupThread = databaseStorage.read { readTx in
|
|
TSGroupThread.anyFetchGroupThread(uniqueId: groupThreadId, transaction: readTx)
|
|
}
|
|
|
|
owsAssertDebug(groupThread != nil)
|
|
if let groupThread = groupThread, groupThread.allowsMentionSend {
|
|
mentionCandidates = groupThread.recipientAddressesWithSneakyTransaction
|
|
} else {
|
|
mentionCandidates = []
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CameraFirstCaptureSendFlow: SendMediaNavDelegate {
|
|
|
|
func sendMediaNavDidCancel(_ sendMediaNavigationController: SendMediaNavigationController) {
|
|
// Restore status bar visibility (if current VC hides it) so that
|
|
// there's no visible UI updates in the presenter.
|
|
if sendMediaNavigationController.topViewController?.prefersStatusBarHidden ?? false {
|
|
sendMediaNavigationController.modalPresentationCapturesStatusBarAppearance = false
|
|
sendMediaNavigationController.setNeedsStatusBarAppearanceUpdate()
|
|
}
|
|
delegate?.cameraFirstCaptureSendFlowDidCancel(self)
|
|
}
|
|
|
|
func sendMediaNav(_ sendMediaNavigationController: SendMediaNavigationController, didApproveAttachments attachments: [SignalAttachment], messageBody: MessageBody?) {
|
|
self.approvedAttachments = attachments
|
|
self.approvalMessageBody = messageBody
|
|
|
|
let pickerVC = ConversationPickerViewController(
|
|
selection: selection,
|
|
attachments: attachments
|
|
)
|
|
pickerVC.pickerDelegate = self
|
|
pickerVC.shouldBatchUpdateIdentityKeys = true
|
|
if storiesOnly {
|
|
pickerVC.isStorySectionExpanded = true
|
|
pickerVC.sectionOptions = .storiesOnly
|
|
} else if showsStoriesInPicker {
|
|
pickerVC.sectionOptions.insert(.stories)
|
|
}
|
|
sendMediaNavigationController.pushViewController(pickerVC, animated: true)
|
|
}
|
|
|
|
func sendMediaNav(_ sendMediaNavigationController: SendMediaNavigationController, didFinishWithTextAttachment textAttachment: UnsentTextAttachment) {
|
|
self.textAttachment = textAttachment
|
|
|
|
let pickerVC = ConversationPickerViewController(selection: selection, textAttacment: textAttachment)
|
|
pickerVC.pickerDelegate = self
|
|
pickerVC.shouldBatchUpdateIdentityKeys = true
|
|
if showsStoriesInPicker || storiesOnly {
|
|
pickerVC.isStorySectionExpanded = true
|
|
pickerVC.sectionOptions = .storiesOnly
|
|
} else {
|
|
owsFailDebug("Shouldn't ever have stories disabled with text attachments!")
|
|
}
|
|
sendMediaNavigationController.pushViewController(pickerVC, animated: true)
|
|
}
|
|
|
|
func sendMediaNav(_ sendMediaNavigationController: SendMediaNavigationController, didChangeMessageBody newMessageBody: MessageBody?) {
|
|
self.approvalMessageBody = newMessageBody
|
|
}
|
|
|
|
func sendMediaNav(_ sendMediaNavigationController: SendMediaNavigationController, didChangeViewOnceState isViewOnce: Bool) {
|
|
guard !self.storiesOnly else { return }
|
|
// Don't enable view once media to send to stories.
|
|
self.showsStoriesInPicker = !isViewOnce
|
|
}
|
|
}
|
|
|
|
extension CameraFirstCaptureSendFlow: SendMediaNavDataSource {
|
|
|
|
func sendMediaNavInitialMessageBody(_ sendMediaNavigationController: SendMediaNavigationController) -> MessageBody? {
|
|
return approvalMessageBody
|
|
}
|
|
|
|
var sendMediaNavTextInputContextIdentifier: String? {
|
|
nil
|
|
}
|
|
|
|
var sendMediaNavRecipientNames: [String] {
|
|
selectedConversations.map { $0.titleWithSneakyTransaction }
|
|
}
|
|
|
|
func sendMediaNavMentionableAddresses(tx: DBReadTransaction) -> [SignalServiceAddress] {
|
|
return mentionCandidates
|
|
}
|
|
|
|
func sendMediaNavMentionCacheInvalidationKey() -> String {
|
|
return "\(mentionCandidates.hashValue)"
|
|
}
|
|
}
|
|
|
|
extension CameraFirstCaptureSendFlow: ConversationPickerDelegate {
|
|
|
|
public func conversationPickerSelectionDidChange(_ conversationPickerViewController: ConversationPickerViewController) {
|
|
updateMentionCandidates()
|
|
}
|
|
|
|
public func conversationPickerDidCompleteSelection(_ conversationPickerViewController: ConversationPickerViewController) {
|
|
if let textAttachment = textAttachment {
|
|
let selectedStoryItems = selectedConversations.filter { $0 is StoryConversationItem }
|
|
guard !selectedStoryItems.isEmpty else {
|
|
owsFailDebug("Selection was unexpectedly empty.")
|
|
delegate?.cameraFirstCaptureSendFlowDidCancel(self)
|
|
return
|
|
}
|
|
|
|
firstly {
|
|
TSResourceMultisend.sendTextAttachment(
|
|
textAttachment,
|
|
to: selectedStoryItems
|
|
).enqueuedPromise
|
|
}.done { _ in
|
|
self.delegate?.cameraFirstCaptureSendFlowDidComplete(self)
|
|
}.catch { error in
|
|
owsFailDebug("Error: \(error)")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
guard let approvedAttachments = self.approvedAttachments else {
|
|
owsFailDebug("approvedAttachments was unexpectedly nil")
|
|
delegate?.cameraFirstCaptureSendFlowDidCancel(self)
|
|
return
|
|
}
|
|
|
|
let conversations = selectedConversations
|
|
firstly {
|
|
TSResourceMultisend.sendApprovedMedia(
|
|
conversations: conversations,
|
|
approvalMessageBody: self.approvalMessageBody,
|
|
approvedAttachments: approvedAttachments
|
|
).enqueuedPromise
|
|
}.done { _ in
|
|
self.delegate?.cameraFirstCaptureSendFlowDidComplete(self)
|
|
}.catch { error in
|
|
owsFailDebug("Error: \(error)")
|
|
}
|
|
}
|
|
|
|
public func conversationPickerCanCancel(_ conversationPickerViewController: ConversationPickerViewController) -> Bool {
|
|
return false
|
|
}
|
|
|
|
public func conversationPickerDidCancel(_ conversationPickerViewController: ConversationPickerViewController) {
|
|
owsFailDebug("Camera-first capture flow should never cancel conversation picker.")
|
|
}
|
|
|
|
public func approvalMode(_ conversationPickerViewController: ConversationPickerViewController) -> ApprovalMode {
|
|
return .send
|
|
}
|
|
|
|
public func conversationPickerDidBeginEditingText() {}
|
|
|
|
public func conversationPickerSearchBarActiveDidChange(_ conversationPickerViewController: ConversationPickerViewController) {}
|
|
}
|