TF-2871 Handle click notification to open detailed email on iOS
This commit is contained in:
@@ -21,8 +21,6 @@
|
||||
F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; };
|
||||
F522E8872C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; };
|
||||
F522E8882C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; };
|
||||
F522E88A2C0F117900DDA35B /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8892C0F117900DDA35B /* IntegerExtensions.swift */; };
|
||||
F522E88B2C0F117900DDA35B /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8892C0F117900DDA35B /* IntegerExtensions.swift */; };
|
||||
F52F993027FD6EB900346091 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F52F992F27FD6EB900346091 /* ShareViewController.swift */; };
|
||||
F52F993327FD6EB900346091 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F52F993127FD6EB900346091 /* MainInterface.storyboard */; };
|
||||
F52F993727FD6EB900346091 /* TeamMailShareExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F52F992D27FD6EB900346091 /* TeamMailShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
@@ -139,7 +137,6 @@
|
||||
B2EAFF659572E6B9F5AFAAF8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
F522E87E2C0EE23400DDA35B /* AuthenticationSSOTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationSSOTests.swift; sourceTree = "<group>"; };
|
||||
F522E8852C0EE8B600DDA35B /* CoreUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreUtils.swift; sourceTree = "<group>"; };
|
||||
F522E8892C0F117900DDA35B /* IntegerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerExtensions.swift; sourceTree = "<group>"; };
|
||||
F52F992D27FD6EB900346091 /* TeamMailShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TeamMailShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F52F992F27FD6EB900346091 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
F52F993227FD6EB900346091 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
@@ -446,7 +443,6 @@
|
||||
children = (
|
||||
F5E7D8782B38763B0009BB8A /* DateExtensions.swift */,
|
||||
F5E7D87B2B38764F0009BB8A /* StringExtensions.swift */,
|
||||
F522E8892C0F117900DDA35B /* IntegerExtensions.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -748,7 +744,6 @@
|
||||
files = (
|
||||
F5E7D87C2B38764F0009BB8A /* StringExtensions.swift in Sources */,
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
F522E88A2C0F117900DDA35B /* IntegerExtensions.swift in Sources */,
|
||||
F5E7D8792B38763B0009BB8A /* DateExtensions.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */,
|
||||
@@ -787,7 +782,6 @@
|
||||
F53D1E7F2B2E3C2600051FD0 /* JmapConstants.swift in Sources */,
|
||||
F5E7D8822B3876F60009BB8A /* AuthenticationCredential.swift in Sources */,
|
||||
F5D4EA032B2ABF090090DDFC /* NotificationService.swift in Sources */,
|
||||
F522E88B2C0F117900DDA35B /* IntegerExtensions.swift in Sources */,
|
||||
F522E8872C0EE8B600DDA35B /* CoreUtils.swift in Sources */,
|
||||
F53D1E8A2B2E4BB700051FD0 /* TokenOidc.swift in Sources */,
|
||||
F53D1E862B2E401B00051FD0 /* JmapRequestGenerator.swift in Sources */,
|
||||
|
||||
@@ -8,7 +8,7 @@ import flutter_local_notifications
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
|
||||
var notificationInteractionChannel: FlutterMethodChannel?
|
||||
var initialNotificationInfo: Any?
|
||||
var currentEmailId: String?
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
@@ -17,7 +17,12 @@ import flutter_local_notifications
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
createNotificationInteractionChannel()
|
||||
initialNotificationInfo = launchOptions?[.remoteNotification]
|
||||
|
||||
if let payload = launchOptions?[.remoteNotification] as? [AnyHashable : Any],
|
||||
let emailId = payload[JmapConstants.EMAIL_ID] as? String,
|
||||
!emailId.isEmpty {
|
||||
currentEmailId = emailId
|
||||
}
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
|
||||
@@ -90,9 +95,7 @@ import flutter_local_notifications
|
||||
TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent:newBadgeCount: \(newBadgeCount)")
|
||||
updateAppBadger(newBadgeCount: newBadgeCount)
|
||||
}
|
||||
if let emailId = notification.request.content.userInfo[JmapConstants.EMAIL_ID] as? String,
|
||||
!emailId.isEmpty,
|
||||
!isAppForegroundActive() {
|
||||
if validateDisplayPushNotification(userInfo: notification.request.content.userInfo) {
|
||||
completionHandler([.alert, .badge, .sound])
|
||||
} else {
|
||||
completionHandler([])
|
||||
@@ -105,12 +108,24 @@ import flutter_local_notifications
|
||||
let newBadgeCount = currentBadgeCount > 0 ? currentBadgeCount - 1 : 0
|
||||
updateAppBadger(newBadgeCount: newBadgeCount)
|
||||
|
||||
if let emailId = response.notification.request.content.userInfo[JmapConstants.EMAIL_ID] as? String {
|
||||
self.notificationInteractionChannel?.invokeMethod("openEmail", arguments: emailId)
|
||||
|
||||
let userInfo = response.notification.request.content.userInfo
|
||||
|
||||
if let emailId = userInfo[JmapConstants.EMAIL_ID] as? String, !emailId.isEmpty {
|
||||
self.notificationInteractionChannel?.invokeMethod(
|
||||
CoreUtils.CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_FOREGROUND_OR_BACKGROUND,
|
||||
arguments: emailId)
|
||||
}
|
||||
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
private func validateDisplayPushNotification(userInfo: [AnyHashable : Any]) -> Bool {
|
||||
if let emailId = userInfo[JmapConstants.EMAIL_ID] as? String, !emailId.isEmpty, UIApplication.shared.applicationState != .active {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension AppDelegate {
|
||||
@@ -134,25 +149,21 @@ extension AppDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func isAppForegroundActive() -> Bool {
|
||||
return UIApplication.shared.applicationState == .active
|
||||
}
|
||||
|
||||
private func createNotificationInteractionChannel() {
|
||||
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
||||
|
||||
self.notificationInteractionChannel = FlutterMethodChannel(
|
||||
name: "notification_interaction_channel",
|
||||
name: CoreUtils.NOTIFICATION_INTERACTION_CHANNEL_NAME,
|
||||
binaryMessenger: controller.binaryMessenger
|
||||
)
|
||||
|
||||
self.notificationInteractionChannel?.setMethodCallHandler { (call, result) in
|
||||
switch call.method {
|
||||
case "getInitialNotificationInfo":
|
||||
result(self.initialNotificationInfo)
|
||||
self.initialNotificationInfo = nil
|
||||
default:
|
||||
break
|
||||
case CoreUtils.CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED:
|
||||
result(self.currentEmailId)
|
||||
self.currentEmailId = nil
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,8 @@ extension Date {
|
||||
dateFormatter.locale = Locale(identifier: CoreUtils.EN_US_POSIX_LOCALE)
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
|
||||
func adding(seconds: Int) -> Date {
|
||||
return self.addingTimeInterval(TimeInterval(seconds))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import Foundation
|
||||
|
||||
extension Int {
|
||||
func convertMillisecondsToDate() -> Date {
|
||||
return Date(timeIntervalSince1970: TimeInterval(self) / 1000)
|
||||
}
|
||||
|
||||
func convertMillisecondsToISO8601String() -> String {
|
||||
return convertMillisecondsToDate().convertDateToISO8601String()
|
||||
}
|
||||
}
|
||||
@@ -41,9 +41,12 @@ class AuthenticationInterceptor: RequestInterceptor {
|
||||
}
|
||||
|
||||
let newRefreshToken = tokenResponse.refreshToken ?? authenticationSSO.refreshToken
|
||||
let expireTime = tokenResponse.expiresTime != nil
|
||||
? tokenResponse.expiresTime!.convertMillisecondsToISO8601String()
|
||||
: nil
|
||||
|
||||
var expireTime: String? = nil
|
||||
|
||||
if (tokenResponse.expiresTime != nil) {
|
||||
expireTime = CoreUtils.shared.getCurrentDate().adding(seconds: tokenResponse.expiresTime!).convertDateToISO8601String()
|
||||
}
|
||||
|
||||
self.authentication = AuthenticationSSO(
|
||||
type: AuthenticationType.oidc,
|
||||
|
||||
@@ -5,6 +5,9 @@ class CoreUtils {
|
||||
|
||||
static let ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS"
|
||||
static let EN_US_POSIX_LOCALE = "en_US_POSIX"
|
||||
static let NOTIFICATION_INTERACTION_CHANNEL_NAME = "notification_interaction_channel"
|
||||
static let CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_FOREGROUND_OR_BACKGROUND = "current_email_id_in_notification_click_when_app_foreground_or_background"
|
||||
static let CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED = "current_email_id_in_notification_click_when_app_terminated"
|
||||
|
||||
func getCurrentDate() -> Date {
|
||||
if #available(iOS 15, *) {
|
||||
|
||||
@@ -5,25 +5,39 @@ import XCTest
|
||||
class DateConversionTests: XCTestCase {
|
||||
|
||||
func testConvertValidISO8601StringToDate() {
|
||||
// Arrange
|
||||
let validDateString = "2024-05-20T22:54:57.958"
|
||||
|
||||
// Act
|
||||
let date = validDateString.convertISO8601StringToDate()
|
||||
|
||||
// Assert
|
||||
XCTAssertNotNil(date, "Date should not be nil")
|
||||
|
||||
// Arrange
|
||||
let calendar = Calendar.current
|
||||
let expectedComponents = DateComponents(year: 2024, month: 5, day: 20, hour: 22, minute: 54, second: 57, nanosecond: 958000000)
|
||||
|
||||
// Act
|
||||
let expectedDate = calendar.date(from: expectedComponents)
|
||||
|
||||
// Assert
|
||||
XCTAssertEqual(date, expectedDate, "Converted date does not match expected date")
|
||||
}
|
||||
|
||||
func testInvalidISO8601String() {
|
||||
// Arrange
|
||||
let invalidDateString = "Invalid Date String"
|
||||
|
||||
// Act
|
||||
let date = invalidDateString.convertISO8601StringToDate()
|
||||
|
||||
// Assert
|
||||
XCTAssertNil(date, "The conversion should return nil for an invalid date string.")
|
||||
}
|
||||
|
||||
func testConvertValidDateToISO8601String() {
|
||||
// Arrange
|
||||
let validDate = Calendar.current.date(
|
||||
from: DateComponents(
|
||||
year: 2024,
|
||||
@@ -35,11 +49,38 @@ class DateConversionTests: XCTestCase {
|
||||
nanosecond: 958000000
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
let expectedDateString = "2024-05-20T22:54:57.958"
|
||||
|
||||
// Act
|
||||
let validDateString = validDate!.convertDateToISO8601String()
|
||||
|
||||
// Assert
|
||||
XCTAssertEqual(validDateString, expectedDateString, "Converted date string does not match expected date string")
|
||||
}
|
||||
|
||||
func testAddingSeconds() {
|
||||
// Arrange
|
||||
let initialDate = Date()
|
||||
let secondsToAdd = 60
|
||||
let expectedDate = initialDate.addingTimeInterval(TimeInterval(secondsToAdd))
|
||||
|
||||
// Act
|
||||
let resultDate = initialDate.adding(seconds: secondsToAdd)
|
||||
|
||||
// Assert
|
||||
XCTAssertEqual(resultDate, expectedDate, "The date after adding seconds should be correct.")
|
||||
}
|
||||
|
||||
func testAddingNegativeSeconds() {
|
||||
// Arrange
|
||||
let initialDate = Date()
|
||||
let secondsToSubtract = -60
|
||||
let expectedDate = initialDate.addingTimeInterval(TimeInterval(secondsToSubtract))
|
||||
|
||||
// Act
|
||||
let resultDate = initialDate.adding(seconds: secondsToSubtract)
|
||||
|
||||
// Assert
|
||||
XCTAssertEqual(resultDate, expectedDate, "The date after subtracting seconds should be correct.")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user