浏览代码

initial commit for image/gif drafts

cyberta 4 年之前
父节点
当前提交
d601bfabcf

+ 4 - 0
deltachat-ios.xcodeproj/project.pbxproj

@@ -21,6 +21,7 @@
 		302E1BB4252B5AB4008F4264 /* PlayButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302E1BB3252B5AB4008F4264 /* PlayButtonView.swift */; };
 		30349291256441E200A523D0 /* QuotePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30349290256441E200A523D0 /* QuotePreview.swift */; };
 		303492952565AABC00A523D0 /* DraftModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303492942565AABC00A523D0 /* DraftModel.swift */; };
+		3034929F25752FC800A523D0 /* MediaPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3034929E25752FC800A523D0 /* MediaPreview.swift */; };
 		303492A5257546B400A523D0 /* DraftPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303492A4257546B400A523D0 /* DraftPreview.swift */; };
 		304219D3243F588500516852 /* DcCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 304219D1243F588500516852 /* DcCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		304219D92440734A00516852 /* DcMsg+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304219D82440734A00516852 /* DcMsg+Extension.swift */; };
@@ -218,6 +219,7 @@
 		302E1BB3252B5AB4008F4264 /* PlayButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PlayButtonView.swift; path = "deltachat-ios/Chat/Views/PlayButtonView.swift"; sourceTree = SOURCE_ROOT; };
 		30349290256441E200A523D0 /* QuotePreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotePreview.swift; sourceTree = "<group>"; };
 		303492942565AABC00A523D0 /* DraftModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftModel.swift; sourceTree = "<group>"; };
+		3034929E25752FC800A523D0 /* MediaPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPreview.swift; sourceTree = "<group>"; };
 		303492A4257546B400A523D0 /* DraftPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftPreview.swift; sourceTree = "<group>"; };
 		304219D1243F588500516852 /* DcCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DcCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		304219D82440734A00516852 /* DcMsg+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DcMsg+Extension.swift"; sourceTree = "<group>"; };
@@ -548,6 +550,7 @@
 				3052C609253F082E007D13EA /* MessageLabelDelegate.swift */,
 				3052C60D253F088E007D13EA /* DetectorType.swift */,
 				30653080254358B10093E196 /* QuoteView.swift */,
+				3034929E25752FC800A523D0 /* MediaPreview.swift */,
 				30349290256441E200A523D0 /* QuotePreview.swift */,
 				303492A4257546B400A523D0 /* DraftPreview.swift */,
 			);
@@ -1219,6 +1222,7 @@
 				3008CB7624F95B6D00E6A617 /* AudioController.swift in Sources */,
 				302B84CE2397F6CD001C261F /* URL+Extension.swift in Sources */,
 				7A9FB1441FB061E2001FEA36 /* AppDelegate.swift in Sources */,
+				3034929F25752FC800A523D0 /* MediaPreview.swift in Sources */,
 				AE76E5EE242BF2EA003CF461 /* WelcomeViewController.swift in Sources */,
 				3052C60A253F082E007D13EA /* MessageLabelDelegate.swift in Sources */,
 				AE0AA9562478191900D42A7F /* GridCollectionViewFlowLayout.swift in Sources */,

+ 74 - 12
deltachat-ios/Chat/ChatViewController.swift

@@ -37,6 +37,13 @@ class ChatViewController: UITableViewController {
         return view
     }()
 
+    lazy var mediaPreview: MediaPreview = {
+        let view = MediaPreview()
+        view.delegate = self
+        view.translatesAutoresizingMaskIntoConstraints = false
+        return view
+    }()
+
     open override var shouldAutorotate: Bool {
         return false
     }
@@ -395,8 +402,9 @@ class ChatViewController: UITableViewController {
 
     private func configureDraftArea(draft: DraftModel) {
         quotePreview.configure(draft: draft)
+        mediaPreview.configure(draft: draft)
         // setStackViewItems recalculates the proper messageInputBar height
-        messageInputBar.setStackViewItems([quotePreview], forStack: .top, animated: true)
+        messageInputBar.setStackViewItems([quotePreview, mediaPreview], forStack: .top, animated: true)
     }
 
     override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
@@ -901,15 +909,51 @@ class ChatViewController: UITableViewController {
         }
     }
 
