TF-2871 Handle click notification to open detailed email on iOS
This commit is contained in:
@@ -30,7 +30,6 @@ class HtmlUtils {
|
|||||||
|
|
||||||
static const unregisterDropListener = (
|
static const unregisterDropListener = (
|
||||||
script: '''
|
script: '''
|
||||||
console.log("unregisterDropListener");
|
|
||||||
const editor = document.querySelector(".note-editable");
|
const editor = document.querySelector(".note-editable");
|
||||||
const newEditor = editor.cloneNode(true);
|
const newEditor = editor.cloneNode(true);
|
||||||
editor.parentNode.replaceChild(newEditor, editor);''',
|
editor.parentNode.replaceChild(newEditor, editor);''',
|
||||||
@@ -76,12 +75,12 @@ class HtmlUtils {
|
|||||||
required String base64Data,
|
required String base64Data,
|
||||||
required String mimeType
|
required String mimeType
|
||||||
}) {
|
}) {
|
||||||
|
log('HtmlUtils::convertBase64ToImageResourceData:');
|
||||||
mimeType = validateHtmlImageResourceMimeType(mimeType);
|
mimeType = validateHtmlImageResourceMimeType(mimeType);
|
||||||
if (!base64Data.endsWith('==')) {
|
if (!base64Data.endsWith('==')) {
|
||||||
base64Data.append('==');
|
base64Data.append('==');
|
||||||
}
|
}
|
||||||
final imageResource = 'data:$mimeType;base64,$base64Data';
|
final imageResource = 'data:$mimeType;base64,$base64Data';
|
||||||
log('HtmlUtils::convertBase64ToImageResourceData:imageResource: $imageResource');
|
|
||||||
return imageResource;
|
return imageResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Brief the logic flows when clicking notifications:
|
|||||||
We use `FlutterMethodChannel` to pass `UserInfo` from iOS native code to Flutter dart code
|
We use `FlutterMethodChannel` to pass `UserInfo` from iOS native code to Flutter dart code
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
self.notificationInteractionChannel?.invokeMethod("current_email_id_in_notification_click_on_foreground", arguments: response.notification.request.content.userInfo)
|
self.notificationInteractionChannel?.invokeMethod("current_email_id_in_notification_click_when_app_foreground_or_background", arguments: response.notification.request.content.userInfo)
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Terminated state
|
2. Terminated state
|
||||||
@@ -52,7 +52,7 @@ Save it in a `remoteNotificationPayload` variable in memory and use `FlutterMeth
|
|||||||
```swift
|
```swift
|
||||||
self.notificationInteractionChannel?.setMethodCallHandler { (call, result) in
|
self.notificationInteractionChannel?.setMethodCallHandler { (call, result) in
|
||||||
switch call.method {
|
switch call.method {
|
||||||
case "current_email_id_in_notification_click":
|
case "current_email_id_in_notification_click_when_app_terminated":
|
||||||
result(self.remoteNotificationPayload)
|
result(self.remoteNotificationPayload)
|
||||||
self.remoteNotificationPayload = nil
|
self.remoteNotificationPayload = nil
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -21,8 +21,6 @@
|
|||||||
F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; };
|
F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F522E8852C0EE8B600DDA35B /* CoreUtils.swift */; };
|
||||||
F522E8872C0EE8B600DDA35B /* 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 */; };
|
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 */; };
|
F52F993027FD6EB900346091 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F52F992F27FD6EB900346091 /* ShareViewController.swift */; };
|
||||||
F52F993327FD6EB900346091 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F52F993127FD6EB900346091 /* MainInterface.storyboard */; };
|
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, ); }; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
F52F993227FD6EB900346091 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||||
@@ -446,7 +443,6 @@
|
|||||||
children = (
|
children = (
|
||||||
F5E7D8782B38763B0009BB8A /* DateExtensions.swift */,
|
F5E7D8782B38763B0009BB8A /* DateExtensions.swift */,
|
||||||
F5E7D87B2B38764F0009BB8A /* StringExtensions.swift */,
|
F5E7D87B2B38764F0009BB8A /* StringExtensions.swift */,
|
||||||
F522E8892C0F117900DDA35B /* IntegerExtensions.swift */,
|
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -748,7 +744,6 @@
|
|||||||
files = (
|
files = (
|
||||||
F5E7D87C2B38764F0009BB8A /* StringExtensions.swift in Sources */,
|
F5E7D87C2B38764F0009BB8A /* StringExtensions.swift in Sources */,
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
F522E88A2C0F117900DDA35B /* IntegerExtensions.swift in Sources */,
|
|
||||||
F5E7D8792B38763B0009BB8A /* DateExtensions.swift in Sources */,
|
F5E7D8792B38763B0009BB8A /* DateExtensions.swift in Sources */,
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */,
|
F522E8862C0EE8B600DDA35B /* CoreUtils.swift in Sources */,
|
||||||
@@ -787,7 +782,6 @@
|
|||||||
F53D1E7F2B2E3C2600051FD0 /* JmapConstants.swift in Sources */,
|
F53D1E7F2B2E3C2600051FD0 /* JmapConstants.swift in Sources */,
|
||||||
F5E7D8822B3876F60009BB8A /* AuthenticationCredential.swift in Sources */,
|
F5E7D8822B3876F60009BB8A /* AuthenticationCredential.swift in Sources */,
|
||||||
F5D4EA032B2ABF090090DDFC /* NotificationService.swift in Sources */,
|
F5D4EA032B2ABF090090DDFC /* NotificationService.swift in Sources */,
|
||||||
F522E88B2C0F117900DDA35B /* IntegerExtensions.swift in Sources */,
|
|
||||||
F522E8872C0EE8B600DDA35B /* CoreUtils.swift in Sources */,
|
F522E8872C0EE8B600DDA35B /* CoreUtils.swift in Sources */,
|
||||||
F53D1E8A2B2E4BB700051FD0 /* TokenOidc.swift in Sources */,
|
F53D1E8A2B2E4BB700051FD0 /* TokenOidc.swift in Sources */,
|
||||||
F53D1E862B2E401B00051FD0 /* JmapRequestGenerator.swift in Sources */,
|
F53D1E862B2E401B00051FD0 /* JmapRequestGenerator.swift in Sources */,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import flutter_local_notifications
|
|||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
|
|
||||||
var notificationInteractionChannel: FlutterMethodChannel?
|
var notificationInteractionChannel: FlutterMethodChannel?
|
||||||
var initialNotificationInfo: Any?
|
var currentEmailId: String?
|
||||||
|
|
||||||
override func application(
|
override func application(
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
@@ -17,7 +17,12 @@ import flutter_local_notifications
|
|||||||
GeneratedPluginRegistrant.register(with: self)
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
|
||||||
createNotificationInteractionChannel()
|
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, *) {
|
if #available(iOS 10.0, *) {
|
||||||
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
|
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
|
||||||
@@ -90,9 +95,7 @@ import flutter_local_notifications
|
|||||||
TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent:newBadgeCount: \(newBadgeCount)")
|
TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent:newBadgeCount: \(newBadgeCount)")
|
||||||
updateAppBadger(newBadgeCount: newBadgeCount)
|
updateAppBadger(newBadgeCount: newBadgeCount)
|
||||||
}
|
}
|
||||||
if let emailId = notification.request.content.userInfo[JmapConstants.EMAIL_ID] as? String,
|
if validateDisplayPushNotification(userInfo: notification.request.content.userInfo) {
|
||||||
!emailId.isEmpty,
|
|
||||||
!isAppForegroundActive() {
|
|
||||||
completionHandler([.alert, .badge, .sound])
|
completionHandler([.alert, .badge, .sound])
|
||||||
} else {
|
} else {
|
||||||
completionHandler([])
|
completionHandler([])
|
||||||
@@ -105,12 +108,24 @@ import flutter_local_notifications
|
|||||||
let newBadgeCount = currentBadgeCount > 0 ? currentBadgeCount - 1 : 0
|
let newBadgeCount = currentBadgeCount > 0 ? currentBadgeCount - 1 : 0
|
||||||
updateAppBadger(newBadgeCount: newBadgeCount)
|
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()
|
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 {
|
extension AppDelegate {
|
||||||
@@ -134,25 +149,21 @@ extension AppDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isAppForegroundActive() -> Bool {
|
|
||||||
return UIApplication.shared.applicationState == .active
|
|
||||||
}
|
|
||||||
|
|
||||||
private func createNotificationInteractionChannel() {
|
private func createNotificationInteractionChannel() {
|
||||||
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
|
||||||
|
|
||||||
self.notificationInteractionChannel = FlutterMethodChannel(
|
self.notificationInteractionChannel = FlutterMethodChannel(
|
||||||
name: "notification_interaction_channel",
|
name: CoreUtils.NOTIFICATION_INTERACTION_CHANNEL_NAME,
|
||||||
binaryMessenger: controller.binaryMessenger
|
binaryMessenger: controller.binaryMessenger
|
||||||
)
|
)
|
||||||
|
|
||||||
self.notificationInteractionChannel?.setMethodCallHandler { (call, result) in
|
self.notificationInteractionChannel?.setMethodCallHandler { (call, result) in
|
||||||
switch call.method {
|
switch call.method {
|
||||||
case "getInitialNotificationInfo":
|
case CoreUtils.CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED:
|
||||||
result(self.initialNotificationInfo)
|
result(self.currentEmailId)
|
||||||
self.initialNotificationInfo = nil
|
self.currentEmailId = nil
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,4 +11,8 @@ extension Date {
|
|||||||
dateFormatter.locale = Locale(identifier: CoreUtils.EN_US_POSIX_LOCALE)
|
dateFormatter.locale = Locale(identifier: CoreUtils.EN_US_POSIX_LOCALE)
|
||||||
return dateFormatter.string(from: self)
|
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 newRefreshToken = tokenResponse.refreshToken ?? authenticationSSO.refreshToken
|
||||||
let expireTime = tokenResponse.expiresTime != nil
|
|
||||||
? tokenResponse.expiresTime!.convertMillisecondsToISO8601String()
|
var expireTime: String? = nil
|
||||||
: nil
|
|
||||||
|
if (tokenResponse.expiresTime != nil) {
|
||||||
|
expireTime = CoreUtils.shared.getCurrentDate().adding(seconds: tokenResponse.expiresTime!).convertDateToISO8601String()
|
||||||
|
}
|
||||||
|
|
||||||
self.authentication = AuthenticationSSO(
|
self.authentication = AuthenticationSSO(
|
||||||
type: AuthenticationType.oidc,
|
type: AuthenticationType.oidc,
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ class CoreUtils {
|
|||||||
|
|
||||||
static let ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS"
|
static let ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS"
|
||||||
static let EN_US_POSIX_LOCALE = "en_US_POSIX"
|
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 {
|
func getCurrentDate() -> Date {
|
||||||
if #available(iOS 15, *) {
|
if #available(iOS 15, *) {
|
||||||
|
|||||||
@@ -5,25 +5,39 @@ import XCTest
|
|||||||
class DateConversionTests: XCTestCase {
|
class DateConversionTests: XCTestCase {
|
||||||
|
|
||||||
func testConvertValidISO8601StringToDate() {
|
func testConvertValidISO8601StringToDate() {
|
||||||
|
// Arrange
|
||||||
let validDateString = "2024-05-20T22:54:57.958"
|
let validDateString = "2024-05-20T22:54:57.958"
|
||||||
|
|
||||||
|
// Act
|
||||||
let date = validDateString.convertISO8601StringToDate()
|
let date = validDateString.convertISO8601StringToDate()
|
||||||
|
|
||||||
|
// Assert
|
||||||
XCTAssertNotNil(date, "Date should not be nil")
|
XCTAssertNotNil(date, "Date should not be nil")
|
||||||
|
|
||||||
|
// Arrange
|
||||||
let calendar = Calendar.current
|
let calendar = Calendar.current
|
||||||
let expectedComponents = DateComponents(year: 2024, month: 5, day: 20, hour: 22, minute: 54, second: 57, nanosecond: 958000000)
|
let expectedComponents = DateComponents(year: 2024, month: 5, day: 20, hour: 22, minute: 54, second: 57, nanosecond: 958000000)
|
||||||
|
|
||||||
|
// Act
|
||||||
let expectedDate = calendar.date(from: expectedComponents)
|
let expectedDate = calendar.date(from: expectedComponents)
|
||||||
|
|
||||||
|
// Assert
|
||||||
XCTAssertEqual(date, expectedDate, "Converted date does not match expected date")
|
XCTAssertEqual(date, expectedDate, "Converted date does not match expected date")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInvalidISO8601String() {
|
func testInvalidISO8601String() {
|
||||||
|
// Arrange
|
||||||
let invalidDateString = "Invalid Date String"
|
let invalidDateString = "Invalid Date String"
|
||||||
|
|
||||||
|
// Act
|
||||||
let date = invalidDateString.convertISO8601StringToDate()
|
let date = invalidDateString.convertISO8601StringToDate()
|
||||||
|
|
||||||
|
// Assert
|
||||||
XCTAssertNil(date, "The conversion should return nil for an invalid date string.")
|
XCTAssertNil(date, "The conversion should return nil for an invalid date string.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testConvertValidDateToISO8601String() {
|
func testConvertValidDateToISO8601String() {
|
||||||
|
// Arrange
|
||||||
let validDate = Calendar.current.date(
|
let validDate = Calendar.current.date(
|
||||||
from: DateComponents(
|
from: DateComponents(
|
||||||
year: 2024,
|
year: 2024,
|
||||||
@@ -35,11 +49,38 @@ class DateConversionTests: XCTestCase {
|
|||||||
nanosecond: 958000000
|
nanosecond: 958000000
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
let expectedDateString = "2024-05-20T22:54:57.958"
|
let expectedDateString = "2024-05-20T22:54:57.958"
|
||||||
|
|
||||||
|
// Act
|
||||||
let validDateString = validDate!.convertDateToISO8601String()
|
let validDateString = validDate!.convertDateToISO8601String()
|
||||||
|
|
||||||
|
// Assert
|
||||||
XCTAssertEqual(validDateString, expectedDateString, "Converted date string does not match expected date string")
|
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.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ abstract class BaseController extends GetxController
|
|||||||
(failure) {
|
(failure) {
|
||||||
if (failure is FeatureFailure) {
|
if (failure is FeatureFailure) {
|
||||||
final exception = _performFilterExceptionInError(failure.exception);
|
final exception = _performFilterExceptionInError(failure.exception);
|
||||||
logError('BaseController::onData:exception: $exception');
|
logError('$runtimeType::onData:exception: $exception');
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
handleExceptionAction(failure: failure, exception: exception);
|
handleExceptionAction(failure: failure, exception: exception);
|
||||||
} else {
|
} else {
|
||||||
@@ -120,7 +120,7 @@ abstract class BaseController extends GetxController
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onError(Object error, StackTrace stackTrace) {
|
void onError(Object error, StackTrace stackTrace) {
|
||||||
logError('BaseController::onError():error: $error | stackTrace: $stackTrace');
|
logError('$runtimeType::onError():error: $error | stackTrace: $stackTrace');
|
||||||
final exception = _performFilterExceptionInError(error);
|
final exception = _performFilterExceptionInError(error);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
handleExceptionAction(exception: exception);
|
handleExceptionAction(exception: exception);
|
||||||
@@ -132,7 +132,7 @@ abstract class BaseController extends GetxController
|
|||||||
void onDone() {}
|
void onDone() {}
|
||||||
|
|
||||||
Exception? _performFilterExceptionInError(dynamic error) {
|
Exception? _performFilterExceptionInError(dynamic error) {
|
||||||
logError('BaseController::_performFilterExceptionInError(): $error');
|
logError('$runtimeType::_performFilterExceptionInError(): $error');
|
||||||
if (error is NoNetworkError || error is ConnectionTimeout || error is InternalServerError) {
|
if (error is NoNetworkError || error is ConnectionTimeout || error is InternalServerError) {
|
||||||
if (PlatformInfo.isWeb && currentOverlayContext != null && currentContext != null) {
|
if (PlatformInfo.isWeb && currentOverlayContext != null && currentContext != null) {
|
||||||
appToast.showToastMessage(
|
appToast.showToastMessage(
|
||||||
@@ -157,7 +157,7 @@ abstract class BaseController extends GetxController
|
|||||||
void handleErrorViewState(Object error, StackTrace stackTrace) {}
|
void handleErrorViewState(Object error, StackTrace stackTrace) {}
|
||||||
|
|
||||||
void handleExceptionAction({Failure? failure, Exception? exception}) {
|
void handleExceptionAction({Failure? failure, Exception? exception}) {
|
||||||
logError('BaseController::handleExceptionAction():failure: $failure | exception: $exception');
|
logError('$runtimeType::handleExceptionAction():failure: $failure | exception: $exception');
|
||||||
if (exception is ConnectionError) {
|
if (exception is ConnectionError) {
|
||||||
if (currentOverlayContext != null && currentContext != null) {
|
if (currentOverlayContext != null && currentContext != null) {
|
||||||
appToast.showToastErrorMessage(
|
appToast.showToastErrorMessage(
|
||||||
@@ -183,7 +183,7 @@ abstract class BaseController extends GetxController
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleFailureViewState(Failure failure) async {
|
void handleFailureViewState(Failure failure) async {
|
||||||
logError('BaseController::handleFailureViewState(): ${failure.runtimeType}');
|
logError('$runtimeType::handleFailureViewState():Failure = $failure');
|
||||||
if (failure is LogoutOidcFailure) {
|
if (failure is LogoutOidcFailure) {
|
||||||
if (_isFcmEnabled) {
|
if (_isFcmEnabled) {
|
||||||
_getStoredFirebaseRegistrationFromCache();
|
_getStoredFirebaseRegistrationFromCache();
|
||||||
@@ -197,7 +197,7 @@ abstract class BaseController extends GetxController
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleSuccessViewState(Success success) async {
|
void handleSuccessViewState(Success success) async {
|
||||||
log('BaseController::handleSuccessViewState(): ${success.runtimeType}');
|
log('$runtimeType::handleSuccessViewState():Success = ${success.runtimeType}');
|
||||||
if (success is LogoutOidcSuccess) {
|
if (success is LogoutOidcSuccess) {
|
||||||
if (_isFcmEnabled) {
|
if (_isFcmEnabled) {
|
||||||
_getStoredFirebaseRegistrationFromCache();
|
_getStoredFirebaseRegistrationFromCache();
|
||||||
@@ -214,7 +214,7 @@ abstract class BaseController extends GetxController
|
|||||||
void startFpsMeter() {
|
void startFpsMeter() {
|
||||||
FpsManager().start();
|
FpsManager().start();
|
||||||
fpsCallback = (fpsInfo) {
|
fpsCallback = (fpsInfo) {
|
||||||
log('BaseController::startFpsMeter(): $fpsInfo');
|
log('$runtimeType::startFpsMeter(): $fpsInfo');
|
||||||
};
|
};
|
||||||
if (fpsCallback != null) {
|
if (fpsCallback != null) {
|
||||||
FpsManager().addFpsCallback(fpsCallback!);
|
FpsManager().addFpsCallback(fpsCallback!);
|
||||||
@@ -234,7 +234,7 @@ abstract class BaseController extends GetxController
|
|||||||
requireCapability(session!, accountId!, [tmailContactCapabilityIdentifier]);
|
requireCapability(session!, accountId!, [tmailContactCapabilityIdentifier]);
|
||||||
TMailAutoCompleteBindings().dependencies();
|
TMailAutoCompleteBindings().dependencies();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('BaseController::injectAutoCompleteBindings(): exception: $e');
|
logError('$runtimeType::injectAutoCompleteBindings(): exception: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ abstract class BaseController extends GetxController
|
|||||||
requireCapability(session!, accountId!, [CapabilityIdentifier.jmapMdn]);
|
requireCapability(session!, accountId!, [CapabilityIdentifier.jmapMdn]);
|
||||||
MdnInteractorBindings().dependencies();
|
MdnInteractorBindings().dependencies();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
logError('BaseController::injectMdnBindings(): exception: $e');
|
logError('$runtimeType::injectMdnBindings(): exception: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ abstract class BaseController extends GetxController
|
|||||||
requireCapability(session!, accountId!, [capabilityForward]);
|
requireCapability(session!, accountId!, [capabilityForward]);
|
||||||
ForwardingInteractorsBindings().dependencies();
|
ForwardingInteractorsBindings().dependencies();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
logError('BaseController::injectForwardBindings(): exception: $e');
|
logError('$runtimeType::injectForwardBindings(): exception: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,14 +261,14 @@ abstract class BaseController extends GetxController
|
|||||||
requireCapability(session!, accountId!, [capabilityRuleFilter]);
|
requireCapability(session!, accountId!, [capabilityRuleFilter]);
|
||||||
EmailRulesInteractorBindings().dependencies();
|
EmailRulesInteractorBindings().dependencies();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
logError('BaseController::injectRuleFilterBindings(): exception: $e');
|
logError('$runtimeType::injectRuleFilterBindings(): exception: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> injectFCMBindings(Session? session, AccountId? accountId) async {
|
Future<void> injectFCMBindings(Session? session, AccountId? accountId) async {
|
||||||
try {
|
try {
|
||||||
requireCapability(session!, accountId!, [FirebaseCapability.fcmIdentifier]);
|
requireCapability(session!, accountId!, [FirebaseCapability.fcmIdentifier]);
|
||||||
log('BaseController::injectFCMBindings: fcmAvailable = ${AppConfig.fcmAvailable}');
|
log('$runtimeType::injectFCMBindings: fcmAvailable = ${AppConfig.fcmAvailable}');
|
||||||
if (AppConfig.fcmAvailable) {
|
if (AppConfig.fcmAvailable) {
|
||||||
final mapEnvData = Map<String, String>.from(dotenv.env);
|
final mapEnvData = Map<String, String>.from(dotenv.env);
|
||||||
await AppUtils.loadFcmConfigFileToEnv(currentMapEnvData: mapEnvData);
|
await AppUtils.loadFcmConfigFileToEnv(currentMapEnvData: mapEnvData);
|
||||||
@@ -285,7 +285,7 @@ abstract class BaseController extends GetxController
|
|||||||
throw NotSupportFCMException();
|
throw NotSupportFCMException();
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
logError('BaseController::injectFCMBindings(): exception: $e');
|
logError('$runtimeType::injectFCMBindings(): exception: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,7 +348,7 @@ abstract class BaseController extends GetxController
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> clearDataAndGoToLoginPage() async {
|
Future<void> clearDataAndGoToLoginPage() async {
|
||||||
log('BaseController::clearDataAndGoToLoginPage:');
|
log('$runtimeType::clearDataAndGoToLoginPage:');
|
||||||
await clearAllData();
|
await clearAllData();
|
||||||
goToLogin();
|
goToLogin();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,18 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
|
import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||||
import 'package:model/extensions/session_extension.dart';
|
import 'package:model/account/password.dart';
|
||||||
|
import 'package:model/oidc/oidc_configuration.dart';
|
||||||
|
import 'package:model/oidc/token_oidc.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
|
||||||
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
|
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
|
||||||
import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart';
|
import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart';
|
import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart';
|
import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart';
|
import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/domain/state/update_authentication_account_state.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_interactors_bindings.dart';
|
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_interactors_bindings.dart';
|
||||||
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
import 'package:tmail_ui_user/main/error/capability_validator.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
|
||||||
@@ -26,16 +28,18 @@ import 'package:tmail_ui_user/main/utils/message_toast_utils.dart';
|
|||||||
abstract class ReloadableController extends BaseController {
|
abstract class ReloadableController extends BaseController {
|
||||||
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
|
final GetSessionInteractor _getSessionInteractor = Get.find<GetSessionInteractor>();
|
||||||
final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor = Get.find<GetAuthenticatedAccountInteractor>();
|
final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor = Get.find<GetAuthenticatedAccountInteractor>();
|
||||||
final UpdateAuthenticationAccountInteractor _updateAuthenticationAccountInteractor = Get.find<UpdateAuthenticationAccountInteractor>();
|
final UpdateAccountCacheInteractor _updateAccountCacheInteractor = Get.find<UpdateAccountCacheInteractor>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void handleFailureViewState(Failure failure) {
|
void handleFailureViewState(Failure failure) {
|
||||||
if (failure is GetCredentialFailure ||
|
if (failure is GetCredentialFailure ||
|
||||||
failure is GetStoredTokenOidcFailure ||
|
failure is GetStoredTokenOidcFailure ||
|
||||||
failure is GetAuthenticatedAccountFailure) {
|
failure is GetAuthenticatedAccountFailure ||
|
||||||
log('ReloadableController::handleFailureViewState(): failure: $failure');
|
failure is UpdateAccountCacheFailure) {
|
||||||
|
logError('$runtimeType::handleFailureViewState():Failure = $failure');
|
||||||
goToLogin();
|
goToLogin();
|
||||||
} else if (failure is GetSessionFailure) {
|
} else if (failure is GetSessionFailure) {
|
||||||
|
logError('$runtimeType::handleFailureViewState():Failure = $failure');
|
||||||
_handleGetSessionFailure(failure.exception);
|
_handleGetSessionFailure(failure.exception);
|
||||||
} else {
|
} else {
|
||||||
super.handleFailureViewState(failure);
|
super.handleFailureViewState(failure);
|
||||||
@@ -45,11 +49,26 @@ abstract class ReloadableController extends BaseController {
|
|||||||
@override
|
@override
|
||||||
void handleSuccessViewState(Success success) {
|
void handleSuccessViewState(Success success) {
|
||||||
if (success is GetCredentialViewState) {
|
if (success is GetCredentialViewState) {
|
||||||
_handleGetCredentialSuccess(success);
|
log('$runtimeType::handleSuccessViewState:Success = ${success.runtimeType}');
|
||||||
} else if (success is GetSessionSuccess) {
|
_setDataToInterceptors(
|
||||||
_handleGetSessionSuccess(success);
|
baseUrl: success.baseUrl.origin,
|
||||||
|
userName: success.userName,
|
||||||
|
password: success.password);
|
||||||
|
getSessionAction();
|
||||||
} else if (success is GetStoredTokenOidcSuccess) {
|
} else if (success is GetStoredTokenOidcSuccess) {
|
||||||
_handleGetStoredTokenOIDCSuccess(success);
|
log('$runtimeType::handleSuccessViewState:Success = ${success.runtimeType}');
|
||||||
|
_setDataToInterceptors(
|
||||||
|
baseUrl: success.baseUrl.toString(),
|
||||||
|
tokenOIDC: success.tokenOidc,
|
||||||
|
oidcConfiguration: success.oidcConfiguration);
|
||||||
|
getSessionAction();
|
||||||
|
} else if (success is GetSessionSuccess) {
|
||||||
|
log('$runtimeType::handleSuccessViewState:Success = ${success.runtimeType}');
|
||||||
|
updateAccountCache(success.session);
|
||||||
|
} else if (success is UpdateAccountCacheSuccess) {
|
||||||
|
log('$runtimeType::handleSuccessViewState:Success = ${success.runtimeType}');
|
||||||
|
dynamicUrlInterceptors.changeBaseUrl(success.apiUrl);
|
||||||
|
handleReloaded(success.session);
|
||||||
} else {
|
} else {
|
||||||
super.handleSuccessViewState(success);
|
super.handleSuccessViewState(success);
|
||||||
}
|
}
|
||||||
@@ -66,22 +85,25 @@ abstract class ReloadableController extends BaseController {
|
|||||||
consumeState(_getAuthenticatedAccountInteractor.execute());
|
consumeState(_getAuthenticatedAccountInteractor.execute());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setUpInterceptors(GetCredentialViewState credentialViewState) {
|
void _setDataToInterceptors({
|
||||||
dynamicUrlInterceptors.setJmapUrl(credentialViewState.baseUrl.origin);
|
required String baseUrl,
|
||||||
dynamicUrlInterceptors.changeBaseUrl(credentialViewState.baseUrl.origin);
|
UserName? userName,
|
||||||
authorizationInterceptors.setBasicAuthorization(
|
Password? password,
|
||||||
credentialViewState.userName,
|
TokenOIDC? tokenOIDC,
|
||||||
credentialViewState.password,
|
OIDCConfiguration? oidcConfiguration
|
||||||
);
|
}) {
|
||||||
authorizationIsolateInterceptors.setBasicAuthorization(
|
dynamicUrlInterceptors.setJmapUrl(baseUrl);
|
||||||
credentialViewState.userName,
|
dynamicUrlInterceptors.changeBaseUrl(baseUrl);
|
||||||
credentialViewState.password,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleGetCredentialSuccess(GetCredentialViewState credentialViewState) {
|
if (userName != null && password != null) {
|
||||||
_setUpInterceptors(credentialViewState);
|
authorizationInterceptors.setBasicAuthorization(userName, password);
|
||||||
getSessionAction();
|
authorizationIsolateInterceptors.setBasicAuthorization(userName, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenOIDC != null && oidcConfiguration != null) {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(newToken: tokenOIDC, newConfig: oidcConfiguration);
|
||||||
|
authorizationIsolateInterceptors.setTokenAndAuthorityOidc(newToken: tokenOIDC, newConfig: oidcConfiguration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSessionAction() {
|
void getSessionAction() {
|
||||||
@@ -98,50 +120,18 @@ abstract class ReloadableController extends BaseController {
|
|||||||
clearDataAndGoToLoginPage();
|
clearDataAndGoToLoginPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleGetSessionSuccess(GetSessionSuccess success) {
|
|
||||||
final session = success.session;
|
|
||||||
final personalAccount = session.personalAccount;
|
|
||||||
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
|
|
||||||
if (apiUrl.isNotEmpty) {
|
|
||||||
dynamicUrlInterceptors.changeBaseUrl(apiUrl);
|
|
||||||
updateAuthenticationAccount(session, personalAccount.accountId, session.username);
|
|
||||||
handleReloaded(session);
|
|
||||||
} else {
|
|
||||||
clearDataAndGoToLoginPage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleReloaded(Session session) {}
|
void handleReloaded(Session session) {}
|
||||||
|
|
||||||
void _handleGetStoredTokenOIDCSuccess(GetStoredTokenOidcSuccess tokenOidcSuccess) {
|
|
||||||
_setUpInterceptorsOidc(tokenOidcSuccess);
|
|
||||||
getSessionAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _setUpInterceptorsOidc(GetStoredTokenOidcSuccess tokenOidcSuccess) {
|
|
||||||
dynamicUrlInterceptors.setJmapUrl(tokenOidcSuccess.baseUrl.toString());
|
|
||||||
dynamicUrlInterceptors.changeBaseUrl(tokenOidcSuccess.baseUrl.toString());
|
|
||||||
authorizationInterceptors.setTokenAndAuthorityOidc(
|
|
||||||
newToken: tokenOidcSuccess.tokenOidc,
|
|
||||||
newConfig: tokenOidcSuccess.oidcConfiguration);
|
|
||||||
authorizationIsolateInterceptors.setTokenAndAuthorityOidc(
|
|
||||||
newToken: tokenOidcSuccess.tokenOidc,
|
|
||||||
newConfig: tokenOidcSuccess.oidcConfiguration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void injectVacationBindings(Session? session, AccountId? accountId) {
|
void injectVacationBindings(Session? session, AccountId? accountId) {
|
||||||
try {
|
try {
|
||||||
requireCapability(session!, accountId!, [CapabilityIdentifier.jmapVacationResponse]);
|
requireCapability(session!, accountId!, [CapabilityIdentifier.jmapVacationResponse]);
|
||||||
VacationInteractorsBindings().dependencies();
|
VacationInteractorsBindings().dependencies();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
logError('ReloadableController::injectVacationBindings(): exception: $e');
|
logError('$runtimeType::injectVacationBindings(): exception: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) {
|
void updateAccountCache(Session session) {
|
||||||
final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl);
|
consumeState(_updateAccountCacheInteractor.execute(session));
|
||||||
if (apiUrl.isNotEmpty) {
|
|
||||||
consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,8 @@ import 'package:core/utils/platform_info.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_downloader/flutter_downloader.dart';
|
import 'package:flutter_downloader/flutter_downloader.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||||
import 'package:model/account/personal_account.dart';
|
|
||||||
import 'package:model/email/email_content.dart';
|
import 'package:model/email/email_content.dart';
|
||||||
import 'package:model/email/email_content_type.dart';
|
import 'package:model/email/email_content_type.dart';
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
@@ -21,12 +18,11 @@ import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_email_cac
|
|||||||
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_url_cache_interactor.dart';
|
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_url_cache_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_username_interactor.dart';
|
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_login_username_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_search_cache_interactor.dart';
|
import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_search_cache_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/preview_email_arguments.dart';
|
|
||||||
import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_receiver.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
|
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
||||||
|
|
||||||
class HomeController extends ReloadableController {
|
class HomeController extends ReloadableController {
|
||||||
final CleanupEmailCacheInteractor _cleanupEmailCacheInteractor;
|
final CleanupEmailCacheInteractor _cleanupEmailCacheInteractor;
|
||||||
@@ -35,6 +31,8 @@ class HomeController extends ReloadableController {
|
|||||||
final CleanupRecentLoginUrlCacheInteractor _cleanupRecentLoginUrlCacheInteractor;
|
final CleanupRecentLoginUrlCacheInteractor _cleanupRecentLoginUrlCacheInteractor;
|
||||||
final CleanupRecentLoginUsernameCacheInteractor _cleanupRecentLoginUsernameCacheInteractor;
|
final CleanupRecentLoginUsernameCacheInteractor _cleanupRecentLoginUsernameCacheInteractor;
|
||||||
|
|
||||||
|
IOSNotificationManager? _iosNotificationManager;
|
||||||
|
|
||||||
HomeController(
|
HomeController(
|
||||||
this._cleanupEmailCacheInteractor,
|
this._cleanupEmailCacheInteractor,
|
||||||
this._emailReceiveManager,
|
this._emailReceiveManager,
|
||||||
@@ -43,9 +41,6 @@ class HomeController extends ReloadableController {
|
|||||||
this._cleanupRecentLoginUsernameCacheInteractor,
|
this._cleanupRecentLoginUsernameCacheInteractor,
|
||||||
);
|
);
|
||||||
|
|
||||||
PersonalAccount? currentAccount;
|
|
||||||
EmailId? _emailIdPreview;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
if (PlatformInfo.isMobile) {
|
if (PlatformInfo.isMobile) {
|
||||||
@@ -53,7 +48,7 @@ class HomeController extends ReloadableController {
|
|||||||
_registerReceivingSharingIntent();
|
_registerReceivingSharingIntent();
|
||||||
}
|
}
|
||||||
if (PlatformInfo.isIOS) {
|
if (PlatformInfo.isIOS) {
|
||||||
_handleIOSDataMessage();
|
_registerNotificationClickOnIOS();
|
||||||
}
|
}
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
@@ -66,20 +61,9 @@ class HomeController extends ReloadableController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void handleReloaded(Session session) {
|
void handleReloaded(Session session) {
|
||||||
if (_emailIdPreview != null) {
|
pushAndPopAll(
|
||||||
popAndPush(
|
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
|
||||||
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
|
arguments: session);
|
||||||
arguments: PreviewEmailArguments(
|
|
||||||
session: session,
|
|
||||||
emailId: _emailIdPreview!
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
popAndPush(
|
|
||||||
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
|
|
||||||
arguments: session
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initFlutterDownloader() {
|
void _initFlutterDownloader() {
|
||||||
@@ -90,7 +74,7 @@ class HomeController extends ReloadableController {
|
|||||||
|
|
||||||
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {}
|
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {}
|
||||||
|
|
||||||
void _cleanupCache() async {
|
Future<void> _cleanupCache() async {
|
||||||
await HiveCacheConfig.instance.onUpgradeDatabase(cachingManager);
|
await HiveCacheConfig.instance.onUpgradeDatabase(cachingManager);
|
||||||
|
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
@@ -98,7 +82,7 @@ class HomeController extends ReloadableController {
|
|||||||
_cleanupRecentSearchCacheInteractor.execute(RecentSearchCleanupRule()),
|
_cleanupRecentSearchCacheInteractor.execute(RecentSearchCleanupRule()),
|
||||||
_cleanupRecentLoginUrlCacheInteractor.execute(RecentLoginUrlCleanupRule()),
|
_cleanupRecentLoginUrlCacheInteractor.execute(RecentLoginUrlCleanupRule()),
|
||||||
_cleanupRecentLoginUsernameCacheInteractor.execute(RecentLoginUsernameCleanupRule()),
|
_cleanupRecentLoginUsernameCacheInteractor.execute(RecentLoginUsernameCleanupRule()),
|
||||||
]).then((value) => getAuthenticatedAccountAction());
|
], eagerError: true).then((_) => getAuthenticatedAccountAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerReceivingSharingIntent() {
|
void _registerReceivingSharingIntent() {
|
||||||
@@ -117,17 +101,8 @@ class HomeController extends ReloadableController {
|
|||||||
_emailReceiveManager.receivingFileSharingStream.listen(_emailReceiveManager.setPendingFileInfo);
|
_emailReceiveManager.receivingFileSharingStream.listen(_emailReceiveManager.setPendingFileInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _handleIOSDataMessage() async {
|
void _registerNotificationClickOnIOS() {
|
||||||
if (Get.arguments is EmailId) {
|
_iosNotificationManager = getBinding<IOSNotificationManager>();
|
||||||
_emailIdPreview = Get.arguments;
|
_iosNotificationManager?.listenClickNotification();
|
||||||
} else {
|
|
||||||
final notificationInfo = await FcmReceiver.instance.getIOSInitialNotificationInfo();
|
|
||||||
if (notificationInfo != null && notificationInfo.containsKey('email_id')) {
|
|
||||||
final emailId = notificationInfo['email_id'] as String?;
|
|
||||||
if (emailId?.isNotEmpty == true) {
|
|
||||||
_emailIdPreview = EmailId(Id(emailId!));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,11 +48,11 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
_token = newToken;
|
_token = newToken;
|
||||||
_configOIDC = newConfig;
|
_configOIDC = newConfig;
|
||||||
_authenticationType = AuthenticationType.oidc;
|
_authenticationType = AuthenticationType.oidc;
|
||||||
log('AuthorizationInterceptors::setTokenAndAuthorityOidc: INITIAL_TOKEN = ${newToken?.token} | EXPIRED_TIME = ${newToken?.expiredTime}');
|
log('AuthorizationInterceptors::setTokenAndAuthorityOidc: TokenId = ${newToken?.tokenIdHash}');
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateNewToken(TokenOIDC newToken) {
|
void _updateNewToken(TokenOIDC newToken) {
|
||||||
log('AuthorizationInterceptors::_updateNewToken: NEW_TOKEN = ${newToken.token} | EXPIRED_TIME = ${newToken.expiredTime}');
|
log('AuthorizationInterceptors::_updateNewToken: TokenId = ${newToken.tokenIdHash}');
|
||||||
_token = newToken;
|
_token = newToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,13 +76,13 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
case AuthenticationType.none:
|
case AuthenticationType.none:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
log('AuthorizationInterceptors::onRequest(): URL = ${options.uri} | HEADER = ${options.headers} | DATA = ${options.data} | METHOD = ${options.method}');
|
log('AuthorizationInterceptors::onRequest(): URL = ${options.uri} | DATA = ${options.data}');
|
||||||
super.onRequest(options, handler);
|
super.onRequest(options, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onError(DioError err, ErrorInterceptorHandler handler) async {
|
void onError(DioError err, ErrorInterceptorHandler handler) async {
|
||||||
logError('AuthorizationInterceptors::onError(): TOKEN = ${_token?.expiredTime} | DIO_ERROR = $err | METHOD = ${err.requestOptions.method}');
|
logError('AuthorizationInterceptors::onError(): DIO_ERROR = $err');
|
||||||
try {
|
try {
|
||||||
final requestOptions = err.requestOptions;
|
final requestOptions = err.requestOptions;
|
||||||
final extraInRequest = requestOptions.extra;
|
final extraInRequest = requestOptions.extra;
|
||||||
@@ -92,24 +92,29 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
responseStatusCode: err.response?.statusCode,
|
responseStatusCode: err.response?.statusCode,
|
||||||
tokenOIDC: _token
|
tokenOIDC: _token
|
||||||
)) {
|
)) {
|
||||||
log('AuthorizationInterceptors::onError:_validateToRefreshToken');
|
log('AuthorizationInterceptors::onError: Perform get New Token');
|
||||||
final newTokenOidc = PlatformInfo.isIOS
|
final newTokenOidc = PlatformInfo.isIOS
|
||||||
? await _handleRefreshTokenOnIOSPlatform()
|
? await _getNewTokenForIOSPlatform()
|
||||||
: await _handleRefreshTokenOnOtherPlatform();
|
: await _getNewTokenForOtherPlatform();
|
||||||
|
|
||||||
if (newTokenOidc.token == _token?.token) {
|
if (newTokenOidc.token == _token?.token) {
|
||||||
log('AuthorizationInterceptors::onError: TokenOIDC duplicated');
|
log('AuthorizationInterceptors::onError: Token duplicated');
|
||||||
return super.onError(err, handler);
|
return super.onError(err, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateNewToken(newTokenOidc);
|
_updateNewToken(newTokenOidc);
|
||||||
|
|
||||||
|
final personalAccount = await _updateCurrentAccount(tokenOIDC: newTokenOidc);
|
||||||
|
|
||||||
|
if (PlatformInfo.isIOS) {
|
||||||
|
await _iosSharingManager.saveKeyChainSharingSession(personalAccount);
|
||||||
|
}
|
||||||
|
|
||||||
isRetryRequest = true;
|
isRetryRequest = true;
|
||||||
} else if (validateToRetryTheRequestWithNewToken(
|
} else if (validateToRetryTheRequestWithNewToken(
|
||||||
authHeader: requestOptions.headers[HttpHeaders.authorizationHeader],
|
authHeader: requestOptions.headers[HttpHeaders.authorizationHeader],
|
||||||
tokenOIDC: _token
|
tokenOIDC: _token
|
||||||
)) {
|
)) {
|
||||||
log('AuthorizationInterceptors::onError:validateToRetryTheRequestWithNewToken');
|
log('AuthorizationInterceptors::onError: Request using old token');
|
||||||
isRetryRequest = true;
|
isRetryRequest = true;
|
||||||
} else {
|
} else {
|
||||||
return super.onError(err, handler);
|
return super.onError(err, handler);
|
||||||
@@ -117,7 +122,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
|
|
||||||
if (isRetryRequest) {
|
if (isRetryRequest) {
|
||||||
if (extraInRequest.containsKey(FileUploader.uploadAttachmentExtraKey)) {
|
if (extraInRequest.containsKey(FileUploader.uploadAttachmentExtraKey)) {
|
||||||
log('AuthorizationInterceptors::onError: Perform upload attachment request');
|
log('AuthorizationInterceptors::onError: Retry upload request with TokenId = ${_token?.tokenIdHash}');
|
||||||
final uploadExtra = extraInRequest[FileUploader.uploadAttachmentExtraKey];
|
final uploadExtra = extraInRequest[FileUploader.uploadAttachmentExtraKey];
|
||||||
|
|
||||||
requestOptions.headers[HttpHeaders.authorizationHeader] = _getTokenAsBearerHeader(_token!.token);
|
requestOptions.headers[HttpHeaders.authorizationHeader] = _getTokenAsBearerHeader(_token!.token);
|
||||||
@@ -136,7 +141,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
|
|
||||||
return handler.resolve(response);
|
return handler.resolve(response);
|
||||||
} else {
|
} else {
|
||||||
log('AuthorizationInterceptors::onError: Perform normal request');
|
log('AuthorizationInterceptors::onError: Retry request with TokenId = ${_token?.tokenIdHash}');
|
||||||
requestOptions.headers[HttpHeaders.authorizationHeader] = _getTokenAsBearerHeader(_token!.token);
|
requestOptions.headers[HttpHeaders.authorizationHeader] = _getTokenAsBearerHeader(_token!.token);
|
||||||
|
|
||||||
final response = await _dio.fetch(requestOptions);
|
final response = await _dio.fetch(requestOptions);
|
||||||
@@ -231,13 +236,11 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
|
|
||||||
Future<TokenOIDC?> _getTokenInKeychain(TokenOIDC currentTokenOidc) async {
|
Future<TokenOIDC?> _getTokenInKeychain(TokenOIDC currentTokenOidc) async {
|
||||||
final currentAccount = await _accountCacheManager.getCurrentAccount();
|
final currentAccount = await _accountCacheManager.getCurrentAccount();
|
||||||
log('AuthorizationInterceptors::_getTokenInKeychain:currentAccount: $currentAccount');
|
|
||||||
if (currentAccount.accountId == null) {
|
if (currentAccount.accountId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final keychainSharingSession = await _iosSharingManager.getKeychainSharingSession(currentAccount.accountId!);
|
final keychainSharingSession = await _iosSharingManager.getKeychainSharingSession(currentAccount.accountId!);
|
||||||
log('AuthorizationInterceptors::_getTokenInKeychain:keychainSharingSession: $keychainSharingSession');
|
|
||||||
if (keychainSharingSession == null) {
|
if (keychainSharingSession == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -250,36 +253,29 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TokenOIDC> _invokeRefreshTokenFromServer() async {
|
Future<TokenOIDC> _invokeRefreshTokenFromServer() {
|
||||||
final newToken = await _authenticationClient.refreshingTokensOIDC(
|
log('AuthorizationInterceptors::_invokeRefreshTokenFromServer:');
|
||||||
|
return _authenticationClient.refreshingTokensOIDC(
|
||||||
_configOIDC!.clientId,
|
_configOIDC!.clientId,
|
||||||
_configOIDC!.redirectUrl,
|
_configOIDC!.redirectUrl,
|
||||||
_configOIDC!.discoveryUrl,
|
_configOIDC!.discoveryUrl,
|
||||||
_configOIDC!.scopes,
|
_configOIDC!.scopes,
|
||||||
_token!.refreshToken
|
_token!.refreshToken
|
||||||
);
|
);
|
||||||
log('AuthorizationInterceptors::_invokeRefreshTokenFromServer:newToken: $newToken');
|
|
||||||
return newToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TokenOIDC> _handleRefreshTokenOnIOSPlatform() async {
|
Future<TokenOIDC> _getNewTokenForIOSPlatform() async {
|
||||||
final keychainToken = await _getTokenInKeychain(_token!);
|
final tokenInKeychain = await _getTokenInKeychain(_token!);
|
||||||
|
log('AuthorizationInterceptors::_handleRefreshTokenOnIOSPlatform: KeychainTokenId = ${tokenInKeychain?.tokenIdHash} | isTokenExpired = ${_isTokenExpired(tokenInKeychain)}');
|
||||||
if (keychainToken == null) {
|
if (tokenInKeychain == null || _isTokenExpired(tokenInKeychain)) {
|
||||||
final newToken = await _invokeRefreshTokenFromServer();
|
return _invokeRefreshTokenFromServer();
|
||||||
final newAccount = await _updateCurrentAccount(tokenOIDC: newToken);
|
|
||||||
await _iosSharingManager.saveKeyChainSharingSession(newAccount);
|
|
||||||
return newToken;
|
|
||||||
} else {
|
} else {
|
||||||
await _updateCurrentAccount(tokenOIDC: keychainToken);
|
return tokenInKeychain;
|
||||||
return keychainToken;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<TokenOIDC> _handleRefreshTokenOnOtherPlatform() async {
|
Future<TokenOIDC> _getNewTokenForOtherPlatform() {
|
||||||
final newToken = await _invokeRefreshTokenFromServer();
|
return _invokeRefreshTokenFromServer();
|
||||||
await _updateCurrentAccount(tokenOIDC: newToken);
|
|
||||||
return newToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
import 'package:core/presentation/state/failure.dart';
|
import 'package:core/presentation/state/failure.dart';
|
||||||
import 'package:core/presentation/state/success.dart';
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
|
|
||||||
class UpdateAuthenticationAccountLoading extends LoadingState {}
|
class UpdatingAccountCache extends LoadingState {}
|
||||||
|
|
||||||
class UpdateAuthenticationAccountSuccess extends UIState {}
|
class UpdateAccountCacheSuccess extends UIState {
|
||||||
|
final Session session;
|
||||||
|
final String apiUrl;
|
||||||
|
|
||||||
class UpdateAuthenticationAccountFailure extends FeatureFailure {
|
UpdateAccountCacheSuccess({
|
||||||
|
required this.session,
|
||||||
|
required this.apiUrl});
|
||||||
|
|
||||||
UpdateAuthenticationAccountFailure(dynamic exception) : super(exception: exception);
|
@override
|
||||||
|
List<Object?> get props => [session, apiUrl];
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateAccountCacheFailure extends FeatureFailure {
|
||||||
|
|
||||||
|
UpdateAccountCacheFailure(dynamic exception) : super(exception: exception);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||||
|
import 'package:model/account/authentication_type.dart';
|
||||||
|
import 'package:model/account/personal_account.dart';
|
||||||
|
import 'package:model/extensions/personal_account_extension.dart';
|
||||||
|
import 'package:model/extensions/session_extension.dart';
|
||||||
|
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/domain/state/update_authentication_account_state.dart';
|
||||||
|
|
||||||
|
class UpdateAccountCacheInteractor {
|
||||||
|
final AccountRepository _accountRepository;
|
||||||
|
final CredentialRepository _credentialRepository;
|
||||||
|
|
||||||
|
UpdateAccountCacheInteractor(
|
||||||
|
this._accountRepository,
|
||||||
|
this._credentialRepository);
|
||||||
|
|
||||||
|
Stream<Either<Failure, Success>> execute(Session session) async* {
|
||||||
|
try{
|
||||||
|
yield Right(UpdatingAccountCache());
|
||||||
|
|
||||||
|
final futureValue = await Future.wait([
|
||||||
|
_credentialRepository.getBaseUrl(),
|
||||||
|
_accountRepository.getCurrentAccount(),
|
||||||
|
], eagerError: true);
|
||||||
|
|
||||||
|
final baseUrl = futureValue[0] as Uri;
|
||||||
|
final currentAccount = futureValue[1] as PersonalAccount;
|
||||||
|
final apiUrl = session.getQualifiedApiUrl(
|
||||||
|
baseUrl: currentAccount.authenticationType == AuthenticationType.basic
|
||||||
|
? baseUrl.origin
|
||||||
|
: baseUrl.toString());
|
||||||
|
|
||||||
|
await _accountRepository.setCurrentAccount(
|
||||||
|
currentAccount.fromAccount(
|
||||||
|
accountId: session.accountId,
|
||||||
|
apiUrl: apiUrl,
|
||||||
|
userName: session.username
|
||||||
|
));
|
||||||
|
|
||||||
|
yield Right(UpdateAccountCacheSuccess(
|
||||||
|
session: session,
|
||||||
|
apiUrl: apiUrl));
|
||||||
|
} catch(e) {
|
||||||
|
yield Left(UpdateAccountCacheFailure(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import 'package:core/presentation/state/failure.dart';
|
|
||||||
import 'package:core/presentation/state/success.dart';
|
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
|
||||||
import 'package:model/extensions/personal_account_extension.dart';
|
|
||||||
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
|
|
||||||
import 'package:tmail_ui_user/features/login/domain/state/update_authentication_account_state.dart';
|
|
||||||
|
|
||||||
class UpdateAuthenticationAccountInteractor {
|
|
||||||
final AccountRepository _accountRepository;
|
|
||||||
|
|
||||||
UpdateAuthenticationAccountInteractor(this._accountRepository);
|
|
||||||
|
|
||||||
Stream<Either<Failure, Success>> execute(AccountId accountId, String apiUrl, UserName userName) async* {
|
|
||||||
try{
|
|
||||||
yield Right(UpdateAuthenticationAccountLoading());
|
|
||||||
final currentAccount = await _accountRepository.getCurrentAccount();
|
|
||||||
await _accountRepository.setCurrentAccount(
|
|
||||||
currentAccount.fromAccount(
|
|
||||||
accountId: accountId,
|
|
||||||
apiUrl: apiUrl,
|
|
||||||
userName: userName
|
|
||||||
)
|
|
||||||
);
|
|
||||||
yield Right(UpdateAuthenticationAccountSuccess());
|
|
||||||
} catch(e) {
|
|
||||||
yield Left(UpdateAuthenticationAccountFailure(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+33
-31
@@ -94,7 +94,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/comp
|
|||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/preview_email_arguments.dart';
|
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/refresh_action_view_event.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/refresh_action_view_event.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart';
|
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart';
|
||||||
@@ -153,6 +152,7 @@ import 'package:tmail_ui_user/main/routes/navigation_router.dart';
|
|||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
|
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class MailboxDashBoardController extends ReloadableController {
|
class MailboxDashBoardController extends ReloadableController {
|
||||||
@@ -197,6 +197,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
GetMailboxStateToRefreshInteractor? _getMailboxStateToRefreshInteractor;
|
GetMailboxStateToRefreshInteractor? _getMailboxStateToRefreshInteractor;
|
||||||
DeleteMailboxStateToRefreshInteractor? _deleteMailboxStateToRefreshInteractor;
|
DeleteMailboxStateToRefreshInteractor? _deleteMailboxStateToRefreshInteractor;
|
||||||
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
GetAutoCompleteInteractor? _getAutoCompleteInteractor;
|
||||||
|
IOSNotificationManager? _iosNotificationManager;
|
||||||
|
|
||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
final selectedMailbox = Rxn<PresentationMailbox>();
|
final selectedMailbox = Rxn<PresentationMailbox>();
|
||||||
@@ -234,6 +235,8 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
late StreamSubscription _emailContentStreamSubscription;
|
late StreamSubscription _emailContentStreamSubscription;
|
||||||
late StreamSubscription _fileReceiveManagerStreamSubscription;
|
late StreamSubscription _fileReceiveManagerStreamSubscription;
|
||||||
|
|
||||||
|
StreamSubscription? _currentEmailIdInNotificationIOSStreamSubscription;
|
||||||
|
|
||||||
final StreamController<Either<Failure, Success>> _progressStateController =
|
final StreamController<Either<Failure, Success>> _progressStateController =
|
||||||
StreamController<Either<Failure, Success>>.broadcast();
|
StreamController<Either<Failure, Success>>.broadcast();
|
||||||
Stream<Either<Failure, Success>> get progressState => _progressStateController.stream;
|
Stream<Either<Failure, Success>> get progressState => _progressStateController.stream;
|
||||||
@@ -272,7 +275,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() async {
|
void onInit() {
|
||||||
_registerStreamListener();
|
_registerStreamListener();
|
||||||
BackButtonInterceptor.add(_onBackButtonInterceptor, name: AppRoutes.dashboard);
|
BackButtonInterceptor.add(_onBackButtonInterceptor, name: AppRoutes.dashboard);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
@@ -286,6 +289,9 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
_registerPendingEmailAddress();
|
_registerPendingEmailAddress();
|
||||||
_registerPendingEmailContents();
|
_registerPendingEmailContents();
|
||||||
_registerPendingFileInfo();
|
_registerPendingFileInfo();
|
||||||
|
if (PlatformInfo.isIOS) {
|
||||||
|
_registerPendingCurrentEmailIdInNotification();
|
||||||
|
}
|
||||||
_handleArguments();
|
_handleArguments();
|
||||||
super.onReady();
|
super.onReady();
|
||||||
}
|
}
|
||||||
@@ -349,7 +355,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
} else if (success is GetAppDashboardConfigurationSuccess) {
|
} else if (success is GetAppDashboardConfigurationSuccess) {
|
||||||
appGridDashboardController.handleShowAppDashboard(success.linagoraApplications);
|
appGridDashboardController.handleShowAppDashboard(success.linagoraApplications);
|
||||||
} else if(success is GetEmailByIdSuccess) {
|
} else if(success is GetEmailByIdSuccess) {
|
||||||
_moveToEmailDetailedView(success);
|
openEmailDetailedView(success.email);
|
||||||
} else if (success is StoreSendingEmailSuccess) {
|
} else if (success is StoreSendingEmailSuccess) {
|
||||||
_handleStoreSendingEmailSuccess(success);
|
_handleStoreSendingEmailSuccess(success);
|
||||||
} else if (success is GetAllSendingEmailSuccess) {
|
} else if (success is GetAllSendingEmailSuccess) {
|
||||||
@@ -392,7 +398,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
} else if (failure is MarkAsMailboxReadFailure) {
|
} else if (failure is MarkAsMailboxReadFailure) {
|
||||||
_markAsReadMailboxFailure(failure);
|
_markAsReadMailboxFailure(failure);
|
||||||
} else if (failure is GetEmailByIdFailure) {
|
} else if (failure is GetEmailByIdFailure) {
|
||||||
_handleGetEmailDetailedFailed(failure);
|
_handleGetEmailByIdFailure(failure);
|
||||||
} else if (failure is RestoreDeletedMessageFailure) {
|
} else if (failure is RestoreDeletedMessageFailure) {
|
||||||
_handleRestoreDeletedMessageFailed();
|
_handleRestoreDeletedMessageFailed();
|
||||||
} else if (failure is GetRestoredDeletedMessageFailure) {
|
} else if (failure is GetRestoredDeletedMessageFailure) {
|
||||||
@@ -439,6 +445,18 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _registerPendingCurrentEmailIdInNotification() {
|
||||||
|
_iosNotificationManager = getBinding<IOSNotificationManager>();
|
||||||
|
_currentEmailIdInNotificationIOSStreamSubscription = _iosNotificationManager
|
||||||
|
?.pendingCurrentEmailIdInNotification.stream
|
||||||
|
.listen((emailId) {
|
||||||
|
if (emailId != null) {
|
||||||
|
_iosNotificationManager?.clearPendingCurrentEmailId();
|
||||||
|
_handleNotificationMessageFromEmailId(emailId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _registerStreamListener() {
|
void _registerStreamListener() {
|
||||||
progressState.listen((state) {
|
progressState.listen((state) {
|
||||||
viewStateMarkAsReadMailbox.value = state;
|
viewStateMarkAsReadMailbox.value = state;
|
||||||
@@ -455,7 +473,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
_notificationManager.localNotificationStream.listen(_handleClickLocalNotificationOnForeground);
|
_notificationManager.localNotificationStream.listen(_handleClickLocalNotificationOnForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleClickLocalNotificationOnTerminated() async {
|
Future<void> _handleClickNotificationOnAndroidInTerminated() async {
|
||||||
_notificationManager.activatedNotificationClickedOnTerminate = true;
|
_notificationManager.activatedNotificationClickedOnTerminate = true;
|
||||||
final notificationResponse = await _notificationManager.getCurrentNotificationResponse();
|
final notificationResponse = await _notificationManager.getCurrentNotificationResponse();
|
||||||
log('MailboxDashBoardController::_handleClickLocalNotificationOnTerminated():payload: ${notificationResponse?.payload}');
|
log('MailboxDashBoardController::_handleClickLocalNotificationOnTerminated():payload: ${notificationResponse?.payload}');
|
||||||
@@ -464,13 +482,11 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
|
|
||||||
void _handleArguments() {
|
void _handleArguments() {
|
||||||
final arguments = Get.arguments;
|
final arguments = Get.arguments;
|
||||||
log('MailboxDashBoardController::_getSessionCurrent(): arguments = $arguments');
|
log('MailboxDashBoardController::_handleArguments():Arguments = ${arguments.runtimeType}');
|
||||||
if (arguments is Session) {
|
if (arguments is Session) {
|
||||||
_handleSessionFromArguments(arguments);
|
_handleSessionFromArguments(arguments);
|
||||||
} else if (arguments is MailtoArguments) {
|
} else if (arguments is MailtoArguments) {
|
||||||
_handleMailtoURL(arguments);
|
_handleMailtoURL(arguments);
|
||||||
} else if (arguments is PreviewEmailArguments) {
|
|
||||||
_handleOpenEmailAction(arguments);
|
|
||||||
} else {
|
} else {
|
||||||
dispatchRoute(DashboardRoutes.thread);
|
dispatchRoute(DashboardRoutes.thread);
|
||||||
reload();
|
reload();
|
||||||
@@ -501,11 +517,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
|
|
||||||
void _handleSessionFromArguments(Session session) {
|
void _handleSessionFromArguments(Session session) {
|
||||||
log('MailboxDashBoardController::_handleSession:');
|
log('MailboxDashBoardController::_handleSession:');
|
||||||
updateAuthenticationAccount(
|
updateAccountCache(session);
|
||||||
session,
|
|
||||||
session.personalAccount.accountId,
|
|
||||||
session.username
|
|
||||||
);
|
|
||||||
|
|
||||||
_setUpComponentsFromSession(session);
|
_setUpComponentsFromSession(session);
|
||||||
|
|
||||||
@@ -513,15 +525,15 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
_handleComposerCache();
|
_handleComposerCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlatformInfo.isMobile && !_notificationManager.isNotificationClickedOnTerminate) {
|
if (PlatformInfo.isAndroid && !_notificationManager.isNotificationClickedOnTerminate) {
|
||||||
_handleClickLocalNotificationOnTerminated();
|
_handleClickNotificationOnAndroidInTerminated();
|
||||||
} else {
|
} else {
|
||||||
dispatchRoute(DashboardRoutes.thread);
|
dispatchRoute(DashboardRoutes.thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setUpComponentsFromSession(Session session) {
|
void _setUpComponentsFromSession(Session session) {
|
||||||
final currentAccountId = session.personalAccount.accountId;
|
final currentAccountId = session.accountId;
|
||||||
sessionCurrent = session;
|
sessionCurrent = session;
|
||||||
accountId.value = currentAccountId;
|
accountId.value = currentAccountId;
|
||||||
|
|
||||||
@@ -546,13 +558,6 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
_handleSessionFromArguments(arguments.session);
|
_handleSessionFromArguments(arguments.session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleOpenEmailAction(PreviewEmailArguments arguments) {
|
|
||||||
log('MailboxDashBoardController::_handleOpenEmailAction:arguments: $arguments');
|
|
||||||
dispatchRoute(DashboardRoutes.waiting);
|
|
||||||
_handleSessionFromArguments(arguments.session);
|
|
||||||
_handleNotificationMessageFromEmailId(arguments.emailId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _getVacationResponse() {
|
void _getVacationResponse() {
|
||||||
if (accountId.value != null && _getAllVacationInteractor != null) {
|
if (accountId.value != null && _getAllVacationInteractor != null) {
|
||||||
consumeState(_getAllVacationInteractor!.execute(accountId.value!));
|
consumeState(_getAllVacationInteractor!.execute(accountId.value!));
|
||||||
@@ -1794,14 +1799,7 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _moveToEmailDetailedView(GetEmailByIdSuccess success) {
|
void _handleGetEmailByIdFailure(GetEmailByIdFailure failure) {
|
||||||
log('MailboxDashBoardController::_moveToEmailDetailedView(): ${success.email}');
|
|
||||||
setSelectedEmail(success.email);
|
|
||||||
dispatchRoute(DashboardRoutes.emailDetailed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleGetEmailDetailedFailed(GetEmailByIdFailure failure) {
|
|
||||||
logError('MailboxDashBoardController::_handleGetEmailDetailedFailed(): $failure');
|
|
||||||
dispatchRoute(DashboardRoutes.thread);
|
dispatchRoute(DashboardRoutes.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2552,6 +2550,10 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
_emailReceiveManager.closeEmailReceiveManagerStream();
|
_emailReceiveManager.closeEmailReceiveManagerStream();
|
||||||
|
if (PlatformInfo.isIOS) {
|
||||||
|
_iosNotificationManager?.dispose();
|
||||||
|
_currentEmailIdInNotificationIOSStreamSubscription?.cancel();
|
||||||
|
}
|
||||||
_emailAddressStreamSubscription.cancel();
|
_emailAddressStreamSubscription.cancel();
|
||||||
_emailContentStreamSubscription.cancel();
|
_emailContentStreamSubscription.cancel();
|
||||||
_fileReceiveManagerStreamSubscription.cancel();
|
_fileReceiveManagerStreamSubscription.cancel();
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/router_arguments.dart';
|
|
||||||
|
|
||||||
class PreviewEmailArguments extends RouterArguments {
|
|
||||||
|
|
||||||
final Session session;
|
|
||||||
final EmailId emailId;
|
|
||||||
|
|
||||||
PreviewEmailArguments({required this.session, required this.emailId});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [session, emailId];
|
|
||||||
}
|
|
||||||
@@ -85,7 +85,7 @@ class ManageAccountDashBoardController extends ReloadableController {
|
|||||||
void handleReloaded(Session session) {
|
void handleReloaded(Session session) {
|
||||||
log('ManageAccountDashBoardController::handleReloaded:');
|
log('ManageAccountDashBoardController::handleReloaded:');
|
||||||
sessionCurrent = session;
|
sessionCurrent = session;
|
||||||
accountId.value = session.personalAccount.accountId;
|
accountId.value = session.accountId;
|
||||||
_bindingInteractorForMenuItemView(sessionCurrent, accountId.value);
|
_bindingInteractorForMenuItemView(sessionCurrent, accountId.value);
|
||||||
_getVacationResponse();
|
_getVacationResponse();
|
||||||
_getParametersRouter();
|
_getParametersRouter();
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ class SendingEmailWorker extends Worker {
|
|||||||
|
|
||||||
void _handleGetSessionSuccess(GetSessionSuccess success) async {
|
void _handleGetSessionSuccess(GetSessionSuccess success) async {
|
||||||
_currentSession = success.session;
|
_currentSession = success.session;
|
||||||
_currentAccountId = success.session.personalAccount.accountId;
|
_currentAccountId = success.session.accountId;
|
||||||
final apiUrl = success.session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors?.jmapUrl);
|
final apiUrl = success.session.getQualifiedApiUrl(baseUrl: _dynamicUrlInterceptors?.jmapUrl);
|
||||||
if (apiUrl.isNotEmpty && _currentSession != null && _currentAccountId != null) {
|
if (apiUrl.isNotEmpty && _currentSession != null && _currentAccountId != null) {
|
||||||
_dynamicUrlInterceptors?.changeBaseUrl(apiUrl);
|
_dynamicUrlInterceptors?.changeBaseUrl(apiUrl);
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ class FcmMessageController extends FcmBaseController {
|
|||||||
_dynamicUrlInterceptors?.changeBaseUrl(apiUrl);
|
_dynamicUrlInterceptors?.changeBaseUrl(apiUrl);
|
||||||
|
|
||||||
_pushActionFromRemoteMessageBackground(
|
_pushActionFromRemoteMessageBackground(
|
||||||
accountId: success.session.personalAccount.accountId,
|
accountId: success.session.accountId,
|
||||||
userName: success.session.username,
|
userName: success.session.username,
|
||||||
stateChange: stateChange,
|
stateChange: stateChange,
|
||||||
session: success.session);
|
session: success.session);
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
|
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/utils/broadcast_channel/broadcast_channel.dart';
|
import 'package:core/utils/broadcast_channel/broadcast_channel.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:tmail_ui_user/features/push_notification/presentation/controller/fcm_message_controller.dart';
|
import 'package:tmail_ui_user/features/push_notification/presentation/controller/fcm_message_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart';
|
import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||||
@@ -23,7 +21,6 @@ class FcmReceiver {
|
|||||||
|
|
||||||
static FcmReceiver get instance => _instance;
|
static FcmReceiver get instance => _instance;
|
||||||
|
|
||||||
static const notificationInteractionChannel = MethodChannel('notification_interaction_channel');
|
|
||||||
static const int MAX_COUNT_RETRY_TO_GET_FCM_TOKEN = 3;
|
static const int MAX_COUNT_RETRY_TO_GET_FCM_TOKEN = 3;
|
||||||
|
|
||||||
int _countRetryToGetFcmToken = 0;
|
int _countRetryToGetFcmToken = 0;
|
||||||
@@ -36,24 +33,11 @@ class FcmReceiver {
|
|||||||
if (PlatformInfo.isWeb) {
|
if (PlatformInfo.isWeb) {
|
||||||
_onMessageBroadcastChannel();
|
_onMessageBroadcastChannel();
|
||||||
await _requestNotificationPermissionOnWeb();
|
await _requestNotificationPermissionOnWeb();
|
||||||
} else if (PlatformInfo.isIOS) {
|
|
||||||
_setUpIOSNotificationInteraction();
|
|
||||||
await _onHandleFcmToken();
|
|
||||||
} else {
|
} else {
|
||||||
await _onHandleFcmToken();
|
await _onHandleFcmToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setUpIOSNotificationInteraction() {
|
|
||||||
notificationInteractionChannel.setMethodCallHandler((call) async {
|
|
||||||
log('FcmReceiver::_setUpIOSNotificationInteraction:notificationInteractionChannel: $call');
|
|
||||||
if (call.method == 'openEmail' && call.arguments is String) {
|
|
||||||
log('FcmReceiver::_setUpIOSNotificationInteraction:openEmail with id = ${call.arguments}');
|
|
||||||
FcmService.instance.handleOpenEmailFromNotification(call.arguments);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _requestNotificationPermissionOnWeb() async {
|
Future<void> _requestNotificationPermissionOnWeb() async {
|
||||||
NotificationSettings notificationSetting = await FirebaseMessaging.instance.getNotificationSettings();
|
NotificationSettings notificationSetting = await FirebaseMessaging.instance.getNotificationSettings();
|
||||||
log('FcmReceiver::_requestNotificationPermissionOnWeb: authorizationStatus = ${notificationSetting.authorizationStatus}');
|
log('FcmReceiver::_requestNotificationPermissionOnWeb: authorizationStatus = ${notificationSetting.authorizationStatus}');
|
||||||
@@ -118,18 +102,4 @@ class FcmReceiver {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>?> getIOSInitialNotificationInfo() async {
|
|
||||||
try {
|
|
||||||
final notificationInfo = await notificationInteractionChannel.invokeMethod('getInitialNotificationInfo');
|
|
||||||
log('FcmReceiver::getIOSInitialNotificationInfo:notificationInfo: $notificationInfo');
|
|
||||||
if (notificationInfo != null && notificationInfo is Map<String, dynamic>) {
|
|
||||||
return notificationInfo;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (e) {
|
|
||||||
logError('FcmReceiver::getIOSInitialNotificationInfo: Exception: $e');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,12 +4,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
|
||||||
import 'package:tmail_ui_user/features/push_notification/presentation/model/broadcast_message_event_data.dart';
|
import 'package:tmail_ui_user/features/push_notification/presentation/model/broadcast_message_event_data.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
|
||||||
import 'package:universal_html/html.dart' as html;
|
import 'package:universal_html/html.dart' as html;
|
||||||
|
|
||||||
class FcmService {
|
class FcmService {
|
||||||
@@ -63,14 +58,6 @@ class FcmService {
|
|||||||
fcmTokenStreamController = StreamController<String?>.broadcast();
|
fcmTokenStreamController = StreamController<String?>.broadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleOpenEmailFromNotification(String emailId) {
|
|
||||||
log('FcmService::handleOpenEmailFromNotification:emailId: $emailId');
|
|
||||||
popAndPush(
|
|
||||||
RouteUtils.generateNavigationRoute(AppRoutes.home),
|
|
||||||
arguments: EmailId(Id(emailId))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void closeStream() {
|
void closeStream() {
|
||||||
foregroundMessageStreamController?.close();
|
foregroundMessageStreamController?.close();
|
||||||
backgroundMessageStreamController?.close();
|
backgroundMessageStreamController?.close();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import 'package:tmail_ui_user/features/base/before_unload_manager.dart';
|
|||||||
import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart';
|
import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||||
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
|
import 'package:tmail_ui_user/main/utils/email_receive_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class CoreBindings extends Bindings {
|
class CoreBindings extends Bindings {
|
||||||
@@ -66,6 +67,9 @@ class CoreBindings extends Bindings {
|
|||||||
Get.put(PrintUtils());
|
Get.put(PrintUtils());
|
||||||
Get.put(ApplicationManager(Get.find<DeviceInfoPlugin>()));
|
Get.put(ApplicationManager(Get.find<DeviceInfoPlugin>()));
|
||||||
Get.put(BeforeUnloadManager());
|
Get.put(BeforeUnloadManager());
|
||||||
|
if (PlatformInfo.isIOS) {
|
||||||
|
Get.put(IOSNotificationManager());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _bindingIsolate() {
|
void _bindingIsolate() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_i
|
|||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_token_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_token_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
@@ -61,7 +61,9 @@ class CredentialBindings extends InteractorsBindings {
|
|||||||
Get.find<CredentialRepository>(),
|
Get.find<CredentialRepository>(),
|
||||||
Get.find<AccountRepository>()
|
Get.find<AccountRepository>()
|
||||||
));
|
));
|
||||||
Get.put(UpdateAuthenticationAccountInteractor(Get.find<AccountRepository>()));
|
Get.put(UpdateAccountCacheInteractor(
|
||||||
|
Get.find<AccountRepository>(),
|
||||||
|
Get.find<CredentialRepository>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||||
|
import 'package:model/model.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
|
class IOSNotificationManager {
|
||||||
|
|
||||||
|
static const _notificationInteractionChannel = MethodChannel('notification_interaction_channel');
|
||||||
|
|
||||||
|
static const String CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_FOREGROUND_OR_BACKGROUND = 'current_email_id_in_notification_click_when_app_foreground_or_background';
|
||||||
|
static const String CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED = 'current_email_id_in_notification_click_when_app_terminated';
|
||||||
|
|
||||||
|
BehaviorSubject<EmailId?> _pendingCurrentEmailIdInNotification = BehaviorSubject.seeded(null);
|
||||||
|
BehaviorSubject<EmailId?> get pendingCurrentEmailIdInNotification => _pendingCurrentEmailIdInNotification;
|
||||||
|
|
||||||
|
StreamSubscription<EmailId?>? _getCurrentEmailIdStreamSubscription;
|
||||||
|
|
||||||
|
void listenClickNotification() {
|
||||||
|
try {
|
||||||
|
_notificationInteractionChannel.setMethodCallHandler((methodCall) async {
|
||||||
|
log('IOSNotificationManager::listenClickNotification: $methodCall');
|
||||||
|
if (methodCall.method == CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_FOREGROUND_OR_BACKGROUND
|
||||||
|
&& methodCall.arguments != null) {
|
||||||
|
final emailId = EmailId(Id(methodCall.arguments));
|
||||||
|
setPendingCurrentEmailId(emailId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_getCurrentEmailIdStreamSubscription = Stream.fromFuture(_getCurrentEmailIdInNotificationClick()).listen((emailId) {
|
||||||
|
log('IOSNotificationManager::listenClickNotification:_getCurrentEmailIdInNotificationClick:EmailId = ${emailId?.asString}');
|
||||||
|
if (emailId != null) {
|
||||||
|
setPendingCurrentEmailId(emailId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
logError('IOSNotificationManager::listenClickNotification:Exception = $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<EmailId?> _getCurrentEmailIdInNotificationClick() async {
|
||||||
|
try {
|
||||||
|
log('IOSNotificationManager::_getCurrentEmailIdInNotificationClick: START');
|
||||||
|
final emailId = await _notificationInteractionChannel.invokeMethod<String?>(CURRENT_EMAIL_ID_IN_NOTIFICATION_CLICK_WHEN_APP_TERMINATED);
|
||||||
|
log('IOSNotificationManager::_getCurrentEmailIdInNotificationClick: END');
|
||||||
|
if (emailId?.isNotEmpty == true) {
|
||||||
|
return EmailId(Id(emailId!));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logError('IOSNotificationManager::getCurrentEmailIdInNotificationClick:Exception = $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPendingCurrentEmailId(EmailId emailId) async {
|
||||||
|
clearPendingCurrentEmailId();
|
||||||
|
_pendingCurrentEmailIdInNotification.add(emailId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearPendingCurrentEmailId() {
|
||||||
|
if(_pendingCurrentEmailIdInNotification.isClosed) {
|
||||||
|
_pendingCurrentEmailIdInNotification = BehaviorSubject.seeded(null);
|
||||||
|
} else {
|
||||||
|
_pendingCurrentEmailIdInNotification.add(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_pendingCurrentEmailIdInNotification.close();
|
||||||
|
_getCurrentEmailIdStreamSubscription?.cancel();
|
||||||
|
_getCurrentEmailIdStreamSubscription = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:core/utils/app_logger.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||||
|
import 'package:model/account/authentication_type.dart';
|
||||||
import 'package:model/account/personal_account.dart';
|
import 'package:model/account/personal_account.dart';
|
||||||
import 'package:model/oidc/token_oidc.dart';
|
import 'package:model/oidc/token_oidc.dart';
|
||||||
import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart';
|
import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart';
|
||||||
@@ -50,20 +50,22 @@ class IOSSharingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future saveKeyChainSharingSession(PersonalAccount personalAccount) async {
|
Future<void> saveKeyChainSharingSession(PersonalAccount personalAccount) async {
|
||||||
|
log('IOSSharingManager::saveKeyChainSharingSession: START');
|
||||||
try {
|
try {
|
||||||
if (!_validateToSaveKeychain(personalAccount)) {
|
if (!_validateToSaveKeychain(personalAccount)) {
|
||||||
logError('IOSSharingManager::saveKeyChainSharingSession: account is null');
|
logError('IOSSharingManager::saveKeyChainSharingSession: AccountId | Username | ApiUrl is NULL');
|
||||||
return Future.value(null);
|
return Future.value(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple2<TokenOIDC?, String?> authenticationInfo = await Future.wait(
|
TokenOIDC? tokenOIDC;
|
||||||
[
|
String? credentialInfo;
|
||||||
_getTokenOidc(tokeHashId: personalAccount.id),
|
|
||||||
_getCredentialAuthentication()
|
if (personalAccount.authenticationType == AuthenticationType.oidc) {
|
||||||
],
|
tokenOIDC = await _getTokenOidc(tokeHashId: personalAccount.id);
|
||||||
eagerError: true
|
} else {
|
||||||
).then((listValue) => Tuple2(listValue[0] as TokenOIDC?, listValue[1] as String?));
|
credentialInfo = await _getCredentialAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
final emailDeliveryState = await _getEmailDeliveryState(
|
final emailDeliveryState = await _getEmailDeliveryState(
|
||||||
accountId: personalAccount.accountId!,
|
accountId: personalAccount.accountId!,
|
||||||
@@ -84,13 +86,15 @@ class IOSSharingManager {
|
|||||||
apiUrl: personalAccount.apiUrl!,
|
apiUrl: personalAccount.apiUrl!,
|
||||||
emailState: emailState,
|
emailState: emailState,
|
||||||
emailDeliveryState: emailDeliveryState,
|
emailDeliveryState: emailDeliveryState,
|
||||||
tokenOIDC: authenticationInfo.value1,
|
tokenOIDC: tokenOIDC,
|
||||||
basicAuth: authenticationInfo.value2,
|
basicAuth: credentialInfo,
|
||||||
tokenEndpoint: tokenRecords?.tokenEndpoint,
|
tokenEndpoint: tokenRecords?.tokenEndpoint,
|
||||||
oidcScopes: tokenRecords?.scopes,
|
oidcScopes: tokenRecords?.scopes,
|
||||||
);
|
);
|
||||||
log('IOSSharingManager::_saveKeyChainSharingSession: $keychainSharingSession');
|
|
||||||
await _keychainSharingManager.save(keychainSharingSession);
|
await _keychainSharingManager.save(keychainSharingSession);
|
||||||
|
|
||||||
|
log('IOSSharingManager::_saveKeyChainSharingSession: COMPLETED');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('IOSSharingManager::_saveKeyChainSharingSession: Exception: $e');
|
logError('IOSSharingManager::_saveKeyChainSharingSession: Exception: $e');
|
||||||
}
|
}
|
||||||
@@ -111,9 +115,7 @@ class IOSSharingManager {
|
|||||||
|
|
||||||
Future<TokenOIDC?> _getTokenOidc({required String tokeHashId}) async {
|
Future<TokenOIDC?> _getTokenOidc({required String tokeHashId}) async {
|
||||||
try {
|
try {
|
||||||
final tokenOidc = await _tokenOidcCacheManager.getTokenOidc(tokeHashId);
|
return await _tokenOidcCacheManager.getTokenOidc(tokeHashId);
|
||||||
log('IOSSharingManager::_getTokenOidc:tokenOidc: $tokenOidc');
|
|
||||||
return tokenOidc;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('IOSSharingManager::_getTokenOidc:Exception: $e');
|
logError('IOSSharingManager::_getTokenOidc:Exception: $e');
|
||||||
return null;
|
return null;
|
||||||
@@ -123,7 +125,6 @@ class IOSSharingManager {
|
|||||||
Future<String?> _getCredentialAuthentication() async {
|
Future<String?> _getCredentialAuthentication() async {
|
||||||
try {
|
try {
|
||||||
final credentialInfo = await _authenticationInfoCacheManager.getAuthenticationInfoStored();
|
final credentialInfo = await _authenticationInfoCacheManager.getAuthenticationInfoStored();
|
||||||
log('IOSSharingManager::_getCredentialAuthentication:credentialInfo: $credentialInfo');
|
|
||||||
return base64Encode(utf8.encode('${credentialInfo.username}:${credentialInfo.password}'));
|
return base64Encode(utf8.encode('${credentialInfo.username}:${credentialInfo.password}'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('IOSSharingManager::_getCredentialAuthentication:Exception: $e');
|
logError('IOSSharingManager::_getCredentialAuthentication:Exception: $e');
|
||||||
@@ -136,9 +137,7 @@ class IOSSharingManager {
|
|||||||
required UserName userName
|
required UserName userName
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final emailDeliveryState = await getEmailDeliveryStateFromKeychain(accountId);
|
return await getEmailDeliveryStateFromKeychain(accountId);
|
||||||
log('IOSSharingManager::_getEmailState:emailDeliveryState: $emailDeliveryState');
|
|
||||||
return emailDeliveryState;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError('IOSSharingManager::_getEmailDeliveryState:Exception: $e');
|
logError('IOSSharingManager::_getEmailDeliveryState:Exception: $e');
|
||||||
return null;
|
return null;
|
||||||
@@ -167,7 +166,6 @@ class IOSSharingManager {
|
|||||||
try {
|
try {
|
||||||
final oidcConfig = await _oidcConfigurationCacheManager.getOidcConfiguration();
|
final oidcConfig = await _oidcConfigurationCacheManager.getOidcConfiguration();
|
||||||
final oidcDiscoveryResponse = await _oidcHttpClient.discoverOIDC(oidcConfig);
|
final oidcDiscoveryResponse = await _oidcHttpClient.discoverOIDC(oidcConfig);
|
||||||
log('IOSSharingManager::_getTokenEndpointAndScopes:oidcDiscoveryResponse = $oidcDiscoveryResponse | oidcConfig = $oidcConfig');
|
|
||||||
return (
|
return (
|
||||||
tokenEndpoint: oidcDiscoveryResponse.tokenEndpoint,
|
tokenEndpoint: oidcDiscoveryResponse.tokenEndpoint,
|
||||||
scopes: oidcConfig.scopes
|
scopes: oidcConfig.scopes
|
||||||
@@ -180,7 +178,6 @@ class IOSSharingManager {
|
|||||||
|
|
||||||
Future updateEmailStateInKeyChain(AccountId accountId, String newEmailState) async {
|
Future updateEmailStateInKeyChain(AccountId accountId, String newEmailState) async {
|
||||||
final keychainSharingStored = await getKeychainSharingSession(accountId);
|
final keychainSharingStored = await getKeychainSharingSession(accountId);
|
||||||
log('IOSSharingManager::updateEmailStateInKeyChain:keychainSharingStored: $keychainSharingStored | newEmailState: $newEmailState');
|
|
||||||
if (keychainSharingStored == null) {
|
if (keychainSharingStored == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ extension SessionExtension on Session {
|
|||||||
throw NotFoundPersonalAccountException();
|
throw NotFoundPersonalAccountException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AccountId get accountId => personalAccount.accountId;
|
||||||
|
|
||||||
({
|
({
|
||||||
bool isAvailable,
|
bool isAvailable,
|
||||||
CalendarEventCapability? calendarEventCapability
|
CalendarEventCapability? calendarEventCapability
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
import 'package:core/utils/app_logger.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:model/oidc/converter/token_id_converter.dart';
|
import 'package:model/oidc/converter/token_id_converter.dart';
|
||||||
@@ -40,8 +39,6 @@ extension TokenOIDCExtension on TokenOIDC {
|
|||||||
bool get isExpired {
|
bool get isExpired {
|
||||||
if (expiredTime != null) {
|
if (expiredTime != null) {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
log('TokenOIDC::isExpired(): TIME_NOW: $now');
|
|
||||||
log('TokenOIDC::isExpired(): EXPIRED_DATE: $expiredTime');
|
|
||||||
return expiredTime!.isBefore(now);
|
return expiredTime!.isBefore(now);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_
|
|||||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
|
||||||
@@ -49,7 +49,7 @@ import 'home_controller_test.mocks.dart';
|
|||||||
MockSpec<EmailReceiveManager>(),
|
MockSpec<EmailReceiveManager>(),
|
||||||
MockSpec<GetSessionInteractor>(),
|
MockSpec<GetSessionInteractor>(),
|
||||||
MockSpec<GetAuthenticatedAccountInteractor>(),
|
MockSpec<GetAuthenticatedAccountInteractor>(),
|
||||||
MockSpec<UpdateAuthenticationAccountInteractor>(),
|
MockSpec<UpdateAccountCacheInteractor>(),
|
||||||
MockSpec<CachingManager>(),
|
MockSpec<CachingManager>(),
|
||||||
MockSpec<LanguageCacheManager>(),
|
MockSpec<LanguageCacheManager>(),
|
||||||
MockSpec<ApplicationManager>(),
|
MockSpec<ApplicationManager>(),
|
||||||
@@ -66,7 +66,7 @@ void main() {
|
|||||||
|
|
||||||
late MockGetSessionInteractor mockGetSessionInteractor;
|
late MockGetSessionInteractor mockGetSessionInteractor;
|
||||||
late MockGetAuthenticatedAccountInteractor mockGetAuthenticatedAccountInteractor;
|
late MockGetAuthenticatedAccountInteractor mockGetAuthenticatedAccountInteractor;
|
||||||
late MockUpdateAuthenticationAccountInteractor mockUpdateAuthenticationAccountInteractor;
|
late MockUpdateAccountCacheInteractor mockUpdateAccountCacheInteractor;
|
||||||
|
|
||||||
late MockCachingManager mockCachingManager;
|
late MockCachingManager mockCachingManager;
|
||||||
late MockLanguageCacheManager mockLanguageCacheManager;
|
late MockLanguageCacheManager mockLanguageCacheManager;
|
||||||
@@ -91,7 +91,7 @@ void main() {
|
|||||||
// mock reloadable controller
|
// mock reloadable controller
|
||||||
mockGetSessionInteractor = MockGetSessionInteractor();
|
mockGetSessionInteractor = MockGetSessionInteractor();
|
||||||
mockGetAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor();
|
mockGetAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor();
|
||||||
mockUpdateAuthenticationAccountInteractor = MockUpdateAuthenticationAccountInteractor();
|
mockUpdateAccountCacheInteractor = MockUpdateAccountCacheInteractor();
|
||||||
|
|
||||||
//mock base controller
|
//mock base controller
|
||||||
mockCachingManager = MockCachingManager();
|
mockCachingManager = MockCachingManager();
|
||||||
@@ -109,7 +109,7 @@ void main() {
|
|||||||
|
|
||||||
Get.put<GetSessionInteractor>(mockGetSessionInteractor);
|
Get.put<GetSessionInteractor>(mockGetSessionInteractor);
|
||||||
Get.put<GetAuthenticatedAccountInteractor>(mockGetAuthenticatedAccountInteractor);
|
Get.put<GetAuthenticatedAccountInteractor>(mockGetAuthenticatedAccountInteractor);
|
||||||
Get.put<UpdateAuthenticationAccountInteractor>(mockUpdateAuthenticationAccountInteractor);
|
Get.put<UpdateAccountCacheInteractor>(mockUpdateAccountCacheInteractor);
|
||||||
|
|
||||||
Get.put<CachingManager>(mockCachingManager);
|
Get.put<CachingManager>(mockCachingManager);
|
||||||
Get.put<LanguageCacheManager>(mockLanguageCacheManager);
|
Get.put<LanguageCacheManager>(mockLanguageCacheManager);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_oidc_con
|
|||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/presentation/login_controller.dart';
|
import 'package:tmail_ui_user/features/login/presentation/login_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
|
import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
|
||||||
@@ -63,7 +63,7 @@ import 'login_controller_test.mocks.dart';
|
|||||||
MockSpec<DNSLookupToGetJmapUrlInteractor>(),
|
MockSpec<DNSLookupToGetJmapUrlInteractor>(),
|
||||||
MockSpec<GetSessionInteractor>(),
|
MockSpec<GetSessionInteractor>(),
|
||||||
MockSpec<GetAuthenticatedAccountInteractor>(),
|
MockSpec<GetAuthenticatedAccountInteractor>(),
|
||||||
MockSpec<UpdateAuthenticationAccountInteractor>(),
|
MockSpec<UpdateAccountCacheInteractor>(),
|
||||||
MockSpec<CachingManager>(),
|
MockSpec<CachingManager>(),
|
||||||
MockSpec<LanguageCacheManager>(),
|
MockSpec<LanguageCacheManager>(),
|
||||||
MockSpec<ApplicationManager>(),
|
MockSpec<ApplicationManager>(),
|
||||||
@@ -84,7 +84,7 @@ void main() {
|
|||||||
late MockDNSLookupToGetJmapUrlInteractor mockDNSLookupToGetJmapUrlInteractor;
|
late MockDNSLookupToGetJmapUrlInteractor mockDNSLookupToGetJmapUrlInteractor;
|
||||||
late MockGetSessionInteractor mockGetSessionInteractor;
|
late MockGetSessionInteractor mockGetSessionInteractor;
|
||||||
late MockGetAuthenticatedAccountInteractor mockGetAuthenticatedAccountInteractor;
|
late MockGetAuthenticatedAccountInteractor mockGetAuthenticatedAccountInteractor;
|
||||||
late MockUpdateAuthenticationAccountInteractor mockUpdateAuthenticationAccountInteractor;
|
late MockUpdateAccountCacheInteractor mockUpdateAccountCacheInteractor;
|
||||||
late CachingManager mockCachingManager;
|
late CachingManager mockCachingManager;
|
||||||
late LanguageCacheManager mockLanguageCacheManager;
|
late LanguageCacheManager mockLanguageCacheManager;
|
||||||
late MockAuthorizationInterceptors mockAuthorizationInterceptors;
|
late MockAuthorizationInterceptors mockAuthorizationInterceptors;
|
||||||
@@ -119,7 +119,7 @@ void main() {
|
|||||||
// mock reloadable controller
|
// mock reloadable controller
|
||||||
mockGetSessionInteractor = MockGetSessionInteractor();
|
mockGetSessionInteractor = MockGetSessionInteractor();
|
||||||
mockGetAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor();
|
mockGetAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor();
|
||||||
mockUpdateAuthenticationAccountInteractor = MockUpdateAuthenticationAccountInteractor();
|
mockUpdateAccountCacheInteractor = MockUpdateAccountCacheInteractor();
|
||||||
|
|
||||||
//mock base controller
|
//mock base controller
|
||||||
mockCachingManager = MockCachingManager();
|
mockCachingManager = MockCachingManager();
|
||||||
@@ -137,7 +137,7 @@ void main() {
|
|||||||
|
|
||||||
Get.put<GetSessionInteractor>(mockGetSessionInteractor);
|
Get.put<GetSessionInteractor>(mockGetSessionInteractor);
|
||||||
Get.put<GetAuthenticatedAccountInteractor>(mockGetAuthenticatedAccountInteractor);
|
Get.put<GetAuthenticatedAccountInteractor>(mockGetAuthenticatedAccountInteractor);
|
||||||
Get.put<UpdateAuthenticationAccountInteractor>(mockUpdateAuthenticationAccountInteractor);
|
Get.put<UpdateAccountCacheInteractor>(mockUpdateAccountCacheInteractor);
|
||||||
Get.put<CachingManager>(mockCachingManager);
|
Get.put<CachingManager>(mockCachingManager);
|
||||||
Get.put<LanguageCacheManager>(mockLanguageCacheManager);
|
Get.put<LanguageCacheManager>(mockLanguageCacheManager);
|
||||||
Get.put<AuthorizationInterceptors>(mockAuthorizationInterceptors);
|
Get.put<AuthorizationInterceptors>(mockAuthorizationInterceptors);
|
||||||
|
|||||||
+4
-4
@@ -34,7 +34,7 @@ import 'package:tmail_ui_user/features/login/data/network/interceptors/authoriza
|
|||||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart';
|
import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_default_mailbox_interactor.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_default_mailbox_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart';
|
import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart';
|
||||||
@@ -138,7 +138,7 @@ const fallbackGenerators = {
|
|||||||
MockSpec<GetAllRecentSearchLatestInteractor>(),
|
MockSpec<GetAllRecentSearchLatestInteractor>(),
|
||||||
MockSpec<GetSessionInteractor>(),
|
MockSpec<GetSessionInteractor>(),
|
||||||
MockSpec<GetAuthenticatedAccountInteractor>(),
|
MockSpec<GetAuthenticatedAccountInteractor>(),
|
||||||
MockSpec<UpdateAuthenticationAccountInteractor>(),
|
MockSpec<UpdateAccountCacheInteractor>(),
|
||||||
MockSpec<CreateNewMailboxInteractor>(),
|
MockSpec<CreateNewMailboxInteractor>(),
|
||||||
MockSpec<DeleteMultipleMailboxInteractor>(),
|
MockSpec<DeleteMultipleMailboxInteractor>(),
|
||||||
MockSpec<RenameMailboxInteractor>(),
|
MockSpec<RenameMailboxInteractor>(),
|
||||||
@@ -234,7 +234,7 @@ void main() {
|
|||||||
// mock reloadable controller Get dependencies
|
// mock reloadable controller Get dependencies
|
||||||
final getSessionInteractor = MockGetSessionInteractor();
|
final getSessionInteractor = MockGetSessionInteractor();
|
||||||
final getAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor();
|
final getAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor();
|
||||||
final updateAuthenticationAccountInteractor = MockUpdateAuthenticationAccountInteractor();
|
final updateAccountCacheInteractor = MockUpdateAccountCacheInteractor();
|
||||||
|
|
||||||
// mock mailbox controller direct dependencies
|
// mock mailbox controller direct dependencies
|
||||||
final createNewMailboxInteractor = MockCreateNewMailboxInteractor();
|
final createNewMailboxInteractor = MockCreateNewMailboxInteractor();
|
||||||
@@ -299,7 +299,7 @@ void main() {
|
|||||||
Get.put<ApplicationManager>(applicationManager);
|
Get.put<ApplicationManager>(applicationManager);
|
||||||
Get.put<GetSessionInteractor>(getSessionInteractor);
|
Get.put<GetSessionInteractor>(getSessionInteractor);
|
||||||
Get.put<GetAuthenticatedAccountInteractor>(getAuthenticatedAccountInteractor);
|
Get.put<GetAuthenticatedAccountInteractor>(getAuthenticatedAccountInteractor);
|
||||||
Get.put<UpdateAuthenticationAccountInteractor>(updateAuthenticationAccountInteractor);
|
Get.put<UpdateAccountCacheInteractor>(updateAccountCacheInteractor);
|
||||||
Get.put<GetAllIdentitiesInteractor>(getAllIdentitiesInteractor);
|
Get.put<GetAllIdentitiesInteractor>(getAllIdentitiesInteractor);
|
||||||
Get.put<RemoveComposerCacheOnWebInteractor>(removeComposerCacheOnWebInteractor);
|
Get.put<RemoveComposerCacheOnWebInteractor>(removeComposerCacheOnWebInteractor);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user