-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MM-57409] Notify error sharing on iOS #8408
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -128,6 +128,14 @@ extension ShareExtension: URLSessionDataDelegate { | |
session.configuration.identifier ?? "no identifier", | ||
error?.localizedDescription ?? "no error description available" | ||
) | ||
|
||
if let sessionID = session.configuration.identifier { | ||
let uploadData = getUploadSessionData(id: sessionID) | ||
self.notifyFailureNow(failID: uploadData?.failNotificationID) | ||
} else { | ||
_ = self.scheduleFailNotification(timeout: 1) | ||
} | ||
|
||
return | ||
} | ||
|
||
|
@@ -176,6 +184,14 @@ extension ShareExtension: URLSessionDataDelegate { | |
|
||
let total = data.totalFiles | ||
let count = data.fileIds.count | ||
if let timeout = data.failTimeout, timeout < Date() { | ||
backgroundSession?.invalidateAndCancel() | ||
os_log( | ||
"Mattermost BackgroundSession: postMessageIfUploadFinished session %{public}@ timed out", | ||
session.configuration.identifier ?? "no identifier" | ||
) | ||
return | ||
} | ||
Comment on lines
+187
to
+194
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't this be done after checking if the last file was actually uploaded? because if that is the case, this is cancelling the whole thing instead of just posting the message There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup! The reason is that we already told the user that the sharing failed due to a timeout. Since we can't control when this happens this could be a day later making things confusing. So we cancel the first opportunity we have |
||
|
||
if count == total { | ||
os_log( | ||
|
@@ -189,10 +205,16 @@ extension ShareExtension: URLSessionDataDelegate { | |
self.postMessageForSession(withId: id) | ||
self.urlSessionDidFinishEvents(forBackgroundURLSession: session) | ||
} | ||
os_log( | ||
OSLogType.default, | ||
"Mattermost BackgroundSession: we are good, removing the fail notification=%{public}@", | ||
id | ||
) | ||
self.cancelFailNotification(failID: data.failNotificationID) | ||
} else { | ||
os_log( | ||
OSLogType.default, | ||
"Mattermost BackgroundSession: finish uploading files %{public}@ of %{public}@ for identifier=%{public}@", | ||
"Mattermost BackgroundSession: still uploading files %{public}@ of %{public}@ for identifier=%{public}@", | ||
"\(count)", | ||
"\(total)", | ||
id | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,14 +9,19 @@ import Foundation | |
import os.log | ||
|
||
extension ShareExtension { | ||
|
||
public func uploadFiles(serverUrl: String, channelId: String, message: String, | ||
files: [String], completionHandler: @escaping () -> Void) -> String? { | ||
let id = "mattermost-share-upload-\(UUID().uuidString)" | ||
|
||
let uuidString = self.scheduleFailNotification(timeout: SHARE_TIMEOUT) | ||
|
||
createUploadSessionData( | ||
id: id, serverUrl: serverUrl, | ||
channelId: channelId, message: message, | ||
files: files | ||
files: files, | ||
failNotificationID: uuidString, | ||
failTimeout: Date().addingTimeInterval(SHARE_TIMEOUT) | ||
) | ||
|
||
guard let token = try? Keychain.default.getToken(for: serverUrl) else {return "Could not retrieve the session token from the KeyChain"} | ||
|
@@ -56,6 +61,7 @@ extension ShareExtension { | |
filename, | ||
id | ||
) | ||
return "There was an error when trying to upload. Please try again" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. before the other two returns you added a |
||
} | ||
} else { | ||
os_log( | ||
|
@@ -64,6 +70,7 @@ extension ShareExtension { | |
filename, | ||
id | ||
) | ||
notifyFailureNow(description: FILE_ERROR_MESSAGE, failID: uuidString) | ||
return "The file \(filename) could not be processed for upload" | ||
} | ||
} else { | ||
|
@@ -73,6 +80,7 @@ extension ShareExtension { | |
file, | ||
id | ||
) | ||
notifyFailureNow(description: FILE_ERROR_MESSAGE, failID: uuidString) | ||
return "File not found \(file)" | ||
} | ||
} | ||
|
@@ -83,13 +91,14 @@ extension ShareExtension { | |
"Mattermost BackgroundSession: posting message for identifier=%{public}@ without files", | ||
id | ||
) | ||
self.postMessageForSession(withId: id, completionHandler: completionHandler) | ||
let cancelAndComplete = createCancelHandler(completionHandler: completionHandler, failNotificationID: uuidString) | ||
self.postMessageForSession(withId: id, completionHandler: cancelAndComplete) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func postMessageForSession(withId id: String, completionHandler: (() -> Void)? = nil) { | ||
func postMessageForSession(withId id: String, completionHandler: ((Bool) -> Void)? = nil) { | ||
guard let data = getUploadSessionData(id: id) | ||
else { | ||
os_log( | ||
|
@@ -127,7 +136,7 @@ extension ShareExtension { | |
"Mattermost BackgroundSession: postMessageForSession without files call completionHandler for identifier=%{public}@", | ||
id | ||
) | ||
handler() | ||
handler(error == nil) | ||
} | ||
}) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
|
||
import Foundation | ||
import os.log | ||
import UserNotifications | ||
|
||
struct UploadSessionData { | ||
var serverUrl: String? | ||
|
@@ -15,6 +16,8 @@ struct UploadSessionData { | |
var fileIds: [String] = [] | ||
var message: String = "" | ||
var totalFiles: Int = 0 | ||
var failNotificationID: String? | ||
var failTimeout: Date? | ||
|
||
func toDictionary() -> NSDictionary { | ||
let data: NSDictionary = [ | ||
|
@@ -23,7 +26,9 @@ struct UploadSessionData { | |
"files": files, | ||
"fileIds": fileIds, | ||
"message": message, | ||
"totalFiles": totalFiles | ||
"totalFiles": totalFiles, | ||
"failNotificationID": failNotificationID, | ||
"failTimeout": failTimeout | ||
] | ||
|
||
return data | ||
|
@@ -36,18 +41,27 @@ struct UploadSessionData { | |
let fileIds = dict["fileIds"] as! [String] | ||
let message = dict["message"] as! String | ||
let totalFiles = dict["totalFiles"] as! Int | ||
let failNotificationID = dict["failNotificationID"] as! String | ||
let failTimeout = dict["failTimeout"] as! Date | ||
|
||
return UploadSessionData( | ||
serverUrl: serverUrl, | ||
channelId: channelId, | ||
files: files, | ||
fileIds: fileIds, | ||
message: message, | ||
totalFiles: totalFiles | ||
totalFiles: totalFiles, | ||
failNotificationID: failNotificationID, | ||
failTimeout: failTimeout | ||
) | ||
} | ||
} | ||
|
||
// TODO: setup some configuration for allowing users to figure out the right ammount of time to wait. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should allow the user to set this, we should come up with a number that makes sense.. maybe an hour or so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's cool with me too. O just didn't feel entitled to make a decision on either option |
||
let SHARE_TIMEOUT: Double = 120 // change to 300 | ||
let NETWORK_ERROR_MESSAGE: String = "Mattermost App couldn't acknowledge the message was posted due to network conditions, please review and try again" | ||
let FILE_ERROR_MESSAGE: String = "The shared file couldn't be processed for sharing" | ||
Comment on lines
+62
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally this would be localized. You can check the code in the Share Extension around the Views on how is done with the use of NSLocalizedString or String.localizedString. |
||
|
||
public class ShareExtension: NSObject { | ||
public var backgroundSession: URLSession? | ||
private var cacheURL: URL? | ||
|
@@ -88,13 +102,15 @@ public class ShareExtension: NSObject { | |
) | ||
} | ||
|
||
func createUploadSessionData(id: String, serverUrl: String, channelId: String, message: String, files: [String]) { | ||
func createUploadSessionData(id: String, serverUrl: String, channelId: String, message: String, files: [String], failNotificationID: String, failTimeout: Date) { | ||
let data = UploadSessionData( | ||
serverUrl: serverUrl, | ||
channelId: channelId, | ||
files: files, | ||
message: message, | ||
totalFiles: files.count | ||
totalFiles: files.count, | ||
failNotificationID: failNotificationID, | ||
failTimeout: failTimeout | ||
) | ||
|
||
saveUploadSessionData(id: id, data: data) | ||
|
@@ -127,7 +143,9 @@ public class ShareExtension: NSObject { | |
files: data.files, | ||
fileIds: fileIds, | ||
message: data.message, | ||
totalFiles: data.totalFiles | ||
totalFiles: data.totalFiles, | ||
failNotificationID: data.failNotificationID, | ||
failTimeout: data.failTimeout | ||
) | ||
saveUploadSessionData(id: id, data: newData) | ||
} | ||
|
@@ -146,4 +164,65 @@ public class ShareExtension: NSObject { | |
} | ||
} | ||
} | ||
|
||
func scheduleFailNotification(timeout: Double = SHARE_TIMEOUT, description: String = NETWORK_ERROR_MESSAGE) -> String { | ||
let failNotification = UNMutableNotificationContent() | ||
failNotification.title = "Share content failed" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should also be localized. |
||
failNotification.body = description | ||
|
||
let timeoutTrigger = UNTimeIntervalNotificationTrigger(timeInterval: timeout, repeats: false) | ||
|
||
let uuidString = UUID().uuidString | ||
let request = UNNotificationRequest(identifier: uuidString, content: failNotification, trigger: timeoutTrigger) | ||
|
||
os_log( | ||
OSLogType.default, | ||
"Mattermost BackgroundSession: Created Fail notification with ID: %{uuidString}@", | ||
uuidString | ||
) | ||
|
||
// Schedule the request with the system. | ||
UNUserNotificationCenter.current().add(request) | ||
|
||
return uuidString | ||
} | ||
|
||
func cancelFailNotification(failID: String? = nil) -> Void { | ||
if let id = failID { | ||
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [id]) | ||
os_log( | ||
OSLogType.default, | ||
"Mattermost BackgroundSession: Cancelled fail notification %{id}@", | ||
id | ||
) | ||
} | ||
} | ||
|
||
func notifyFailureNow(description: String = NETWORK_ERROR_MESSAGE, failID: String?) -> Void { | ||
self.cancelFailNotification(failID: failID) | ||
_ = self.scheduleFailNotification(timeout: 1, description: description) | ||
} | ||
|
||
func createCancelHandler(completionHandler: @escaping () -> Void, failNotificationID: String) -> (Bool) -> Void { | ||
return { success in | ||
if success { | ||
self.cancelFailNotification() | ||
|
||
// cancel any pending tasks in case of not getting connection. If this affects anything else, then we should track the uploads and cancel them individually | ||
// Session is configured to wait for connection, but we don't know when that's going to happen, so we die early if we don't get a connection in a time that | ||
// makes sense. | ||
self.backgroundSession?.invalidateAndCancel() | ||
completionHandler() | ||
return | ||
} | ||
|
||
self.notifyFailureNow(failID: failNotificationID) | ||
os_log( | ||
OSLogType.default, | ||
"Mattermost BackgroundSession: sending fail notification now" | ||
) | ||
|
||
completionHandler() | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the changes on this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove them, they were added by mistake