浏览代码

show a quote preview on swipe-to-reply

cyberta 4 年之前
父节点
当前提交
9f9c1a78e0

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

@@ -19,6 +19,7 @@
 		302B84C72396770B001C261F /* RelayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B84C42396627F001C261F /* RelayHelper.swift */; };
 		302B84CE2397F6CD001C261F /* URL+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302B84CD2397F6CD001C261F /* URL+Extension.swift */; };
 		302E1BB4252B5AB4008F4264 /* PlayButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302E1BB3252B5AB4008F4264 /* PlayButtonView.swift */; };
+		30349291256441E200A523D0 /* QuotePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30349290256441E200A523D0 /* QuotePreview.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 */; };
 		304F5E44244F571C00462538 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A9FB14A1FB061E2001FEA36 /* Assets.xcassets */; };
@@ -213,6 +214,7 @@
 		302B84C42396627F001C261F /* RelayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayHelper.swift; sourceTree = "<group>"; };
 		302B84CD2397F6CD001C261F /* URL+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extension.swift"; sourceTree = "<group>"; };
 		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>"; };
 		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>"; };
 		3052C609253F082E007D13EA /* MessageLabelDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageLabelDelegate.swift; sourceTree = "<group>"; };
@@ -775,6 +777,7 @@
 				AEFBE22E23FEF23D0045327A /* ProviderInfoCell.swift */,
 				AED62BCD247687E6009E220D /* LocationStreamingIndicator.swift */,
 				30F4BFED252E3E020006B9B3 /* PaddingTextView.swift */,
+				30349290256441E200A523D0 /* QuotePreview.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -1218,6 +1221,7 @@
 				305961CF2346125100C80F33 /* UIColor+Extensions.swift in Sources */,
 				AEACE2E51FB32E1900DCDD78 /* Utils.swift in Sources */,
 				3052C60E253F088E007D13EA /* DetectorType.swift in Sources */,
+				30349291256441E200A523D0 /* QuotePreview.swift in Sources */,
 				AEC67A1E241FCFE0007DDBE1 /* ChatListViewModel.swift in Sources */,
 				30FDB71F24D8170E0066C48D /* TextMessageCell.swift in Sources */,
 				AE1988A523EB2FBA00B4CD5F /* Errors.swift in Sources */,

+ 24 - 0
deltachat-ios/Chat/ChatViewController.swift

@@ -26,6 +26,13 @@ class ChatViewController: UITableViewController {
     /// The `InputBarAccessoryView` used as the `inputAccessoryView` in the view controller.
     open var messageInputBar = InputBarAccessoryView()
 
+    lazy var quotePreview: QuotePreview = {
+        let view = QuotePreview()
+        view.delegate = self
+        view.translatesAutoresizingMaskIntoConstraints = false
+        return view
+    }()
+
     open override var shouldAutorotate: Bool {
         return false
     }
@@ -403,6 +410,14 @@ class ChatViewController: UITableViewController {
         let action = UIContextualAction(style: .normal, title: nil,
                                         handler: { (action, view, completionHandler) in
                                             // Update data source when user taps action
+                                            let message = DcMsg(id: self.messageIds[indexPath.row])
+                                            let contact = message.fromContact
+                                            self.messageInputBar.setStackViewItems([self.quotePreview], forStack: .top, animated: false)
+                                            self.quotePreview.text = message.text
+                                            self.quotePreview.senderTitle.text = contact.displayName
+                                            self.quotePreview.senderTitle.textColor = contact.color
+                                            self.quotePreview.citeBar.backgroundColor = contact.color
+                                            self.quotePreview.imagePreview.image = message.image
                                             completionHandler(true)
                                         })
         if #available(iOS 12.0, *) {
@@ -1196,3 +1211,12 @@ extension ChatViewController: InputBarAccessoryViewDelegate {
         inputBar.inputTextView.attributedText = nil
     }
 }
+
+extension ChatViewController: QuotePreviewDelegate {
+    func onCancel() {
+        // instead of hiding quote preview we need to remove it from the top view stack
+        // setStackViewItems ensures the size of the messagInputBarHeight is
+        // calculated correctly
+        messageInputBar.setStackViewItems([], forStack: .top, animated: false)
+    }
+}

+ 97 - 0
deltachat-ios/View/QuotePreview.swift

@@ -0,0 +1,97 @@
+import UIKit
+import InputBarAccessoryView
+import DcCore
+
+public protocol QuotePreviewDelegate: class {
+    func onCancel()
+}
+
+public class QuotePreview: UIView, InputItem {
+    
+    public var inputBarAccessoryView: InputBarAccessoryView?
+    public var parentStackViewPosition: InputStackView.Position?
+    public func textViewDidChangeAction(with textView: InputTextView) {}
+    public func keyboardSwipeGestureAction(with gesture: UISwipeGestureRecognizer) {}
+    public func keyboardEditingEndsAction() {}
+    public func keyboardEditingBeginsAction() {}
+
+    public weak var delegate: QuotePreviewDelegate?
+
+    lazy var quoteView: QuoteView = {
+        let view = QuoteView()
+        view.translatesAutoresizingMaskIntoConstraints = false
+
+        return view
+    }()
+
+    lazy var cancelButton: UIImageView = {
+        let view = UIImageView(image: UIImage(named: "ic_close_36pt"))
+        view.tintColor = .darkGray
+        view.translatesAutoresizingMaskIntoConstraints = false
+        view.isUserInteractionEnabled = true
+        return view
+    }()
+
+    lazy var upperBorder: UIView = {
+        let view = UIView()
+        view.backgroundColor = UIColor.lightGray
+        view.translatesAutoresizingMaskIntoConstraints = false
+        return view
+    }()
+
+    public var text: String? {
+        set { quoteView.quote.text = newValue }
+        get { return quoteView.quote.text }
+    }
+
+    public var senderTitle: UILabel {
+        set { quoteView.senderTitle = newValue }
+        get { return quoteView.senderTitle }
+    }
+
+    public var citeBar: UIView {
+        set { quoteView.citeBar = newValue }
+        get { return quoteView.citeBar }
+    }
+
+    public var imagePreview: UIImageView {
+        set { quoteView.imagePreview = newValue }
+        get { return quoteView.imagePreview }
+    }
+
+    init() {
+        super.init(frame: .zero)
+        setupSubviews()
+    }
+
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    func setupSubviews() {
+        addSubview(upperBorder)
+        addSubview(quoteView)
+        addSubview(cancelButton)
+        addConstraints([
+            upperBorder.constraintAlignLeadingTo(self),
+            upperBorder.constraintAlignTrailingTo(self),
+            upperBorder.constraintHeightTo(1),
+            upperBorder.constraintAlignTopTo(self, paddingTop: 4),
+            quoteView.constraintAlignTopTo(upperBorder, paddingTop: 4),
+            quoteView.constraintAlignLeadingTo(self),
+            quoteView.constraintAlignBottomTo(self, paddingBottom: 4),
+            quoteView.constraintTrailingToLeadingOf(cancelButton),
+            cancelButton.constraintAlignTrailingTo(self, paddingTrailing: 8),
+            cancelButton.constraintWidthTo(30),
+            cancelButton.constraintHeightTo(30),
+            cancelButton.constraintCenterYTo(self),
+        ])
+        let recognizer = UITapGestureRecognizer(target: self, action: #selector(onCancelPressed))
+        cancelButton.addGestureRecognizer(recognizer)
+    }
+
+    @objc func onCancelPressed() {
+        quoteView.prepareForReuse()
+        delegate?.onCancel()
+    }
+}