Browse Source

Merge pull request #1297 from deltachat/mailto

become default iOS email app
cyBerta 4 years ago
parent
commit
84b27ca2df

+ 6 - 2
DcCore/DcCore/DC/Wrapper.swift

@@ -127,7 +127,7 @@ public class DcContext {
     }
 
     public func sendVideoChatInvitation(chatId: Int) -> Int {
-        return Int(dc_send_videochat_invitation(contextPointer,  UInt32(chatId)))
+        return Int(dc_send_videochat_invitation(contextPointer, UInt32(chatId)))
     }
 
     // TODO: remove count and from parameters if we don't use it
@@ -150,7 +150,7 @@ public class DcContext {
         return ids
     }
 
-    public func createContact(name: String, email: String) -> Int {
+    public func createContact(name: String?, email: String) -> Int {
         return Int(dc_create_contact(contextPointer, name, email))
     }
 
@@ -188,6 +188,10 @@ public class DcContext {
         dc_add_address_book(contextPointer, contactString)
     }
 
+    public func lookupContactIdByAddress(_ address: String) -> Int {
+        return Int(dc_lookup_contact_id_by_addr(contextPointer, addr))
+    }
+
     public func getChat(chatId: Int) -> DcChat {
         let chatPointer = dc_get_chat(contextPointer, UInt32(chatId))
         return DcChat(chatPointer: chatPointer)

+ 17 - 10
deltachat-ios/AppDelegate.swift

@@ -126,17 +126,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         logger.info("➡️ open url")
 
         // gets here when app returns from oAuth2-Setup process - the url contains the provided token
-        if let params = url.queryParameters, let token = params["code"] {
-            NotificationCenter.default.post(name: NSNotification.Name("oauthLoginApproved"), object: nil, userInfo: ["token": token])
+        // if let params = url.queryParameters, let token = params["code"] {
+        //    NotificationCenter.default.post(name: NSNotification.Name("oauthLoginApproved"), object: nil, userInfo: ["token": token])
+        // }
+
+        switch url.scheme?.lowercased() {
+        case "openpgp4fpr":
+            // Hack to format url properly
+            let urlString = url.absoluteString
+                           .replacingOccurrences(of: "openpgp4fpr", with: "OPENPGP4FPR", options: .literal, range: nil)
+                           .replacingOccurrences(of: "%23", with: "#", options: .literal, range: nil)
+
+            self.appCoordinator.handleQRCode(urlString)
+            return true
+        case "mailto":
+            return self.appCoordinator.handleMailtoURL(url)
+        default:
+            return false
         }
-
-        // Hack to format url properly
-        let urlString = url.absoluteString
-                       .replacingOccurrences(of: "openpgp4fpr", with: "OPENPGP4FPR", options: .literal, range: nil)
-                       .replacingOccurrences(of: "%23", with: "#", options: .literal, range: nil)
-
-        self.appCoordinator.handleQRCode(urlString)
-        return true
     }
 
 

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

@@ -354,6 +354,9 @@ class ChatViewController: UITableViewController {
 
         if RelayHelper.sharedInstance.isForwarding() {
             askToForwardMessage()
+        } else if RelayHelper.sharedInstance.isMailtoHandling() {
+            messageInputBar.inputTextView.text = RelayHelper.sharedInstance.mailtoDraft
+            RelayHelper.sharedInstance.finishMailto()
         }
 
         prepareContextMenu()

+ 43 - 16
deltachat-ios/Controller/ChatListController.swift

@@ -393,6 +393,27 @@ class ChatListController: UITableViewController {
         timer = nil
     }
 
+    public func handleMailto() {
+        if let mailtoAddress = RelayHelper.sharedInstance.mailtoAddress {
+            // FIXME: the line below should work
+            // var contactId = dcContext.lookupContactIdByAddress(mailtoAddress)
+
+            // workaround:
+            let contacts: [Int] = dcContext.getContacts(flags: DC_GCL_ADD_SELF, queryString: mailtoAddress)
+            let index = contacts.firstIndex(where: { dcContext.getContact(id: $0).email == mailtoAddress }) ?? -1
+            var contactId = 0
+            if index >= 0 {
+                contactId = contacts[index]
+            }
+
+            if contactId != 0 && dcContext.getChatIdByContactId(contactId: contactId) != 0 {
+                showChat(chatId: dcContext.getChatIdByContactId(contactId: contactId), animated: false)
+            } else {
+                askToChatWith(address: mailtoAddress)
+            }
+        }
+    }
+
     // MARK: - alerts
     private func showDeleteChatConfirmationAlert(chatId: Int) {
         let alert = UIAlertController(
@@ -429,24 +450,30 @@ class ChatListController: UITableViewController {
         present(alert, animated: true, completion: nil)
     }
 
-    private func askToChatWith(contactId: Int) {
-        let dcContact = dcContext.getContact(id: contactId)
-        let alert = UIAlertController(
-            title: String.localizedStringWithFormat(String.localized("ask_start_chat_with"), dcContact.nameNAddr),
-            message: nil,
-            preferredStyle: .safeActionSheet)
-        alert.addAction(UIAlertAction(
-            title: String.localized("start_chat"),
-            style: .default,
-            handler: { _ in
-                self.showNewChat(contactId: contactId)
+    private func askToChatWith(address: String, contactId: Int = 0) {
+        var contactId = contactId
+        let alert = UIAlertController(title: String.localizedStringWithFormat(String.localized("ask_start_chat_with"), address),
+                                      message: nil,
+                                      preferredStyle: .safeActionSheet)
+        alert.addAction(UIAlertAction(title: String.localized("start_chat"), style: .default, handler: { [weak self] _ in
+            guard let self = self else { return }
+            if contactId == 0 {
+                contactId = self.dcContext.createContact(name: nil, email: address)
+            }
+            self.showNewChat(contactId: contactId)
         }))
-        alert.addAction(UIAlertAction(
-            title: String.localized("cancel"),
-            style: .cancel,
-            handler: { _ in
+        alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: { _ in
+            if RelayHelper.sharedInstance.isMailtoHandling() {
+                RelayHelper.sharedInstance.finishMailto()
+            }
         }))
-        self.present(alert, animated: true, completion: nil)
+        present(alert, animated: true, completion: nil)
+    }
+
+
+    private func askToChatWith(contactId: Int) {
+        let dcContact = dcContext.getContact(id: contactId)
+        askToChatWith(address: dcContact.nameNAddr, contactId: contactId)
     }
 
     private func deleteChat(chatId: Int, animated: Bool) {

+ 1 - 5
deltachat-ios/Controller/NewChatViewController.swift

@@ -374,12 +374,9 @@ extension NewChatViewController {
             preferredStyle: .safeActionSheet
         )
         alert.addAction(UIAlertAction(title: String.localized("delete"), style: .destructive, handler: { [weak self] _ in
-            self?.dismiss(animated: true, completion: nil)
             self?.deleteContact(contactId: contactId, indexPath: indexPath)
         }))
-        alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: { _ in
-            self.dismiss(animated: true, completion: nil)
-        }))
+        alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
         present(alert, animated: true, completion: nil)
     }
 
@@ -393,7 +390,6 @@ extension NewChatViewController {
                                           message: nil,
                                           preferredStyle: .safeActionSheet)
             alert.addAction(UIAlertAction(title: String.localized("start_chat"), style: .default, handler: { _ in
-                self.dismiss(animated: true, completion: nil)
                 self.showNewChat(contactId: contactId)
             }))
             alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: { _ in

+ 17 - 0
deltachat-ios/Coordinator/AppCoordinator.swift

@@ -97,6 +97,23 @@ class AppCoordinator {
         }
     }
 
+    func handleMailtoURL(_ url: URL) -> Bool {
+        if RelayHelper.sharedInstance.parseMailtoUrl(url) {
+            showTab(index: chatsTab)
+            if let rootController = self.tabBarController.selectedViewController as? UINavigationController {
+                rootController.popToRootViewController(animated: false)
+                if let controller = rootController.viewControllers.first as? ChatListController {
+                    controller.handleMailto()
+                    return true
+                }
+            }
+        } else {
+            logger.warning("Could not parse mailto: URL")
+        }
+        RelayHelper.sharedInstance.finishMailto()
+        return false
+    }
+    
     func handleQRCode(_ code: String) {
         showTab(index: qrTab)
         if let navController = self.tabBarController.selectedViewController as? UINavigationController,

+ 56 - 0
deltachat-ios/Helper/RelayHelper.swift

@@ -6,6 +6,9 @@ class RelayHelper {
     private static var dcContext: DcContext?
     var messageIds: [Int]?
 
+    var mailtoDraft: String = ""
+    var mailtoAddress: String?
+
     private init() {
         guard RelayHelper.dcContext != nil else {
             fatalError("Error - you must call RelayHelper.setup() before accessing RelayHelper.shared")
@@ -39,4 +42,57 @@ class RelayHelper {
     func cancel() {
         messageIds = nil
     }
+
+    func isMailtoHandling() -> Bool {
+        return !mailtoDraft.isEmpty || mailtoAddress != nil
+    }
+
+    func finishMailto() {
+        mailtoDraft = ""
+        mailtoAddress = nil
+    }
+
+
+    func splitString(_ value: String) -> [String] {
+        return value.split(separator: ",").map(String.init)
+    }
+
+    /**
+            returns true if parsing was successful
+     */
+    func parseMailtoUrl(_ url: URL) -> Bool {
+        if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
+            var subject: String = ""
+            var body: String = ""
+            let queryItems = urlComponents.queryItems ?? []
+            for queryItem in queryItems {
+                guard let value = queryItem.value else {
+                    continue
+                }
+                switch queryItem.name {
+                case "body":
+                    body = value
+                case "subject":
+                    subject = value
+                default:
+                    break
+                }
+            }
+
+            if !subject.isEmpty {
+                mailtoDraft = subject
+                if !body.isEmpty {
+                    mailtoDraft += "\n\n\(body)"
+                }
+            } else if !body.isEmpty {
+                mailtoDraft = body
+            }
+
+            if !urlComponents.path.isEmpty {
+                mailtoAddress = splitString(urlComponents.path)[0] // we currently only allow 1 receipient
+            }
+            return mailtoAddress != nil || !mailtoDraft.isEmpty
+        }
+        return false
+    }
 }

+ 10 - 0
deltachat-ios/Info.plist

@@ -31,6 +31,16 @@
 				<string>openpgp4fpr</string>
 			</array>
 		</dict>
+		<dict>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+			<key>CFBundleURLName</key>
+			<string>chat.delta.mailto</string>
+			<key>CFBundleURLSchemes</key>
+			<array>
+				<string>mailto</string>
+			</array>
+		</dict>
 	</array>
 	<key>CFBundleVersion</key>
 	<string>$(CURRENT_PROJECT_VERSION)</string>