diff --git a/ios/Gekidou/.swiftpm/xcode/xcshareddata/xcschemes/Gekidou.xcscheme b/ios/Gekidou/.swiftpm/xcode/xcshareddata/xcschemes/Gekidou.xcscheme
new file mode 100644
index 0000000000..3f997e5f39
--- /dev/null
+++ b/ios/Gekidou/.swiftpm/xcode/xcshareddata/xcschemes/Gekidou.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ios/Gekidou/Sources/Gekidou/FileCache.swift b/ios/Gekidou/Sources/Gekidou/FileCache.swift
index 8e0cf05a76..118441fcc5 100644
--- a/ios/Gekidou/Sources/Gekidou/FileCache.swift
+++ b/ios/Gekidou/Sources/Gekidou/FileCache.swift
@@ -51,11 +51,10 @@ public class FileCache: NSObject {
}
public func saveProfileImage(serverUrl: String, userId: String, imageData: Data?) {
- guard let data = imageData,
- let url = getUrlImageFor(serverUrl: serverUrl, userId: userId)
- else { return }
-
do {
+ guard let data = imageData,
+ let url = getUrlImageFor(serverUrl: serverUrl, userId: userId)
+ else { return }
try data.write(to: url)
} catch let error {
print("Erro saving image. \(error)")
diff --git a/ios/Gekidou/Sources/Gekidou/Keychain.swift b/ios/Gekidou/Sources/Gekidou/Keychain.swift
index 571811cc61..a31f7aeb95 100644
--- a/ios/Gekidou/Sources/Gekidou/Keychain.swift
+++ b/ios/Gekidou/Sources/Gekidou/Keychain.swift
@@ -48,6 +48,7 @@ extension KeychainError: LocalizedError {
public class Keychain: NSObject {
@objc public static let `default` = Keychain()
+ private var tokenCache = Dictionary()
public func getClientIdentityAndCertificate(for host: String) throws -> (SecIdentity, SecCertificate)? {
let query = try buildIdentityQuery(for: host)
@@ -80,6 +81,10 @@ public class Keychain: NSObject {
}
public func getToken(for serverUrl: String) throws -> String? {
+ if let cache = tokenCache[serverUrl] {
+ return cache
+ }
+
var attributes = try buildTokenAttributes(for: serverUrl)
attributes[kSecMatchLimit] = kSecMatchLimitOne
attributes[kSecReturnData] = kCFBooleanTrue
@@ -88,7 +93,9 @@ public class Keychain: NSObject {
let status = SecItemCopyMatching(attributes as CFDictionary, &result)
let data = result as? Data
if status == errSecSuccess && data != nil {
- return String(data: data!, encoding: .utf8)
+ let token = String(data: data!, encoding: .utf8)
+ tokenCache[serverUrl] = token
+ return token
}
return nil
diff --git a/ios/Gekidou/Sources/Gekidou/Networking/PushNotification.swift b/ios/Gekidou/Sources/Gekidou/Networking/PushNotification.swift
index 596e17c101..415bc7b246 100644
--- a/ios/Gekidou/Sources/Gekidou/Networking/PushNotification.swift
+++ b/ios/Gekidou/Sources/Gekidou/Networking/PushNotification.swift
@@ -87,25 +87,22 @@ extension Network {
private func matchUsername(in message: String) -> [String] {
let specialMentions = Set(["all", "here", "channel"])
- do {
- let regex = try NSRegularExpression(pattern: "\\B@(([a-z0-9-._]*[a-z0-9_])[.-]*)", options: [.caseInsensitive])
+ if let regex = try? NSRegularExpression(pattern: "\\B@(([a-z0-9-._]*[a-z0-9_])[.-]*)", options: [.caseInsensitive]) {
let results = regex.matches(in: message, range: _NSRange(message.startIndex..., in: message))
- return results.map{ String(message[Range($0.range, in: message)!]).removePrefix("@") }.filter{ !specialMentions.contains($0)}
- } catch let error {
- print("invalid regex: \(error.localizedDescription)")
- return []
+ if !results.isEmpty {
+ let username = results.map({ String(message[Range($0.range, in: message)!]).removePrefix("@") }).filter({ !specialMentions.contains($0)})
+ return username
+ }
}
+ return []
}
- public func fetchProfileImageSync(_ serverUrl: String, senderId: String, overrideIconUrl: String?) -> Data? {
- var imgData: Data?
- let semaphore = DispatchSemaphore(value: 0)
-
+ public func fetchProfileImageSync(_ serverUrl: String, senderId: String, overrideIconUrl: String?, completionHandler: @escaping (_ data: Data?) -> Void) {
func processResponse(data: Data?, response: URLResponse?, error: Error?) {
if let httpResponse = response as? HTTPURLResponse {
if (httpResponse.statusCode == 200 && error == nil) {
- imgData = data
FileCache.default.saveProfileImage(serverUrl: serverUrl, userId: senderId, imageData: data)
+ completionHandler(data)
} else {
os_log(
OSLogType.default,
@@ -115,7 +112,6 @@ extension Network {
)
}
}
- semaphore.signal()
}
if let overrideUrl = overrideIconUrl,
@@ -124,16 +120,12 @@ extension Network {
} else {
if let image = FileCache.default.getProfileImage(serverUrl: serverUrl, userId: senderId) {
os_log(OSLogType.default, "Mattermost Notifications: cached image")
- imgData = image.pngData()
- semaphore.signal()
+ completionHandler(image.pngData())
} else {
os_log(OSLogType.default, "Mattermost Notifications: image not cached")
fetchUserProfilePicture(userId: senderId, withServerUrl: serverUrl, completionHandler: processResponse)
}
}
-
- semaphore.wait()
- return imgData
}
public func postNotificationReceipt(_ ackNotification: AckNotification, completionHandler: @escaping ResponseHandler) {
@@ -152,14 +144,20 @@ extension Network {
let operation = BlockOperation {
let group = DispatchGroup()
- let channelId = notification.userInfo["channel_id"] as! String
- let rootId = notification.userInfo.index(forKey: "root_id") != nil ? notification.userInfo["root_id"] as! String : ""
- let serverUrl = notification.userInfo["server_url"] as! String
- let isCRTEnabled = notification.userInfo["is_crt_enabled"] as! Bool
- let currentUser = try! Database.default.queryCurrentUser(serverUrl)
+ let channelId = notification.userInfo["channel_id"] as? String
+ let rootId = notification.userInfo["root_id"] as? String ?? ""
+ let serverUrl = notification.userInfo["server_url"] as? String
+ let isCRTEnabled = notification.userInfo["is_crt_enabled"] as? Bool ?? false
+
+ guard let serverUrl = serverUrl,
+ let channelId = channelId
+ else { return }
+
+ let currentUser = try? Database.default.queryCurrentUser(serverUrl)
let currentUserId = currentUser?[Expression("id")]
let currentUsername = currentUser?[Expression("username")]
+
var postData: PostData? = nil
var myChannelData: ChannelMemberData? = nil
var threadData: ThreadData? = nil
@@ -196,9 +194,10 @@ extension Network {
group.enter()
let since = try? Database.default.queryPostsSinceForChannel(withId: channelId, withServerUrl: serverUrl)
self.fetchPostsForChannel(withId: channelId, withSince: since, withServerUrl: serverUrl, withIsCRTEnabled: isCRTEnabled, withRootId: rootId) { data, response, error in
- if self.responseOK(response), let data = data {
- postData = try! JSONDecoder().decode(PostData.self, from: data)
- if postData?.posts.count ?? 0 > 0 {
+ if self.responseOK(response), let data = data,
+ let jsonData = try? JSONDecoder().decode(PostData.self, from: data) {
+ postData = jsonData
+ if jsonData.posts.count > 0 {
var authorIds: Set = Set()
var usernames: Set = Set()
@@ -206,7 +205,7 @@ extension Network {
var threadParticipantUsernames: Set = Set() // Used to exclude the "usernames" present in the thread participants
var threadParticipantUsers = [String: User]() // All unique users from thread participants are stored here
- postData!.posts.forEach{post in
+ jsonData.posts.forEach{post in
if (currentUserId != nil && post.user_id != currentUserId) {
authorIds.insert(post.user_id)
}
@@ -304,27 +303,28 @@ extension Network {
group.wait()
group.enter()
- if (postData != nil && postData?.posts != nil && postData!.posts.count > 0) {
- if let db = try? Database.default.getDatabaseForServer(serverUrl) {
- let receivingThreads = isCRTEnabled && !rootId.isEmpty
- try? db.transaction {
- try? Database.default.handlePostData(db, postData!, channelId, since != nil, receivingThreads)
-
- if threads.count > 0 {
- try? Database.default.handleThreads(db, threads)
- }
-
- if users.count > 0 {
- try? Database.default.insertUsers(db, users)
- }
-
- if myChannelData != nil {
- try? Database.default.handleMyChannelMentions(db, myChannelData!, withCRTEnabled: isCRTEnabled)
- }
-
- if threadData != nil {
- try? Database.default.handleThreadMentions(db, threadData!)
- }
+ if let data = postData,
+ let posts = data.posts as [Post]?,
+ let db = try? Database.default.getDatabaseForServer(serverUrl),
+ posts.count > 0 {
+ let receivingThreads = isCRTEnabled && !rootId.isEmpty
+ try? db.transaction {
+ try? Database.default.handlePostData(db, data, channelId, since != nil, receivingThreads)
+
+ if threads.count > 0 {
+ try? Database.default.handleThreads(db, threads)
+ }
+
+ if users.count > 0 {
+ try? Database.default.insertUsers(db, users)
+ }
+
+ if let myChannel = myChannelData {
+ try? Database.default.handleMyChannelMentions(db, myChannel, withCRTEnabled: isCRTEnabled)
+ }
+
+ if let threads = threadData {
+ try? Database.default.handleThreadMentions(db, threads)
}
}
}
diff --git a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift
index 70d762c209..70692a4dd5 100644
--- a/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift
+++ b/ios/Gekidou/Sources/Gekidou/Networking/ShareExtension+Post.swift
@@ -10,7 +10,7 @@ import os.log
extension ShareExtension {
public func uploadFiles(serverUrl: String, channelId: String, message: String,
-files: [String], completionHandler: @escaping () -> Void) {
+files: [String], completionHandler: @escaping () -> Void) -> String? {
let id = "mattermost-share-upload-\(UUID().uuidString)"
createUploadSessionData(
@@ -18,6 +18,8 @@ files: [String], completionHandler: @escaping () -> Void) {
channelId: channelId, message: message,
files: files
)
+
+ guard let token = try? Keychain.default.getToken(for: serverUrl) else {return "Could not retrieve the session token from the KeyChain"}
if !files.isEmpty {
createBackroundSession(id: id)
@@ -26,8 +28,7 @@ files: [String], completionHandler: @escaping () -> Void) {
fileUrl.isFileURL {
let filename = fileUrl.lastPathComponent
- if let url = URL(string: "\(serverUrl)/api/v4/files?channel_id=\(channelId)&filename=\(filename)"),
- let token = try? Keychain.default.getToken(for: serverUrl) {
+ if let url = URL(string: "\(serverUrl)/api/v4/files?channel_id=\(channelId)&filename=\(filename)") {
var uploadRequest = URLRequest(url: url)
uploadRequest.httpMethod = "POST"
uploadRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
@@ -39,7 +40,6 @@ files: [String], completionHandler: @escaping () -> Void) {
task.resume()
}
}
-
}
}
completionHandler()
@@ -51,6 +51,8 @@ files: [String], completionHandler: @escaping () -> Void) {
)
self.postMessageForSession(withId: id, completionHandler: completionHandler)
}
+
+ return nil
}
func postMessageForSession(withId id: String, completionHandler: (() -> Void)? = nil) {
diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj
index 8a68311f99..c675279149 100644
--- a/ios/Mattermost.xcodeproj/project.pbxproj
+++ b/ios/Mattermost.xcodeproj/project.pbxproj
@@ -105,6 +105,7 @@
7FD482692864DC5900A5B18B /* OpenSans-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0DB14DFDF6E04FA69FE769DC /* OpenSans-SemiBoldItalic.ttf */; };
7FEB109D1F61019C0039A015 /* MattermostManaged.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109A1F61019C0039A015 /* MattermostManaged.m */; };
7FEC870128A4325D00DE96CB /* NotificationsModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEC870028A4325D00DE96CB /* NotificationsModule.m */; };
+ 7FF9C03D2983E7C6005CDCF5 /* ErrorSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF9C03C2983E7C6005CDCF5 /* ErrorSharingView.swift */; };
A94508A396424B2DB778AFE9 /* OpenSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E5C16B14E1CE4868886A1A00 /* OpenSans-SemiBold.ttf */; };
/* End PBXBuildFile section */
@@ -251,6 +252,7 @@
7FEB109A1F61019C0039A015 /* MattermostManaged.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MattermostManaged.m; path = Mattermost/MattermostManaged.m; sourceTree = ""; };
7FEC870028A4325D00DE96CB /* NotificationsModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NotificationsModule.m; path = Mattermost/NotificationsModule.m; sourceTree = ""; };
7FEC870428A44A7B00DE96CB /* NotificationsModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NotificationsModule.h; path = Mattermost/NotificationsModule.h; sourceTree = ""; };
+ 7FF9C03C2983E7C6005CDCF5 /* ErrorSharingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorSharingView.swift; sourceTree = ""; };
7FFE32B51FD9CCAA0038C7A0 /* FLAnimatedImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FLAnimatedImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7FFE32B61FD9CCAA0038C7A0 /* KSCrash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KSCrash.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7FFE32B71FD9CCAA0038C7A0 /* KSCrash.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KSCrash.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -604,6 +606,7 @@
7F93AAA528761F2E0047B89F /* NoServersView.swift */,
7F93AAA728761FA10047B89F /* NoMembershipView.swift */,
7F93AAAB28771E3F0047B89F /* ErrorLabelView.swift */,
+ 7FF9C03C2983E7C6005CDCF5 /* ErrorSharingView.swift */,
);
path = ErrorViews;
sourceTree = "";
@@ -1046,6 +1049,7 @@
7F7E9F5E2864E76D0064BFAF /* SingleAttachmentView.swift in Sources */,
7F7E9F472864E6C60064BFAF /* View.swift in Sources */,
7F93AAAE287725660047B89F /* Int64.swift in Sources */,
+ 7FF9C03D2983E7C6005CDCF5 /* ErrorSharingView.swift in Sources */,
7F93AAB8287778090047B89F /* Publishers.swift in Sources */,
7F7E9F732864E8060064BFAF /* CompassIcons.swift in Sources */,
7F93AABA28777A390047B89F /* Notification.swift in Sources */,
diff --git a/ios/MattermostShare/Extensions/Color.swift b/ios/MattermostShare/Extensions/Color.swift
index 1306c28cbb..451d93a395 100644
--- a/ios/MattermostShare/Extensions/Color.swift
+++ b/ios/MattermostShare/Extensions/Color.swift
@@ -42,6 +42,7 @@ extension Color {
struct ColorTheme {
let awayIndicator = Color("awayIndicator")
let buttonBg = Color("buttonBg")
+ let buttonColor = Color("buttonColor")
let centerChannelBg = Color("centerChannelBg")
let centerChannelColor = Color("centerChannelColor")
let dndIndicator = Color("dndIndicator")
diff --git a/ios/MattermostShare/ShareViewController.swift b/ios/MattermostShare/ShareViewController.swift
index e70489dd1a..c9eacaa82b 100644
--- a/ios/MattermostShare/ShareViewController.swift
+++ b/ios/MattermostShare/ShareViewController.swift
@@ -87,7 +87,6 @@ class ShareViewController: UIViewController {
}
private func doPost(_ notification: Notification) {
- removeObservers()
if let userInfo = notification.userInfo {
let serverUrl = userInfo["serverUrl"] as? String
let channelId = userInfo["channelId"] as? String
@@ -121,15 +120,20 @@ class ShareViewController: UIViewController {
)
let shareExtension = Gekidou.ShareExtension()
- shareExtension.uploadFiles(
+ let uploadError = shareExtension.uploadFiles(
serverUrl: url,
channelId: channel,
message: message,
files: files,
completionHandler: { [weak self] in
+ self?.removeObservers()
self?.extensionContext!.completeRequest(returningItems: [])
})
+ if uploadError != nil {
+ NotificationCenter.default.post(name: Notification.Name("errorPosting"), object: nil, userInfo: ["info": uploadError as Any])
+ }
} else {
+ removeObservers()
extensionContext!.completeRequest(returningItems: [])
}
}
diff --git a/ios/MattermostShare/ViewModels/ShareViewModel.swift b/ios/MattermostShare/ViewModels/ShareViewModel.swift
index fe46aa9652..ee643381b3 100644
--- a/ios/MattermostShare/ViewModels/ShareViewModel.swift
+++ b/ios/MattermostShare/ViewModels/ShareViewModel.swift
@@ -87,12 +87,14 @@ class ShareViewModel: ObservableObject {
func downloadProfileImage(serverUrl: String, userId: String, imageBinding: Binding) {
guard let _ = URL(string: serverUrl) else {
- fatalError("Missing or Malformed URL")
+ debugPrint("Missing or Malformed URL")
+ return
}
Gekidou.Network.default.fetchUserProfilePicture(userId: userId, withServerUrl: serverUrl, completionHandler: {data, response, error in
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
- fatalError("Error while fetching image \(String(describing: (response as? HTTPURLResponse)?.statusCode))")
+ debugPrint("Error while fetching image \(String(describing: (response as? HTTPURLResponse)?.statusCode))")
+ return
}
if let data = data {
diff --git a/ios/MattermostShare/Views/ErrorViews/ErrorSharingView.swift b/ios/MattermostShare/Views/ErrorViews/ErrorSharingView.swift
new file mode 100644
index 0000000000..beb9e3c910
--- /dev/null
+++ b/ios/MattermostShare/Views/ErrorViews/ErrorSharingView.swift
@@ -0,0 +1,54 @@
+//
+// ErrorSharingView.swift
+// MattermostShare
+//
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+//
+
+import SwiftUI
+
+struct ErrorSharingView: View {
+ var error: String
+ @State var retrying = false
+
+ let onError = NotificationCenter.default.publisher(for: Notification.Name("errorPosting"))
+
+ var body: some View {
+ VStack (spacing: 8) {
+ if retrying {
+ ProgressView()
+ .onAppear {
+ NotificationCenter.default.post(name: Notification.Name("submit"), object: nil, userInfo: nil)
+ }
+ } else {
+ Text("An error ocurred")
+ .font(Font.custom("Metropolis-SemiBold", size: 20))
+ .foregroundColor(Color.theme.centerChannelColor)
+ Text("There was an error when attempting to share the content to Mattermost.")
+ .font(Font.custom("OpenSans", size: 16))
+ .foregroundColor(Color.theme.centerChannelColor.opacity(0.72))
+ Text("Reason: \(error)")
+ .font(Font.custom("OpenSans", size: 12))
+ .foregroundColor(Color.theme.centerChannelColor.opacity(0.60))
+ Button {
+ retrying = true
+ } label: {
+ Text("Try again")
+ .font(Font.custom("OpenSans", size: 16))
+ .foregroundColor(Color.theme.buttonColor)
+ }
+ .buttonStyle(.borderedProminent)
+ .tint(Color.theme.buttonBg)
+ }
+ }
+ .transition(.opacity)
+ .animation(.linear(duration: 0.3))
+ .padding(.horizontal, 12)
+ .onReceive(onError) {_ in
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
+ retrying = false
+ }
+ }
+ }
+}
diff --git a/ios/MattermostShare/Views/InitialView.swift b/ios/MattermostShare/Views/InitialView.swift
index 7b075d7bc7..daee572332 100644
--- a/ios/MattermostShare/Views/InitialView.swift
+++ b/ios/MattermostShare/Views/InitialView.swift
@@ -14,6 +14,9 @@ struct InitialView: View {
@Binding var attachments: [AttachmentModel]
@Binding var linkPreviewUrl: String
@Binding var message: String
+ @State var error: String?
+
+ let onError = NotificationCenter.default.publisher(for: Notification.Name("errorPosting"))
var noServers: Bool {
shareViewModel.allServers.count == 0 || shareViewModel.server == nil
@@ -42,7 +45,10 @@ struct InitialView: View {
var body: some View {
return VStack {
- if noServers {
+ if error != nil {
+ ErrorSharingView(error: error!)
+ .transition(.opacity)
+ } else if noServers {
NoServersView()
} else if noChannels {
NoMembershipView()
@@ -65,5 +71,11 @@ struct InitialView: View {
)
)
.padding(20)
+ .animation(.linear(duration: 0.3))
+ .onReceive(onError) {obj in
+ if let userInfo = obj.userInfo, let info = userInfo["info"] as? String {
+ error = info
+ }
+ }
}
}
diff --git a/ios/MattermostShare/Views/NavigationButtons/PostButton.swift b/ios/MattermostShare/Views/NavigationButtons/PostButton.swift
index 4b4f4e7ef0..b6d3bd186c 100644
--- a/ios/MattermostShare/Views/NavigationButtons/PostButton.swift
+++ b/ios/MattermostShare/Views/NavigationButtons/PostButton.swift
@@ -15,6 +15,8 @@ struct PostButton: View {
@Binding var message: String
@State var pressed: Bool = false
+ let submitPublisher = NotificationCenter.default.publisher(for: Notification.Name("submit"))
+
func submit() {
let userInfo: [String: Any] = [
"serverUrl": shareViewModel.server?.url as Any,
@@ -50,5 +52,8 @@ struct PostButton: View {
.padding(.leading, 10)
.padding(.vertical, 5)
.disabled(disabled || pressed)
+ .onReceive(submitPublisher) {_ in
+ submit()
+ }
}
}
diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift
index 7e107ca269..700472a58e 100644
--- a/ios/NotificationService/NotificationService.swift
+++ b/ios/NotificationService/NotificationService.swift
@@ -81,70 +81,82 @@ class NotificationService: UNNotificationServiceExtension {
}
}
- func sendMessageIntent(notification: UNNotificationContent) {
- if #available(iOSApplicationExtension 15.0, *) {
- let isCRTEnabled = notification.userInfo["is_crt_enabled"] as! Bool
- let channelId = notification.userInfo["channel_id"] as! String
- let rootId = notification.userInfo.index(forKey: "root_id") != nil ? notification.userInfo["root_id"] as! String : ""
+ func sendMessageIntentCompletion(_ notification: UNNotificationContent, _ avatarData: Data?) {
+ if #available(iOSApplicationExtension 15.0, *),
+ let imgData = avatarData,
+ let channelId = notification.userInfo["channel_id"] as? String {
+ os_log(OSLogType.default, "Mattermost Notifications: creating intent")
+
+ let isCRTEnabled = notification.userInfo["is_crt_enabled"] as? Bool ?? false
+ let rootId = notification.userInfo["root_id"] as? String ?? ""
let senderId = notification.userInfo["sender_id"] as? String ?? ""
let channelName = notification.userInfo["channel_name"] as? String ?? ""
- let overrideIconUrl = notification.userInfo["override_icon_url"] as? String
- let serverUrl = notification.userInfo["server_url"] as? String ?? ""
let message = (notification.userInfo["message"] as? String ?? "")
+ let avatar = INImage(imageData: imgData) as INImage?
- if senderId != "" && serverUrl != "" {
- os_log(OSLogType.default, "Mattermost Notifications: Fetching profile Image in server %{public}@ for sender %{public}@", serverUrl, senderId)
- let avatarData = Network.default.fetchProfileImageSync(serverUrl, senderId: senderId, overrideIconUrl: overrideIconUrl)
- if let imgData = avatarData,
- let avatar = INImage(imageData: imgData) as INImage? {
- os_log(OSLogType.default, "Mattermost Notifications: creating intent")
- var conversationId = channelId
- if isCRTEnabled && rootId != "" {
- conversationId = rootId
- }
+ var conversationId = channelId
+ if isCRTEnabled && !rootId.isEmpty {
+ conversationId = rootId
+ }
- let handle = INPersonHandle(value: senderId, type: .unknown)
- let sender = INPerson(personHandle: handle,
- nameComponents: nil,
- displayName: channelName,
- image: avatar,
- contactIdentifier: nil,
- customIdentifier: nil)
+ let handle = INPersonHandle(value: senderId, type: .unknown)
+ let sender = INPerson(personHandle: handle,
+ nameComponents: nil,
+ displayName: channelName,
+ image: avatar,
+ contactIdentifier: nil,
+ customIdentifier: nil)
- let intent = INSendMessageIntent(recipients: nil,
- outgoingMessageType: .outgoingMessageText,
- content: message,
- speakableGroupName: nil,
- conversationIdentifier: conversationId,
- serviceName: nil,
- sender: sender,
- attachments: nil)
+ let intent = INSendMessageIntent(recipients: nil,
+ outgoingMessageType: .outgoingMessageText,
+ content: message,
+ speakableGroupName: nil,
+ conversationIdentifier: conversationId,
+ serviceName: nil,
+ sender: sender,
+ attachments: nil)
- let interaction = INInteraction(intent: intent, response: nil)
- interaction.direction = .incoming
- interaction.donate { error in
- if error != nil {
- self.contentHandler?(notification)
- os_log(OSLogType.default, "Mattermost Notifications: sendMessageIntent intent error %{public}@", error! as CVarArg)
- }
-
- do {
- let updatedContent = try notification.updating(from: intent)
- os_log(OSLogType.default, "Mattermost Notifications: present updated notification")
- self.contentHandler?(updatedContent)
- } catch {
- os_log(OSLogType.default, "Mattermost Notifications: something failed updating the notification %{public}@", error as CVarArg)
- self.contentHandler?(notification)
- }
- }
+ let interaction = INInteraction(intent: intent, response: nil)
+ interaction.direction = .incoming
+ interaction.donate { error in
+ if error != nil {
+ self.contentHandler?(notification)
+ os_log(OSLogType.default, "Mattermost Notifications: sendMessageIntent intent error %{public}@", error! as CVarArg)
+ }
+
+ do {
+ let updatedContent = try notification.updating(from: intent)
+ os_log(OSLogType.default, "Mattermost Notifications: present updated notification")
+ self.contentHandler?(updatedContent)
+ } catch {
+ os_log(OSLogType.default, "Mattermost Notifications: something failed updating the notification %{public}@", error as CVarArg)
+ self.contentHandler?(notification)
}
}
} else {
- os_log(OSLogType.default, "Mattermost Notifications: No intent created. will call contentHandler to present notification")
self.contentHandler?(notification)
}
}
+ func sendMessageIntent(notification: UNNotificationContent) {
+ if #available(iOSApplicationExtension 15.0, *) {
+ guard let serverUrl = notification.userInfo["server_url"] as? String,
+ let senderId = notification.userInfo["sender_id"] as? String
+ else {
+ os_log(OSLogType.default, "Mattermost Notifications: No intent created. will call contentHandler to present notification")
+ self.contentHandler?(notification)
+ return
+ }
+
+ os_log(OSLogType.default, "Mattermost Notifications: Fetching profile Image in server %{public}@ for sender %{public}@", serverUrl, senderId)
+ let overrideIconUrl = notification.userInfo["override_icon_url"] as? String
+
+ Network.default.fetchProfileImageSync(serverUrl, senderId: senderId, overrideIconUrl: overrideIconUrl) {[weak self] data in
+ self?.sendMessageIntentCompletion(notification, data)
+ }
+ }
+ }
+
func fetchReceipt(_ ackNotification: AckNotification) -> Void {
if (self.retryIndex >= self.fibonacciBackoffsInSeconds.count) {
os_log(OSLogType.default, "Mattermost Notifications: max retries reached. Will call sendMessageIntent")