Can't paste multiple photos into compose bar
This commit is contained in:
parent
f9bf52ee7c
commit
e52ee7828e
@ -40,6 +40,6 @@ extension ConversationViewController: BodyRangesTextViewDelegate {
|
||||
public func textViewDidInsertMemoji(_ memojiGlyph: OWSAdaptiveImageGlyph) {
|
||||
// Note: attachment might be nil or have an error at this point; that's fine.
|
||||
let attachment = SignalAttachment.attachmentFromMemoji(memojiGlyph)
|
||||
self.didPasteAttachment(attachment)
|
||||
self.didPasteAttachments(attachment.map { [$0] })
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,7 +556,7 @@ public extension ConversationViewController {
|
||||
message: errorMessage)
|
||||
}
|
||||
|
||||
func showApprovalDialog(forAttachment attachment: SignalAttachment) {
|
||||
func showApprovalDialog(forAttachments attachments: [SignalAttachment]) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard hasViewWillAppearEverBegun else {
|
||||
@ -569,7 +569,7 @@ public extension ConversationViewController {
|
||||
}
|
||||
|
||||
let modal = AttachmentApprovalViewController.wrappedInNavController(
|
||||
attachments: [attachment],
|
||||
attachments: attachments,
|
||||
initialMessageBody: inputToolbar.messageBodyForSending,
|
||||
hasQuotedReplyDraft: inputToolbar.quotedReplyDraft != nil,
|
||||
approvalDelegate: self,
|
||||
@ -830,7 +830,7 @@ extension ConversationViewController: UIDocumentPickerDelegate {
|
||||
}
|
||||
|
||||
let attachment = SignalAttachment.attachment(dataSource: dataSource, dataUTI: contentType.identifier)
|
||||
showApprovalDialog(forAttachment: attachment)
|
||||
showApprovalDialog(forAttachments: [attachment])
|
||||
}
|
||||
|
||||
private func showApprovalDialogAfterProcessingVideoURL(_ movieURL: URL, filename: String?) {
|
||||
@ -864,7 +864,7 @@ extension ConversationViewController: UIDocumentPickerDelegate {
|
||||
owsFailDebug("Invalid attachment: \(attachment.errorName ?? "Unknown error").")
|
||||
self.showErrorAlert(forAttachment: attachment)
|
||||
} else {
|
||||
self.showApprovalDialog(forAttachment: attachment)
|
||||
self.showApprovalDialog(forAttachments: [attachment])
|
||||
}
|
||||
}
|
||||
}.catch(on: DispatchQueue.main) { error in
|
||||
|
||||
@ -220,39 +220,39 @@ extension ConversationViewController: ConversationInputTextViewDelegate {
|
||||
// the pasteboard will be cleared as soon as paste() exits.
|
||||
if SignalAttachment.pasteboardHasStickerAttachment() {
|
||||
let attachment: SignalAttachment? = SignalAttachment.stickerAttachmentFromPasteboard()
|
||||
self.didPasteAttachment(attachment)
|
||||
self.didPasteAttachments(attachment.map { [$0] })
|
||||
return
|
||||
}
|
||||
|
||||
ModalActivityIndicatorViewController.present(fromViewController: self) { modal in
|
||||
let attachment: SignalAttachment? = await SignalAttachment.attachmentFromPasteboard()
|
||||
let attachments: [SignalAttachment]? = await SignalAttachment.attachmentsFromPasteboard()
|
||||
|
||||
await MainActor.run {
|
||||
modal.dismiss {
|
||||
// Note: attachment might be nil or have an error at this point; that's fine.
|
||||
self.didPasteAttachment(attachment)
|
||||
// Note: attachment array might be nil or have an error at this point; that's fine.
|
||||
self.didPasteAttachments(attachments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func didPasteAttachment(_ attachment: SignalAttachment?) {
|
||||
func didPasteAttachments(_ attachments: [SignalAttachment]?) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let attachment = attachment else {
|
||||
owsFailDebug("Missing attachment.")
|
||||
guard let attachments, attachments.count > 0 else {
|
||||
owsFailDebug("Missing attachments")
|
||||
return
|
||||
}
|
||||
|
||||
// If the thing we pasted is sticker-like, send it immediately
|
||||
// and render it borderless.
|
||||
if attachment.isBorderless {
|
||||
if attachments.count == 1, let a = attachments.first, a.isBorderless {
|
||||
Task {
|
||||
await self.sendAttachments([attachment], from: self, messageBody: nil)
|
||||
await self.sendAttachments([a], from: self, messageBody: nil)
|
||||
}
|
||||
} else {
|
||||
dismissKeyBoard()
|
||||
showApprovalDialog(forAttachment: attachment)
|
||||
showApprovalDialog(forAttachments: attachments)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -633,22 +633,50 @@ public class SignalAttachment: NSObject {
|
||||
///
|
||||
/// NOTE: The attachment returned by this method may not be valid.
|
||||
/// Check the attachment's error property.
|
||||
public class func attachmentFromPasteboard() async -> SignalAttachment? {
|
||||
return await attachmentFromPasteboard(retrySinglePixelImages: true)
|
||||
public class func attachmentsFromPasteboard() async -> [SignalAttachment]? {
|
||||
guard UIPasteboard.general.numberOfItems >= 1,
|
||||
let pasteboardUTITypes = UIPasteboard.general.types(forItemSet: nil)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var attachments = [SignalAttachment]()
|
||||
for (index, utiSet) in pasteboardUTITypes.enumerated() {
|
||||
let attachment = await attachmentFromPasteboard(pasteboardUTIs: utiSet, index: IndexSet(integer: index), retrySinglePixelImages: true)
|
||||
|
||||
guard let attachment else {
|
||||
owsFailDebug("Missing attachment")
|
||||
continue
|
||||
}
|
||||
|
||||
if attachments.isEmpty {
|
||||
if attachment.allowMultipleAttachments() == false {
|
||||
// If this is a non-visual-media attachment, we only allow 1 pasted item at a time.
|
||||
return [attachment]
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, continue with any visual media attachments, dropping
|
||||
// any non-visual-media ones based on the first pasteboard item.
|
||||
if attachment.allowMultipleAttachments() {
|
||||
attachments.append(attachment)
|
||||
} else {
|
||||
Logger.warn("Dropping non-visual media attachment in paste action")
|
||||
}
|
||||
}
|
||||
return attachments
|
||||
}
|
||||
|
||||
private class func attachmentFromPasteboard(retrySinglePixelImages: Bool) async -> SignalAttachment? {
|
||||
guard UIPasteboard.general.numberOfItems >= 1 else {
|
||||
return nil
|
||||
}
|
||||
private func allowMultipleAttachments() -> Bool {
|
||||
return !self.isBorderless
|
||||
&& (MimeTypeUtil.isSupportedVideoMimeType(self.mimeType)
|
||||
|| MimeTypeUtil.isSupportedDefinitelyAnimatedMimeType(self.mimeType)
|
||||
|| MimeTypeUtil.isSupportedImageMimeType(self.mimeType))
|
||||
}
|
||||
|
||||
// If pasteboard contains multiple items, use only the first.
|
||||
let itemSet = IndexSet(integer: 0)
|
||||
guard let pasteboardUTITypes = UIPasteboard.general.types(forItemSet: itemSet) else {
|
||||
return nil
|
||||
}
|
||||
private class func attachmentFromPasteboard(pasteboardUTIs: [String], index: IndexSet, retrySinglePixelImages: Bool) async -> SignalAttachment? {
|
||||
|
||||
var pasteboardUTISet = Set<String>(filterDynamicUTITypes(pasteboardUTITypes[0]))
|
||||
var pasteboardUTISet = Set<String>(filterDynamicUTITypes(pasteboardUTIs))
|
||||
guard pasteboardUTISet.count > 0 else {
|
||||
return nil
|
||||
}
|
||||
@ -664,7 +692,7 @@ public class SignalAttachment: NSObject {
|
||||
|
||||
for dataUTI in inputImageUTISet {
|
||||
if pasteboardUTISet.contains(dataUTI) {
|
||||
guard let data = dataForFirstPasteboardItem(dataUTI: dataUTI) else {
|
||||
guard let data = dataForPasteboardItem(dataUTI: dataUTI, index: index) else {
|
||||
owsFailDebug("Missing expected pasteboard data for UTI: \(dataUTI)")
|
||||
return nil
|
||||
}
|
||||
@ -675,7 +703,7 @@ public class SignalAttachment: NSObject {
|
||||
// pasteboard after a brief delay (once, then give up).
|
||||
if dataSource?.imageMetadata.pixelSize == CGSize(square: 1), retrySinglePixelImages {
|
||||
try? await Task.sleep(nanoseconds: NSEC_PER_MSEC * 50)
|
||||
return await attachmentFromPasteboard(retrySinglePixelImages: false)
|
||||
return await attachmentFromPasteboard(pasteboardUTIs: pasteboardUTIs, index: index, retrySinglePixelImages: false)
|
||||
}
|
||||
|
||||
// If the data source is sticker like AND we're pasting the attachment,
|
||||
@ -688,7 +716,7 @@ public class SignalAttachment: NSObject {
|
||||
for dataUTI in videoUTISet {
|
||||
if pasteboardUTISet.contains(dataUTI) {
|
||||
guard
|
||||
let data = dataForFirstPasteboardItem(dataUTI: dataUTI),
|
||||
let data = dataForPasteboardItem(dataUTI: dataUTI, index: index),
|
||||
let dataSource = DataSourceValue(data, utiType: dataUTI)
|
||||
else {
|
||||
owsFailDebug("Failed to build data source from pasteboard data for UTI: \(dataUTI)")
|
||||
@ -703,7 +731,7 @@ public class SignalAttachment: NSObject {
|
||||
}
|
||||
for dataUTI in audioUTISet {
|
||||
if pasteboardUTISet.contains(dataUTI) {
|
||||
guard let data = dataForFirstPasteboardItem(dataUTI: dataUTI) else {
|
||||
guard let data = dataForPasteboardItem(dataUTI: dataUTI, index: index) else {
|
||||
owsFailDebug("Missing expected pasteboard data for UTI: \(dataUTI)")
|
||||
return nil
|
||||
}
|
||||
@ -713,7 +741,7 @@ public class SignalAttachment: NSObject {
|
||||
}
|
||||
|
||||
let dataUTI = pasteboardUTISet[pasteboardUTISet.startIndex]
|
||||
guard let data = dataForFirstPasteboardItem(dataUTI: dataUTI) else {
|
||||
guard let data = dataForPasteboardItem(dataUTI: dataUTI, index: index) else {
|
||||
owsFailDebug("Missing expected pasteboard data for UTI: \(dataUTI)")
|
||||
return nil
|
||||
}
|
||||
@ -741,7 +769,7 @@ public class SignalAttachment: NSObject {
|
||||
|
||||
for dataUTI in inputImageUTISet {
|
||||
if pasteboardUTISet.contains(dataUTI) {
|
||||
guard let data = dataForFirstPasteboardItem(dataUTI: dataUTI) else {
|
||||
guard let data = dataForPasteboardItem(dataUTI: dataUTI, index: IndexSet(integer: 0)) else {
|
||||
owsFailDebug("Missing expected pasteboard data for UTI: \(dataUTI)")
|
||||
return nil
|
||||
}
|
||||
@ -785,11 +813,8 @@ public class SignalAttachment: NSObject {
|
||||
return genericAttachment(dataSource: dataSource, dataUTI: dataUTI)
|
||||
}
|
||||
|
||||
// This method should only be called for dataUTIs that
|
||||
// are appropriate for the first pasteboard item.
|
||||
private class func dataForFirstPasteboardItem(dataUTI: String) -> Data? {
|
||||
let itemSet = IndexSet(integer: 0)
|
||||
guard let datas = UIPasteboard.general.data(forPasteboardType: dataUTI, inItemSet: itemSet) else {
|
||||
private class func dataForPasteboardItem(dataUTI: String, index: IndexSet) -> Data? {
|
||||
guard let datas = UIPasteboard.general.data(forPasteboardType: dataUTI, inItemSet: index) else {
|
||||
owsFailDebug("Missing expected pasteboard data for UTI: \(dataUTI)")
|
||||
return nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user