+    private func stageImage(url: NSURL) {
+        if url.pathExtension == "gif" {
+            stageAnimatedImage(url: url)
+        } else if let data = try? Data(contentsOf: url as URL),
+                  let image = UIImage(data: data) {
+            stageImage(image)
+        }
+    }
+
+    private func stageAnimatedImage(url: NSURL) {
+        DispatchQueue.global().async {
+            if let path = url.path,
+               let result = SDAnimatedImage(contentsOfFile: path),
+               let animatedImageData = result.animatedImageData,
+               let pathInDocDir = DcUtils.saveImage(data: animatedImageData, suffix: "gif") {
+                DispatchQueue.main.async {
+                    self.draft.setAttachment(viewType: DC_MSG_GIF, path: URL(fileURLWithPath: pathInDocDir))
+                    self.configureDraftArea(draft: self.draft)
+                    self.messageInputBar.inputTextView.becomeFirstResponder()
+                }
+            }
+        }
+    }
+
+    private func stageImage(_ image: UIImage) {
+        DispatchQueue.global().async {
+            if let path = DcUtils.saveImage(image: image) {
+                DispatchQueue.main.async {
+                    self.draft.setAttachment(viewType: DC_MSG_IMAGE, path: URL(fileURLWithPath: path), mimetype: nil)
+                    self.configureDraftArea(draft: self.draft)
+                    self.messageInputBar.inputTextView.becomeFirstResponder()
+                }
+            }
+        }
+    }
+
     private func sendImage(_ image: UIImage, message: String? = nil) {
         DispatchQueue.global().async {
             if let path = DcUtils.saveImage(image: image) {
-                self.sendImageMessage(viewType: DC_MSG_IMAGE, image: image, filePath: path)
+                self.sendImageMessage(viewType: DC_MSG_IMAGE, filePath: path)
             }
         }
     }
 
