diff --git a/DcCore/DcCore/DC/events.swift b/DcCore/DcCore/DC/events.swift index 1e413c75d..9294c5828 100644 --- a/DcCore/DcCore/DC/events.swift +++ b/DcCore/DcCore/DC/events.swift @@ -8,6 +8,7 @@ public enum Event { public static let incomingMessage = Notification.Name(rawValue: "incomingMessage") public static let incomingMessageOnAnyAccount = Notification.Name(rawValue: "incomingMessageOnAnyAccount") public static let incomingReaction = Notification.Name(rawValue: "incomingReaction") + public static let incomingWebxdcNotify = Notification.Name(rawValue: "incomingWebxdcNotify") public static let messagesNoticed = Notification.Name(rawValue: "messagesNoticed") // Chats @@ -155,6 +156,15 @@ public class DcEventHandler { "reaction": event.data2String ]) + case DC_EVENT_INCOMING_WEBXDC_NOTIFY: + logger.info("📡[\(accountId)] incoming webxdc notify") + NotificationCenter.default.post(name: Event.incomingWebxdcNotify, object: nil, userInfo: [ + "account_id": Int(accountId), + "contact_id": Int(data1), + "msg_id": Int(data2), + "text": event.data2String + ]) + case DC_EVENT_CONTACTS_CHANGED: if accountId != dcAccounts.getSelected().id { return diff --git a/DcNotificationService/NotificationService.swift b/DcNotificationService/NotificationService.swift index 7c7ab057e..4a0d9fb8d 100644 --- a/DcNotificationService/NotificationService.swift +++ b/DcNotificationService/NotificationService.swift @@ -74,6 +74,22 @@ class NotificationService: UNNotificationServiceExtension { reactionCount += 1 } } + } else if event.id == DC_EVENT_INCOMING_WEBXDC_NOTIFY { + let dcContext = dcAccounts.get(id: event.accountId) + if !dcContext.isMuted() { + let msg = dcContext.getMessage(id: event.data2Int) + let chat = dcContext.getChat(chatId: msg.chatId) + if !chat.isMuted { + bestAttemptContent.title = chat.name + bestAttemptContent.body = msg.getWebxdcAppName() + ": " + event.data2String + bestAttemptContent.userInfo["account_id"] = dcContext.id + bestAttemptContent.userInfo["chat_id"] = chat.id + bestAttemptContent.userInfo["message_id"] = msg.id + + uniqueChats["\(dcContext.id)-\(chat.id)"] = bestAttemptContent.title + messageCount += 1 + } + } } } diff --git a/deltachat-ios/Chat/ChatViewController.swift b/deltachat-ios/Chat/ChatViewController.swift index b71953082..4e7f95aea 100644 --- a/deltachat-ios/Chat/ChatViewController.swift +++ b/deltachat-ios/Chat/ChatViewController.swift @@ -833,7 +833,7 @@ class ChatViewController: UITableViewController, UITableViewDropDelegate { showWebxdcViewFor(message: message) case (_, DC_INFO_WEBXDC_INFO_MESSAGE): if let parent = message.parent { - scrollToMessage(msgId: parent.id) + showWebxdcViewFor(message: parent, href: message.getWebxdcHref()) } case (_, DC_INFO_PROTECTION_ENABLED): showProtectionEnabledDialog() @@ -2106,8 +2106,8 @@ extension ChatViewController: MCEmojiPickerDelegate { extension ChatViewController { - func showWebxdcViewFor(message: DcMsg) { - let webxdcViewController = WebxdcViewController(dcContext: dcContext, messageId: message.id) + func showWebxdcViewFor(message: DcMsg, href: String? = nil) { + let webxdcViewController = WebxdcViewController(dcContext: dcContext, messageId: message.id, href: href) navigationController?.pushViewController(webxdcViewController, animated: true) } diff --git a/deltachat-ios/Controller/WebxdcViewController.swift b/deltachat-ios/Controller/WebxdcViewController.swift index a9397b7a3..0c4e002ac 100644 --- a/deltachat-ios/Controller/WebxdcViewController.swift +++ b/deltachat-ios/Controller/WebxdcViewController.swift @@ -15,8 +15,12 @@ class WebxdcViewController: WebViewViewController { let INTERNALSCHEMA = "webxdc" var messageId: Int + var href: String? var webxdcName: String = "" var sourceCodeUrl: String? + var selfAddr: String = "" + var sendUpdateInterval: Int = 0 + var sendUpdateMaxSize: Int = 0 private var allowInternet: Bool = false private var shortcutManager: ShortcutManager? @@ -57,7 +61,7 @@ class WebxdcViewController: WebViewViewController { """ lazy var webxdcbridge: String = { - let addr = dcContext.addr? + let addr = selfAddr .addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) let displayname = (dcContext.displayname ?? dcContext.addr)? .addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) @@ -127,7 +131,11 @@ class WebxdcViewController: WebViewViewController { selfAddr: decodeURI("\((addr ?? "unknown"))"), selfName: decodeURI("\((displayname ?? "unknown"))"), - + + sendUpdateInterval: \(sendUpdateInterval), + + sendUpdateMaxSize: \(sendUpdateMaxSize), + joinRealtimeChannel: () => { realtimeChannel = createRealtimeChannel(); webkit.messageHandlers.sendRealtimeAdvertisement.postMessage(""); @@ -267,8 +275,9 @@ class WebxdcViewController: WebViewViewController { } - init(dcContext: DcContext, messageId: Int) { + init(dcContext: DcContext, messageId: Int, href: String? = nil) { self.messageId = messageId + self.href = href self.shortcutManager = ShortcutManager(dcContext: dcContext, messageId: messageId) super.init(dcContext: dcContext) @@ -279,6 +288,7 @@ class WebxdcViewController: WebViewViewController { object: nil) NotificationCenter.default.addObserver(self, selector: #selector(WebxdcViewController.handleWebxdcStatusUpdate(_:)), name: Event.webxdcStatusUpdate, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(WebxdcViewController.handleWebxdcRealtimeDataReceived(_:)), name: Event.webxdcRealtimeDataReceived, object: nil) + refreshWebxdcInfo() } required init?(coder: NSCoder) { @@ -293,7 +303,6 @@ class WebxdcViewController: WebViewViewController { override func viewDidLoad() { super.viewDidLoad() navigationItem.rightBarButtonItem = moreButton - refreshWebxdcInfo() } override func willMove(toParent parent: UIViewController?) { @@ -308,6 +317,9 @@ class WebxdcViewController: WebViewViewController { let document = dict["document"] as? String ?? "" webxdcName = dict["name"] as? String ?? "ErrName" // name should not be empty + selfAddr = dict["self_addr"] as? String ?? "ErrAddr" + sendUpdateInterval = dict["send_update_interval"] as? Int ?? 0 + sendUpdateMaxSize = dict["send_update_max_size"] as? Int ?? 0 let chatName = dcContext.getChat(chatId: msg.chatId).name self.allowInternet = dict["internet_access"] as? Bool ?? false @@ -412,8 +424,10 @@ class WebxdcViewController: WebViewViewController { private func loadHtml() { DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self else { return } - let url = URL(string: "\(self.INTERNALSCHEMA)://acc\(self.dcContext.id)-msg\(self.messageId).localhost/index.html") - let urlRequest = URLRequest(url: url!) + + let base = "\(self.INTERNALSCHEMA)://acc\(self.dcContext.id)-msg\(self.messageId).localhost/" + let url = URL(string: base + (href ?? "index.html")) + let urlRequest = URLRequest(url: url ?? URL(string: base + "index.html")!) DispatchQueue.main.async { self.webView.load(urlRequest) } diff --git a/deltachat-ios/DC/DcMsg+Extension.swift b/deltachat-ios/DC/DcMsg+Extension.swift index 357402cc5..a45916bb8 100644 --- a/deltachat-ios/DC/DcMsg+Extension.swift +++ b/deltachat-ios/DC/DcMsg+Extension.swift @@ -12,15 +12,6 @@ extension DcMsg { return "\(size) \(units[digitGroups])" } - public func getWebxdcInfoDict() -> [String: AnyObject] { - let jsonString = self.getWebxdcInfoJson() - if let data: Data = jsonString.data(using: .utf8), - let infoDict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: AnyObject] { - return infoDict - } - return [:] - } - public func getWebxdcPreviewImage() -> UIImage? { let dict = self.getWebxdcInfoDict() if let iconfilePath = dict["icon"] as? String { diff --git a/deltachat-ios/DC/DcMsg.swift b/deltachat-ios/DC/DcMsg.swift index 190dea60d..11846ae48 100644 --- a/deltachat-ios/DC/DcMsg.swift +++ b/deltachat-ios/DC/DcMsg.swift @@ -168,6 +168,33 @@ public class DcMsg { return swiftString } + public func getWebxdcInfoDict() -> [String: AnyObject] { + let jsonString = self.getWebxdcInfoJson() + if let data: Data = jsonString.data(using: .utf8), + let infoDict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: AnyObject] { + return infoDict + } + return [:] + } + + // returns webxdc app name for an webxdc-info-messages or webxdc-instances + public func getWebxdcAppName() -> String { + let msg = if self.isInfo, let parent = self.parent { + parent + } else { + self + } + let dict = msg.getWebxdcInfoDict() + return dict["name"] as? String ?? "ErrName" + } + + public func getWebxdcHref() -> String? { + guard let cString = dc_msg_get_webxdc_href(messagePointer) else { return nil } + let swiftString = String(cString: cString) + dc_str_unref(cString) + return swiftString + } + public var messageHeight: CGFloat { return CGFloat(dc_msg_get_height(messagePointer)) } diff --git a/deltachat-ios/Helper/NotificationManager.swift b/deltachat-ios/Helper/NotificationManager.swift index 50b6893da..3e0f3bb92 100644 --- a/deltachat-ios/Helper/NotificationManager.swift +++ b/deltachat-ios/Helper/NotificationManager.swift @@ -14,6 +14,7 @@ public class NotificationManager { NotificationCenter.default.addObserver(self, selector: #selector(NotificationManager.handleIncomingMessageOnAnyAccount(_:)), name: Event.incomingMessageOnAnyAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(NotificationManager.handleIncomingReaction(_:)), name: Event.incomingReaction, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(NotificationManager.handleIncomingWebxdcNotify(_:)), name: Event.incomingWebxdcNotify, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(NotificationManager.handleMessagesNoticed(_:)), name: Event.messagesNoticed, object: nil) } @@ -155,4 +156,32 @@ public class NotificationManager { UIApplication.shared.endBackgroundTask(backgroundTask) // this line must be reached to balance call to `beginBackgroundTask` above } } + + @objc private func handleIncomingWebxdcNotify(_ notification: Notification) { + let backgroundTask = UIApplication.shared.beginBackgroundTask { + logger.info("incoming-webxdc-notify-task will end soon") + } + + DispatchQueue.global().async { [weak self] in + guard let self, let ui = notification.userInfo else { return } + let eventContext = dcAccounts.get(id: ui["account_id"] as? Int ?? 0) + if !eventContext.isMuted() { + let msg = eventContext.getMessage(id: ui["msg_id"] as? Int ?? 0) + let chat = eventContext.getChat(chatId: msg.chatId) + if !chat.isMuted { + let content = UNMutableNotificationContent() + content.title = chat.name + content.body = msg.getWebxdcAppName() + ": " + (ui["text"] as? String ?? "") + content.userInfo["account_id"] = eventContext.id + content.userInfo["chat_id"] = chat.id + content.userInfo["message_id"] = msg.id + + let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) + UNUserNotificationCenter.current().add(request, withCompletionHandler: nil) + } + } + + UIApplication.shared.endBackgroundTask(backgroundTask) // this line must be reached to balance call to `beginBackgroundTask` above + } + } }