-    private func sendAnimatedImage(url: NSURL) {
+    /*private func sendAnimatedImage(url: NSURL) {
         if let path = url.path {
             let result = SDAnimatedImage(contentsOfFile: path)
             if let result = result,
@@ -918,12 +962,15 @@ class ChatViewController: UITableViewController {
                 self.sendImageMessage(viewType: DC_MSG_GIF, image: result, filePath: pathInDocDir)
             }
         }
-    }
+    }*/
 
-    private func sendImageMessage(viewType: Int32, image: UIImage, filePath: String, message: String? = nil) {
+    private func sendImageMessage(viewType: Int32, filePath: String, message: String? = nil, quoteMessage: DcMsg? = nil) {
         let msg = DcMsg(viewType: viewType)
         msg.setFile(filepath: filePath)
         msg.text = (message ?? "").isEmpty ? nil : message
+        if quoteMessage != nil {
+            msg.quoteMessage = quoteMessage
+        }
         msg.sendInChat(id: self.chatId)
     }
 
@@ -951,14 +998,14 @@ class ChatViewController: UITableViewController {
         }
     }
 
-    private func sendImage(url: NSURL) {
+    /*private func sendImage(url: NSURL) {
         if url.pathExtension == "gif" {
             sendAnimatedImage(url: url)
         } else if let data = try? Data(contentsOf: url as URL),
             let image = UIImage(data: data) {
             sendImage(image)
         }
-    }
+    }*/
 
     // MARK: - Context menu
     private func prepareContextMenu() {
@@ -1136,11 +1183,11 @@ extension ChatViewController: MediaPickerDelegate {
     }
 
     func onImageSelected(url: NSURL) {
-        sendImage(url: url)
+        stageImage(url: url)
     }
 
     func onImageSelected(image: UIImage) {
-        sendImage(image)
+        stageImage(image)
     }
 
     func onVoiceMessageRecorded(url: NSURL) {
@@ -1158,15 +1205,25 @@ extension ChatViewController: InputBarAccessoryViewDelegate {
     func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) {
         let trimmedText = text.replacingOccurrences(of: "\u{FFFC}", with: "", options: .literal, range: nil)
             .trimmingCharacters(in: .whitespacesAndNewlines)
-        if inputBar.inputTextView.images.isEmpty {
+        if let filePath = draft.draftAttachment, let viewType = draft.draftViewType {
+            switch viewType {
+            case DC_MSG_GIF:
+                self.sendImageMessage(viewType: DC_MSG_GIF, filePath: filePath.absoluteString, message: trimmedText, quoteMessage: draft.quoteMessage)
+            case DC_MSG_IMAGE:
+                self.sendImageMessage(viewType: DC_MSG_IMAGE, filePath: filePath.absoluteString, message: trimmedText, quoteMessage: draft.quoteMessage)
+            default:
+                logger.debug("nothing to do")
+            }
+        } else if inputBar.inputTextView.images.isEmpty {
             self.sendTextMessage(text: trimmedText, quoteMessage: draft.quoteMessage)
-            self.quotePreview.cancel()
         } else {
             // only 1 attachment allowed for now, thus it takes the first one
             self.sendImage(inputBar.inputTextView.images[0], message: trimmedText)
         }
         inputBar.inputTextView.text = String()
         inputBar.inputTextView.attributedText = nil
+        self.quotePreview.cancel()
+        self.mediaPreview.cancel()
     }
 
     func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String) {
@@ -1174,9 +1231,14 @@ extension ChatViewController: InputBarAccessoryViewDelegate {
     }
 }
 
-extension ChatViewController: QuotePreviewDelegate {
+extension ChatViewController: QuotePreviewDelegate, MediaPreviewDelegate {
     func onCancelQuote() {
         draft.setQuote(quotedMsg: nil)
         configureDraftArea(draft: draft)
     }
+
+    func onCancelAttachment() {
+        draft.setAttachment(viewType: nil, path: nil, mimetype: nil)
+        configureDraftArea(draft: draft)
+    }
 }

+ 18 - 1
deltachat-ios/Chat/DraftModel.swift

@@ -6,6 +6,9 @@ public class DraftModel {
     var quoteMessage: DcMsg?
     var quoteText: String?
     var draftText: String?
+    var draftAttachment: URL?
+    var draftAttachmentMimeType: String?
+    var draftViewType: Int32?
     let chatId: Int
 
     public init(chatId: Int) {
@@ -16,6 +19,11 @@ public class DraftModel {
         draftText = draftMsg?.text
         quoteText = draftMsg?.quoteText
         quoteMessage = draftMsg?.quoteMessage
+        draftAttachment = draftMsg?.fileURL
+        if let viewType = draftMsg?.type {
+            draftViewType = Int32(viewType)
+        }
+        draftAttachmentMimeType = draftMsg?.filemime
     }
 
     public func setQuote(quotedMsg: DcMsg?) {
@@ -31,17 +39,26 @@ public class DraftModel {
         }
     }
 
+    public func setAttachment(viewType: Int32?, path: URL?, mimetype: String? = nil) {
+        draftAttachment = path
+        draftViewType = viewType
+        draftAttachmentMimeType = mimetype
+    }
+
     public func save(context: DcContext) {
         if draftText == nil && quoteMessage == nil {
             context.setDraft(chatId: chatId, message: nil)
             return
         }
 
-        let draftMessage = DcMsg(viewType: DC_MSG_TEXT)
+        let draftMessage = DcMsg(viewType: draftViewType ?? DC_MSG_TEXT)
         draftMessage.text = draftText
         if quoteMessage != nil {
             draftMessage.quoteMessage = quoteMessage
         }
+        if draftAttachment != nil {
+            draftMessage.setFile(filepath: draftAttachment?.absoluteString, mimeType: draftAttachmentMimeType)
+        }
         context.setDraft(chatId: chatId, message: draftMessage)
     }
 }

+ 44 - 20
deltachat-ios/Chat/Views/MediaPreview.swift

@@ -1,38 +1,62 @@
 import UIKit
 import SDWebImage
 
-class MediaPreview: UIView {
+public protocol MediaPreviewDelegate: class {
+    func onCancelAttachment()
+}
+class MediaPreview: DraftPreview {
+    var imageWidthConstraint: NSLayoutConstraint?
+    weak var delegate: MediaPreviewDelegate?
 
     lazy var contentImageView: SDAnimatedImageView = {
         let imageView = SDAnimatedImageView()
         imageView.translatesAutoresizingMaskIntoConstraints = false
-        imageView.setContentHuggingPriority(.defaultHigh, for: .vertical)
-        imageView.isUserInteractionEnabled = true
-        imageView.contentMode = .scaleAspectFill
+        imageView.contentMode = .scaleAspectFit
         imageView.clipsToBounds = true
         return imageView
     }()
 
-    /// The play button view to display on video messages.
-    open lazy var playButtonView: PlayButtonView = {
-        let playButtonView = PlayButtonView()
-        playButtonView.isHidden = true
-        translatesAutoresizingMaskIntoConstraints = false
-        return playButtonView
-    }()
-
-    
-
-    init() {
-        super.init(frame: .zero)
-        setupSubviews()
+    override func setupSubviews() {
+        super.setupSubviews()
+        mainContentView.addSubview(contentImageView)
+        addConstraints([
+            contentImageView.constraintAlignTopTo(mainContentView),
+            contentImageView.constraintAlignLeadingMaxTo(mainContentView, paddingLeading: 12),
+            contentImageView.constraintAlignTrailingTo(mainContentView, paddingTrailing: 14),
+            contentImageView.constraintAlignBottomTo(mainContentView),
+            contentImageView.constraintHeightTo(90)
+        ])
     }
 
-    required init?(coder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
+    override func configure(draft: DraftModel) {
+        if let path = draft.draftAttachment {
+            contentImageView.sd_setImage(with: path, completed: { image, error, _, _ in
+                if let error = error {
+                    logger.error("could not load draft image: \(error)")
+                    self.cancel()
+                }
+                if let image = image {
+                    self.setAspectRatio(image: image)
+                }
+            })
+
+            isHidden = false
+        } else {
+            isHidden = true
+        }
     }
 
-    func setupSubviews() {
+    override public func cancel() {
+        contentImageView.sd_cancelCurrentImageLoad()
+        contentImageView.image = nil
+        delegate?.onCancelAttachment()
+    }
 
+    func setAspectRatio(image: UIImage) {
+        let height = image.size.height
+        let width = image.size.width
+        imageWidthConstraint?.isActive = false
+        imageWidthConstraint = contentImageView.widthAnchor.constraint(lessThanOrEqualTo: contentImageView.heightAnchor, multiplier: width / height)
+        imageWidthConstraint?.isActive = true
     }
 }