TF-4268 Cache Sentry config and user for FCM background re-initialization
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:core/utils/file_utils.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:tmail_ui_user/features/caching/clients/hive_cache_version_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
|
||||
import 'package:tmail_ui_user/features/caching/manager/sentry_configuration_cache_manager.dart';
|
||||
import 'package:tmail_ui_user/features/caching/manager/session_cache_manger.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
import 'package:tmail_ui_user/features/cleanup/data/local/recent_login_url_cache_manager.dart';
|
||||
@@ -42,6 +43,7 @@ class CachingManager {
|
||||
final OidcConfigurationCacheManager _oidcConfigurationCacheManager;
|
||||
final EncryptionKeyCacheManager _encryptionKeyCacheManager;
|
||||
final AuthenticationInfoCacheManager _authenticationInfoCacheManager;
|
||||
final SentryConfigurationCacheManager? _sentryConfigurationCacheManager;
|
||||
|
||||
CachingManager(
|
||||
this._mailboxCacheManager,
|
||||
@@ -62,8 +64,9 @@ class CachingManager {
|
||||
this._tokenOidcCacheManager,
|
||||
this._oidcConfigurationCacheManager,
|
||||
this._encryptionKeyCacheManager,
|
||||
this._authenticationInfoCacheManager,
|
||||
);
|
||||
this._authenticationInfoCacheManager, {
|
||||
SentryConfigurationCacheManager? sentryConfigurationCacheManager,
|
||||
}) : _sentryConfigurationCacheManager = sentryConfigurationCacheManager;
|
||||
|
||||
Future<void> clearAll() async {
|
||||
try {
|
||||
@@ -104,6 +107,8 @@ class CachingManager {
|
||||
_oidcConfigurationCacheManager.clear(),
|
||||
_tokenOidcCacheManager.clear(),
|
||||
_authenticationInfoCacheManager.clear(),
|
||||
if (PlatformInfo.isMobile && _sentryConfigurationCacheManager != null)
|
||||
_sentryConfigurationCacheManager!.clearSentryConfiguration(),
|
||||
], eagerError: true);
|
||||
} catch (e) {
|
||||
logWarning('CachingManager::clearAccountDataCached: Cannot clear account data cache: $e');
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
|
||||
class SentryConfigurationCacheClient
|
||||
extends HiveCacheClient<SentryConfigurationCache> {
|
||||
@override
|
||||
String get tableName => CachingConstants.sentryConfigurationCacheBoxName;
|
||||
|
||||
@override
|
||||
bool get encryption => true;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_user_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
|
||||
class SentryUserCacheClient
|
||||
extends HiveCacheClient<SentryUserCache> {
|
||||
@override
|
||||
String get tableName => CachingConstants.sentryUserCacheBoxName;
|
||||
|
||||
@override
|
||||
bool get encryption => true;
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import 'package:tmail_ui_user/features/base/upgradeable/upgrade_hive_database_st
|
||||
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
|
||||
import 'package:tmail_ui_user/features/caching/config/cache_version.dart';
|
||||
import 'package:tmail_ui_user/features/caching/config/fcm_isolate_name_server.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_user_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
import 'package:tmail_ui_user/features/home/data/model/session_hive_obj.dart';
|
||||
import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart';
|
||||
@@ -239,6 +241,16 @@ class HiveCacheConfig {
|
||||
CachingConstants.OIDC_CONFIGURATION_CACHE_ID,
|
||||
isolated: isolated,
|
||||
);
|
||||
registerCacheAdapter<SentryConfigurationCache>(
|
||||
SentryConfigurationCacheAdapter(),
|
||||
CachingConstants.SENTRY_CONFIGURATION_CACHE_ID,
|
||||
isolated: isolated,
|
||||
);
|
||||
registerCacheAdapter<SentryUserCache>(
|
||||
SentryUserCacheAdapter(),
|
||||
CachingConstants.SENTRY_USER_CACHE_ID,
|
||||
isolated: isolated,
|
||||
);
|
||||
}
|
||||
|
||||
void registerCacheAdapter<T>(
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
|
||||
part 'sentry_configuration_cache.g.dart';
|
||||
|
||||
@HiveType(typeId: CachingConstants.SENTRY_CONFIGURATION_CACHE_ID)
|
||||
class SentryConfigurationCache extends HiveObject with EquatableMixin {
|
||||
@HiveField(0)
|
||||
final String dsn;
|
||||
|
||||
@HiveField(1)
|
||||
final String environment;
|
||||
|
||||
@HiveField(2)
|
||||
final String release;
|
||||
|
||||
@HiveField(3)
|
||||
final double tracesSampleRate;
|
||||
|
||||
@HiveField(4)
|
||||
final double profilesSampleRate;
|
||||
|
||||
@HiveField(5)
|
||||
final bool enableLogs;
|
||||
|
||||
@HiveField(6)
|
||||
final bool isDebug;
|
||||
|
||||
@HiveField(7)
|
||||
final bool attachScreenshot;
|
||||
|
||||
@HiveField(8)
|
||||
final bool isAvailable;
|
||||
|
||||
@HiveField(9)
|
||||
final double sessionSampleRate;
|
||||
|
||||
@HiveField(10)
|
||||
final double onErrorSampleRate;
|
||||
|
||||
@HiveField(11)
|
||||
final bool enableFramesTracking;
|
||||
|
||||
@HiveField(12)
|
||||
final String? dist;
|
||||
|
||||
SentryConfigurationCache({
|
||||
required this.dsn,
|
||||
required this.environment,
|
||||
required this.release,
|
||||
required this.tracesSampleRate,
|
||||
required this.profilesSampleRate,
|
||||
required this.enableLogs,
|
||||
required this.isDebug,
|
||||
required this.attachScreenshot,
|
||||
required this.isAvailable,
|
||||
required this.sessionSampleRate,
|
||||
required this.onErrorSampleRate,
|
||||
required this.enableFramesTracking,
|
||||
required this.dist,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
dsn,
|
||||
environment,
|
||||
release,
|
||||
tracesSampleRate,
|
||||
profilesSampleRate,
|
||||
enableLogs,
|
||||
isDebug,
|
||||
attachScreenshot,
|
||||
isAvailable,
|
||||
sessionSampleRate,
|
||||
onErrorSampleRate,
|
||||
enableFramesTracking,
|
||||
dist,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
|
||||
part 'sentry_user_cache.g.dart';
|
||||
|
||||
@HiveType(typeId: CachingConstants.SENTRY_USER_CACHE_ID)
|
||||
class SentryUserCache extends HiveObject with EquatableMixin {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
@HiveField(1)
|
||||
final String name;
|
||||
|
||||
@HiveField(2)
|
||||
final String username;
|
||||
|
||||
@HiveField(3)
|
||||
final String email;
|
||||
|
||||
SentryUserCache({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.username,
|
||||
required this.email,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
name,
|
||||
username,
|
||||
email,
|
||||
];
|
||||
}
|
||||
@@ -6,3 +6,17 @@ class NotFoundDataWithThisKeyException extends AppBaseException {
|
||||
@override
|
||||
String get exceptionName => 'NotFoundDataWithThisKeyException';
|
||||
}
|
||||
|
||||
class NotFoundSentryConfigurationException extends AppBaseException {
|
||||
const NotFoundSentryConfigurationException([super.message]);
|
||||
|
||||
@override
|
||||
String get exceptionName => 'NotFoundSentryConfigurationException';
|
||||
}
|
||||
|
||||
class NotFoundSentryUserException extends AppBaseException {
|
||||
const NotFoundSentryUserException([super.message]);
|
||||
|
||||
@override
|
||||
String get exceptionName => 'NotFoundSentryUserException';
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:core/utils/sentry/sentry_config.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_user_cache.dart';
|
||||
|
||||
extension SentryConfigExtension on SentryConfig {
|
||||
SentryConfigurationCache toSentryConfigurationCache() {
|
||||
return SentryConfigurationCache(
|
||||
dsn: dsn,
|
||||
environment: environment,
|
||||
release: release,
|
||||
isAvailable: isAvailable,
|
||||
tracesSampleRate: tracesSampleRate,
|
||||
profilesSampleRate: profilesSampleRate,
|
||||
sessionSampleRate: sessionSampleRate,
|
||||
onErrorSampleRate: onErrorSampleRate,
|
||||
enableLogs: enableLogs,
|
||||
enableFramesTracking: enableFramesTracking,
|
||||
isDebug: isDebug,
|
||||
attachScreenshot: attachScreenshot,
|
||||
dist: dist,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension SentryConfigurationCacheExtension on SentryConfigurationCache {
|
||||
SentryConfig toSentryConfig() {
|
||||
return SentryConfig(
|
||||
dsn: dsn,
|
||||
environment: environment,
|
||||
release: release,
|
||||
isAvailable: isAvailable,
|
||||
tracesSampleRate: tracesSampleRate,
|
||||
profilesSampleRate: profilesSampleRate,
|
||||
sessionSampleRate: sessionSampleRate,
|
||||
onErrorSampleRate: onErrorSampleRate,
|
||||
enableLogs: enableLogs,
|
||||
enableFramesTracking: enableFramesTracking,
|
||||
isDebug: isDebug,
|
||||
attachScreenshot: attachScreenshot,
|
||||
dist: dist,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension SentryUserExtension on SentryUser {
|
||||
SentryUserCache toSentryUserCache() {
|
||||
return SentryUserCache(
|
||||
id: id ?? '',
|
||||
name: name ?? '',
|
||||
username: username ?? '',
|
||||
email: email ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension SentryUserCacheExtension on SentryUserCache {
|
||||
SentryUser toSentryUser() {
|
||||
return SentryUser(
|
||||
id: id.isEmpty ? null : id,
|
||||
name: name.isEmpty ? null : name,
|
||||
username: username.isEmpty ? null : username,
|
||||
email: email.isEmpty ? null : email,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:tmail_ui_user/features/caching/clients/sentry_configuration_cache_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/clients/sentry_user_cache_client.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_user_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/exceptions/local_storage_exception.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart';
|
||||
|
||||
class SentryConfigurationCacheManager {
|
||||
final SentryConfigurationCacheClient _configurationCacheClient;
|
||||
final SentryUserCacheClient _userCacheClient;
|
||||
final String _configurationCacheKey =
|
||||
CachingConstants.sentryConfigurationCacheKeyName;
|
||||
final String _userCacheKey = CachingConstants.sentryUserCacheKeyName;
|
||||
|
||||
SentryConfigurationCacheManager(
|
||||
this._configurationCacheClient,
|
||||
this._userCacheClient,
|
||||
);
|
||||
|
||||
Future<SentryConfigurationCache> getSentryConfiguration() async {
|
||||
final cache = await _configurationCacheClient.getItem(_configurationCacheKey);
|
||||
if (cache == null) throw const NotFoundSentryConfigurationException();
|
||||
return cache;
|
||||
}
|
||||
|
||||
Future<void> saveSentryConfiguration(
|
||||
SentryConfigurationCache sentryConfigurationCache,
|
||||
) async {
|
||||
await _configurationCacheClient.insertItem(
|
||||
_configurationCacheKey,
|
||||
sentryConfigurationCache,
|
||||
);
|
||||
}
|
||||
|
||||
Future<SentryUserCache> getSentryUser() async {
|
||||
final cache = await _userCacheClient.getItem(_userCacheKey);
|
||||
if (cache == null) throw const NotFoundSentryUserException();
|
||||
return cache;
|
||||
}
|
||||
|
||||
Future<void> saveSentryUser(
|
||||
SentryUserCache sentryUserCache,
|
||||
) async {
|
||||
await _userCacheClient.insertItem(_userCacheKey, sentryUserCache);
|
||||
}
|
||||
|
||||
Future<void> clearSentryConfiguration() async {
|
||||
try {
|
||||
await _configurationCacheClient.clearAllData();
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'SentryConfigurationCacheManager::clearSentryConfiguration: Failed to clear config cache',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
try {
|
||||
await _userCacheClient.clearAllData();
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'SentryConfigurationCacheManager::clearSentryConfiguration: Failed to clear user cache',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ class CachingConstants {
|
||||
static const int SENDING_EMAIL_HIVE_CACHE_ID = 18;
|
||||
static const int SESSION_HIVE_CACHE_ID = 19;
|
||||
static const int OIDC_CONFIGURATION_CACHE_ID = 20;
|
||||
static const int SENTRY_CONFIGURATION_CACHE_ID = 21;
|
||||
static const int SENTRY_USER_CACHE_ID = 22;
|
||||
|
||||
static const String fcmCacheBoxName = 'fcm_cache_box';
|
||||
static const String newEmailCacheBoxName = 'new_email_cache_box';
|
||||
@@ -28,8 +30,12 @@ class CachingConstants {
|
||||
static const String sessionCacheBoxName = 'session_cache_box';
|
||||
static const String firebaseRegistrationCacheBoxName = 'firebase_registration_cache_box';
|
||||
static const String oidcConfigurationCacheBoxName = 'oidc_configuration_cache_box';
|
||||
static const String sentryConfigurationCacheBoxName = 'sentry_configuration_cache_box';
|
||||
static const String sentryUserCacheBoxName = 'sentry_user_cache_box';
|
||||
|
||||
static const String oidcConfigurationCacheKeyName = 'oidc_configuration_cache_key';
|
||||
static const String sentryConfigurationCacheKeyName = 'sentry_configuration_cache_key';
|
||||
static const String sentryUserCacheKeyName = 'sentry_user_cache_key';
|
||||
|
||||
static const String newEmailsContentFolderName = 'new_emails';
|
||||
static const String openedEmailContentFolderName = 'opened_email';
|
||||
|
||||
+13
-3
@@ -154,7 +154,9 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_text_formatting_menu_state_extension.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/web_auth_redirect_processor_extension.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/sentry_ecosystem_mixin.dart';
|
||||
import 'package:tmail_ui_user/features/caching/manager/sentry_configuration_cache_manager.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/sentry_config_linagora_ecosystem.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/sentry_ecosystem.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/draggable_app_state.dart';
|
||||
@@ -242,8 +244,9 @@ class MailboxDashBoardController extends ReloadableController
|
||||
AiScribeMixin,
|
||||
SearchLabelFilterModalMixin,
|
||||
AddLabelToEmailMixin,
|
||||
HandleTeamMailboxMixin,
|
||||
SentryEcosystemMixin {
|
||||
HandleTeamMailboxMixin {
|
||||
|
||||
SentryEcosystem? _sentryEcosystem;
|
||||
|
||||
final RemoveEmailDraftsInteractor _removeEmailDraftsInteractor = Get.find<RemoveEmailDraftsInteractor>();
|
||||
final EmailReceiveManager _emailReceiveManager = Get.find<EmailReceiveManager>();
|
||||
@@ -403,6 +406,7 @@ class MailboxDashBoardController extends ReloadableController
|
||||
@override
|
||||
void onInit() {
|
||||
if (PlatformInfo.isMobile) {
|
||||
_sentryEcosystem = SentryEcosystem(getBinding<SentryConfigurationCacheManager>());
|
||||
_registerReceivingFileSharingStream();
|
||||
_registerDeepLinks();
|
||||
}
|
||||
@@ -415,6 +419,11 @@ class MailboxDashBoardController extends ReloadableController
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
void initSentryUser(SentryUser? user) => _sentryEcosystem?.initUser(user);
|
||||
|
||||
Future<void> setUpSentry(SentryConfigLinagoraEcosystem ecosystemConfig) async =>
|
||||
_sentryEcosystem?.setUp(ecosystemConfig);
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
if (PlatformInfo.isWeb) {
|
||||
@@ -3477,6 +3486,7 @@ class MailboxDashBoardController extends ReloadableController
|
||||
paywallController?.onClose();
|
||||
paywallController = null;
|
||||
cachedLinagoraEcosystem = null;
|
||||
_sentryEcosystem = null;
|
||||
_disposeWorkerObxVariables();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
+2
@@ -1,4 +1,5 @@
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:scribe/scribe/ai/data/service/prompt_service.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_linagora_ecosystem_state.dart';
|
||||
@@ -51,6 +52,7 @@ extension SetupScribePromptUrlExtension on MailboxDashBoardController {
|
||||
}
|
||||
|
||||
void _setUpSentry(LinagoraEcosystem ecosystem) {
|
||||
if (PlatformInfo.isWeb) return;
|
||||
final sentryConfigEcosystem = ecosystem.sentryConfigEcosystem;
|
||||
if (sentryConfigEcosystem != null) {
|
||||
setUpSentry(sentryConfigEcosystem);
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import 'package:core/presentation/extensions/string_extension.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/sentry/sentry_manager.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/sentry_config_linagora_ecosystem.dart';
|
||||
|
||||
mixin SentryEcosystemMixin {
|
||||
SentryUser? _sentryUser;
|
||||
|
||||
void initSentryUser(SentryUser? newSentryUser) {
|
||||
_sentryUser = newSentryUser;
|
||||
}
|
||||
|
||||
Future<void> setUpSentry(
|
||||
SentryConfigLinagoraEcosystem ecosystemConfig,
|
||||
) async {
|
||||
final dsn = ecosystemConfig.dsn?.trimmed;
|
||||
final env = ecosystemConfig.environment?.trimmed;
|
||||
final isValid = ecosystemConfig.enabled == true
|
||||
&& (dsn?.isNotEmpty ?? false)
|
||||
&& (env?.isNotEmpty ?? false);
|
||||
|
||||
if (!isValid) {
|
||||
logWarning(
|
||||
'SentryEcosystemMixin::setUpSentry: config invalid '
|
||||
'(enabled=${ecosystemConfig.enabled}, dsn=${dsn?.isNotEmpty}, env=${env?.isNotEmpty})',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final sentryConfig = await ecosystemConfig.toSentryConfig();
|
||||
|
||||
await SentryManager.instance.initializeWithSentryConfig(sentryConfig);
|
||||
|
||||
_setupSentryUser();
|
||||
}
|
||||
|
||||
void _setupSentryUser() {
|
||||
if (_sentryUser == null) return;
|
||||
|
||||
SentryManager.instance.setUser(_sentryUser!);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:core/presentation/extensions/string_extension.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/sentry/sentry_config.dart';
|
||||
import 'package:core/utils/sentry/sentry_manager.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:tmail_ui_user/features/caching/extensions/sentry_cache_extensions.dart';
|
||||
import 'package:tmail_ui_user/features/caching/manager/sentry_configuration_cache_manager.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/sentry_config_linagora_ecosystem.dart';
|
||||
|
||||
class SentryEcosystem {
|
||||
final SentryConfigurationCacheManager? _cacheManager;
|
||||
|
||||
SentryUser? _sentryUser;
|
||||
|
||||
SentryEcosystem(this._cacheManager);
|
||||
|
||||
void initUser(SentryUser? user) {
|
||||
_sentryUser = user;
|
||||
}
|
||||
|
||||
Future<void> setUp(SentryConfigLinagoraEcosystem ecosystemConfig) async {
|
||||
final dsn = ecosystemConfig.dsn?.trimmed;
|
||||
final env = ecosystemConfig.environment?.trimmed;
|
||||
final isValid = ecosystemConfig.enabled == true
|
||||
&& (dsn?.isNotEmpty ?? false)
|
||||
&& (env?.isNotEmpty ?? false);
|
||||
|
||||
if (!isValid) {
|
||||
logWarning(
|
||||
'SentryEcosystem::setUp: config invalid '
|
||||
'(enabled=${ecosystemConfig.enabled}, dsn=${dsn?.isNotEmpty}, env=${env?.isNotEmpty})',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final sentryConfig = await ecosystemConfig.toSentryConfig();
|
||||
|
||||
await SentryManager.instance.initializeWithSentryConfig(sentryConfig);
|
||||
|
||||
_applyUser();
|
||||
|
||||
await _cacheData(sentryConfig, _sentryUser);
|
||||
}
|
||||
|
||||
void _applyUser() {
|
||||
if (_sentryUser == null) return;
|
||||
SentryManager.instance.setUser(_sentryUser!);
|
||||
}
|
||||
|
||||
Future<void> _cacheData(SentryConfig sentryConfig, SentryUser? sentryUser) async {
|
||||
if (_cacheManager == null) return;
|
||||
try {
|
||||
await _cacheManager!.saveSentryConfiguration(sentryConfig.toSentryConfigurationCache());
|
||||
if (sentryUser != null) {
|
||||
await _cacheManager!.saveSentryUser(sentryUser.toSentryUserCache());
|
||||
}
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'SentryEcosystem::_cacheData: Cannot cache Sentry data',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
// Clear both caches to avoid stale/inconsistent state (e.g. new config + old user PII)
|
||||
await _cacheManager!.clearSentryConfiguration().catchError((_) {});
|
||||
}
|
||||
}
|
||||
}
|
||||
+150
-45
@@ -6,6 +6,7 @@ import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:core/utils/sentry/sentry_manager.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||
@@ -13,6 +14,9 @@ import 'package:jmap_dart_client/jmap/push/state_change.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
|
||||
import 'package:tmail_ui_user/features/caching/entries/sentry_configuration_cache.dart';
|
||||
import 'package:tmail_ui_user/features/caching/extensions/sentry_cache_extensions.dart';
|
||||
import 'package:tmail_ui_user/features/caching/manager/sentry_configuration_cache_manager.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/usecases/get_session_interactor.dart';
|
||||
@@ -48,10 +52,18 @@ class FcmMessageController extends PushBaseController {
|
||||
|
||||
@override
|
||||
void initialize({AccountId? accountId, Session? session}) {
|
||||
super.initialize(accountId: accountId, session: session);
|
||||
try {
|
||||
super.initialize(accountId: accountId, session: session);
|
||||
|
||||
_listenTokenStream();
|
||||
_listenBackgroundMessageStream();
|
||||
_listenTokenStream();
|
||||
_listenBackgroundMessageStream();
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'FcmMessageController::initialize: throw exception',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _listenBackgroundMessageStream() {
|
||||
@@ -59,36 +71,104 @@ class FcmMessageController extends PushBaseController {
|
||||
.debounceTime(const Duration(
|
||||
milliseconds: FcmUtils.durationBackgroundMessageComing,
|
||||
))
|
||||
.listen(_handleBackgroundMessageAction);
|
||||
.listen(
|
||||
_handleBackgroundMessageAction,
|
||||
onError: (e, st) {
|
||||
logError(
|
||||
'FcmMessageController::_listenBackgroundMessageStream',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _listenTokenStream() {
|
||||
FcmService.instance.fcmTokenStreamController
|
||||
?.stream
|
||||
.debounceTime(const Duration(milliseconds: FcmUtils.durationRefreshToken))
|
||||
.listen(FcmTokenController.instance.onFcmTokenChanged);
|
||||
.listen(
|
||||
FcmTokenController.instance.onFcmTokenChanged,
|
||||
onError: (e, st) {
|
||||
logError(
|
||||
'FcmMessageController::_listenTokenStream',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleBackgroundMessageAction(Map<String, dynamic> payloadData) async {
|
||||
log('FcmMessageController::_handleBackgroundMessageAction():payloadData: $payloadData');
|
||||
logTrace('FcmMessageController::_handleBackgroundMessageAction():payloadData keys: ${payloadData.keys.toList()}');
|
||||
final stateChange = FcmUtils.instance.convertFirebaseDataMessageToStateChange(payloadData);
|
||||
await _initialAppConfig();
|
||||
if (stateChange == null) {
|
||||
logTrace('FcmMessageController::_handleBackgroundMessageAction(): stateChange is null');
|
||||
return;
|
||||
} else {
|
||||
logTrace('FcmMessageController::_handleBackgroundMessageAction(): stateChange: ${stateChange.toString()}');
|
||||
}
|
||||
_getAuthenticatedAccount(stateChange: stateChange);
|
||||
}
|
||||
|
||||
Future<void> _initialAppConfig() async {
|
||||
await Future.wait([
|
||||
MainBindings().dependencies(),
|
||||
HiveCacheConfig.instance.setUp()
|
||||
]);
|
||||
Future<void> initialAppConfig() async {
|
||||
try {
|
||||
await MainBindings().dependencies();
|
||||
await HiveCacheConfig.instance.setUp();
|
||||
|
||||
await Future.sync(() {
|
||||
HomeBindings().dependencies();
|
||||
MailboxDashBoardBindings().dependencies();
|
||||
FcmInteractorBindings().dependencies();
|
||||
});
|
||||
|
||||
_getInteractorBindings();
|
||||
_getInteractorBindings();
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'FcmMessageController::initialAppConfig: throw exception',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setUpSentryConfiguration() async {
|
||||
try {
|
||||
final cacheManager = getBinding<SentryConfigurationCacheManager>();
|
||||
if (cacheManager == null) {
|
||||
logWarning('FcmMessageController::setUpSentryConfiguration: SentryConfigurationCacheManager is null');
|
||||
return;
|
||||
}
|
||||
|
||||
final SentryConfigurationCache configCache;
|
||||
try {
|
||||
configCache = await cacheManager.getSentryConfiguration();
|
||||
} catch (e) {
|
||||
logWarning('FcmMessageController::setUpSentryConfiguration: SentryConfiguration not cached: $e');
|
||||
return;
|
||||
}
|
||||
|
||||
final sentryConfig = configCache.toSentryConfig();
|
||||
if (!sentryConfig.isAvailable) {
|
||||
logWarning('FcmMessageController::setUpSentryConfiguration: SentryConfiguration is not available');
|
||||
return;
|
||||
}
|
||||
|
||||
await SentryManager.instance.initializeWithSentryConfig(sentryConfig);
|
||||
|
||||
try {
|
||||
final userCache = await cacheManager.getSentryUser();
|
||||
SentryManager.instance.setUser(userCache.toSentryUser());
|
||||
} catch (e) {
|
||||
logTrace('FcmMessageController::setUpSentryConfiguration: Sentry user not cached: $e');
|
||||
// Acceptable — Sentry initialized without user context
|
||||
}
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'FcmMessageController::setUpSentryConfiguration: throw exception',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _getInteractorBindings() {
|
||||
@@ -100,9 +180,13 @@ class FcmMessageController extends PushBaseController {
|
||||
FcmTokenController.instance.initialBindingInteractor();
|
||||
}
|
||||
|
||||
void _getAuthenticatedAccount({StateChange? stateChange}) {
|
||||
void _getAuthenticatedAccount({required StateChange stateChange}) {
|
||||
if (_getAuthenticatedAccountInteractor != null) {
|
||||
consumeState(_getAuthenticatedAccountInteractor!.execute(stateChange: stateChange));
|
||||
} else {
|
||||
logTrace(
|
||||
'GetAuthenticatedAccountInteractor is null',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,25 +194,18 @@ class FcmMessageController extends PushBaseController {
|
||||
_dynamicUrlInterceptors?.setJmapUrl(storedTokenOidcSuccess.baseUrl.toString());
|
||||
_authorizationInterceptors?.setTokenAndAuthorityOidc(
|
||||
newToken: storedTokenOidcSuccess.tokenOidc,
|
||||
newConfig: storedTokenOidcSuccess.oidcConfiguration
|
||||
newConfig: storedTokenOidcSuccess.oidcConfiguration,
|
||||
);
|
||||
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_dynamicUrlInterceptors?.changeBaseUrl(storedTokenOidcSuccess.baseUrl.toString());
|
||||
_getSessionAction(stateChange: storedTokenOidcSuccess.stateChange);
|
||||
} else {
|
||||
_dynamicUrlInterceptors?.changeBaseUrl(storedTokenOidcSuccess.personalAccount.apiUrl);
|
||||
|
||||
final accountId = storedTokenOidcSuccess.personalAccount.accountId;
|
||||
final username = storedTokenOidcSuccess.personalAccount.userName;
|
||||
final stateChange = storedTokenOidcSuccess.stateChange;
|
||||
|
||||
if (accountId != null && username != null && stateChange != null) {
|
||||
_pushActionFromRemoteMessageBackground(
|
||||
accountId: accountId,
|
||||
userName: username,
|
||||
stateChange: stateChange);
|
||||
}
|
||||
_dispatchPushOnNonAndroid(
|
||||
apiUrl: storedTokenOidcSuccess.personalAccount.apiUrl,
|
||||
accountId: storedTokenOidcSuccess.personalAccount.accountId,
|
||||
username: storedTokenOidcSuccess.personalAccount.userName,
|
||||
stateChange: storedTokenOidcSuccess.stateChange,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,24 +219,39 @@ class FcmMessageController extends PushBaseController {
|
||||
_dynamicUrlInterceptors?.changeBaseUrl(credentialViewState.baseUrl.toString());
|
||||
_getSessionAction(stateChange: credentialViewState.stateChange);
|
||||
} else {
|
||||
_dynamicUrlInterceptors?.changeBaseUrl(credentialViewState.personalAccount.apiUrl);
|
||||
|
||||
final accountId = credentialViewState.personalAccount.accountId;
|
||||
final username = credentialViewState.personalAccount.userName;
|
||||
final stateChange = credentialViewState.stateChange;
|
||||
|
||||
if (accountId != null && username != null && stateChange != null) {
|
||||
_pushActionFromRemoteMessageBackground(
|
||||
accountId: accountId,
|
||||
userName: username,
|
||||
stateChange: stateChange);
|
||||
}
|
||||
_dispatchPushOnNonAndroid(
|
||||
apiUrl: credentialViewState.personalAccount.apiUrl,
|
||||
accountId: credentialViewState.personalAccount.accountId,
|
||||
username: credentialViewState.personalAccount.userName,
|
||||
stateChange: credentialViewState.stateChange,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _dispatchPushOnNonAndroid({
|
||||
required String? apiUrl,
|
||||
required AccountId? accountId,
|
||||
required UserName? username,
|
||||
required StateChange? stateChange,
|
||||
}) {
|
||||
_dynamicUrlInterceptors?.changeBaseUrl(apiUrl);
|
||||
final canPush = accountId != null && username != null && stateChange != null;
|
||||
if (!canPush) {
|
||||
logTrace('FcmMessageController::_dispatchPushOnNonAndroid: accountId or username or stateChange is null');
|
||||
return;
|
||||
}
|
||||
_pushActionFromRemoteMessageBackground(
|
||||
accountId: accountId,
|
||||
userName: username,
|
||||
stateChange: stateChange,
|
||||
);
|
||||
}
|
||||
|
||||
void _getSessionAction({StateChange? stateChange}) {
|
||||
if (_getSessionInteractor != null) {
|
||||
consumeState(_getSessionInteractor!.execute(stateChange: stateChange));
|
||||
} else {
|
||||
logTrace('FcmMessageController::_getSessionAction: _getSessionInteractor is null');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,9 +268,17 @@ class FcmMessageController extends PushBaseController {
|
||||
userName: success.session.username,
|
||||
stateChange: stateChange,
|
||||
session: success.session);
|
||||
} else {
|
||||
logTrace(
|
||||
'FcmMessageController::_handleGetSessionSuccess: Api url or state change is null',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logWarning('FcmMessageController::_handleGetSessionSuccess: Exception $e');
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'FcmMessageController::_handleGetSessionSuccess:',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +289,7 @@ class FcmMessageController extends PushBaseController {
|
||||
Session? session
|
||||
}) {
|
||||
final mapTypeState = stateChange.getMapTypeState(accountId);
|
||||
|
||||
logTrace('FcmMessageController::_pushActionFromRemoteMessageBackground: Mapping type state to action ${mapTypeState.toString()}');
|
||||
mappingTypeStateToAction(
|
||||
mapTypeState,
|
||||
accountId,
|
||||
@@ -204,6 +304,11 @@ class FcmMessageController extends PushBaseController {
|
||||
@override
|
||||
void handleFailureViewState(Failure failure) {
|
||||
log('FcmMessageController::_handleFailureViewState(): $failure');
|
||||
if (failure is GetStoredTokenOidcFailure) {
|
||||
logTrace('FcmMessageController::GetStoredTokenOidcFailure: Get stored token oidc is failed');
|
||||
} else if (failure is GetSessionFailure) {
|
||||
logTrace('FcmMessageController::GetSessionFailure: Get session is failed');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -91,19 +91,9 @@ class EmailChangeListener extends ChangeListener {
|
||||
log('EmailChangeListener::dispatchActions():actions: $actions');
|
||||
for (var action in actions) {
|
||||
if (action is SynchronizeEmailOnForegroundAction) {
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_handleRemoveNotificationWhenEmailMarkAsRead(action.newState, action.accountId, action.session);
|
||||
}
|
||||
_synchronizeEmailOnForegroundAction(action.newState);
|
||||
if (PlatformInfo.isMobile) {
|
||||
_getNewReceiveEmailFromNotificationAction(action.session, action.accountId, action.newState);
|
||||
}
|
||||
_onSynchronizeEmailOnForeground(action);
|
||||
} else if (action is PushNotificationAction) {
|
||||
_pushNotificationAction(action.newState, action.accountId, action.userName, action.session);
|
||||
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_getNewReceiveEmailFromNotificationAction(action.session, action.accountId, action.newState);
|
||||
}
|
||||
_onPushNotification(action);
|
||||
} else if (action is StoreEmailStateToRefreshAction) {
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_handleRemoveNotificationWhenEmailMarkAsRead(action.newState, action.accountId, action.session);
|
||||
@@ -112,6 +102,23 @@ class EmailChangeListener extends ChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
void _onSynchronizeEmailOnForeground(SynchronizeEmailOnForegroundAction action) {
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_handleRemoveNotificationWhenEmailMarkAsRead(action.newState, action.accountId, action.session);
|
||||
}
|
||||
_synchronizeEmailOnForegroundAction(action.newState);
|
||||
if (PlatformInfo.isMobile) {
|
||||
_getNewReceiveEmailFromNotificationAction(action.session, action.accountId, action.newState);
|
||||
}
|
||||
}
|
||||
|
||||
void _onPushNotification(PushNotificationAction action) {
|
||||
_pushNotificationAction(action.newState, action.accountId, action.userName, action.session);
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_getNewReceiveEmailFromNotificationAction(action.session, action.accountId, action.newState);
|
||||
}
|
||||
}
|
||||
|
||||
void _synchronizeEmailOnForegroundAction(jmap.State newState) {
|
||||
log('EmailChangeListener::_synchronizeEmailAction():newState: $newState');
|
||||
_dashBoardController?.dispatchEmailUIAction(RefreshChangeEmailAction(
|
||||
@@ -142,27 +149,29 @@ class EmailChangeListener extends ChangeListener {
|
||||
}
|
||||
|
||||
void _getStoredEmailState() {
|
||||
if (_getStoredEmailStateInteractor != null && _session != null && _accountId != null) {
|
||||
consumeState(_getStoredEmailStateInteractor!.execute(_session!, _accountId!));
|
||||
}
|
||||
final canExecute = _getStoredEmailStateInteractor != null
|
||||
&& _session != null
|
||||
&& _accountId != null;
|
||||
if (!canExecute) return;
|
||||
consumeState(_getStoredEmailStateInteractor!.execute(_session!, _accountId!));
|
||||
}
|
||||
|
||||
void _getEmailChangesAction(jmap.State state) {
|
||||
if (_getEmailChangesToPushNotificationInteractor != null &&
|
||||
_accountId != null &&
|
||||
_session != null &&
|
||||
_userName != null) {
|
||||
consumeState(_getEmailChangesToPushNotificationInteractor!.execute(
|
||||
final canExecute = _getEmailChangesToPushNotificationInteractor != null
|
||||
&& _accountId != null
|
||||
&& _session != null
|
||||
&& _userName != null;
|
||||
if (!canExecute) return;
|
||||
consumeState(_getEmailChangesToPushNotificationInteractor!.execute(
|
||||
_session!,
|
||||
_accountId!,
|
||||
_userName!,
|
||||
state,
|
||||
propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(
|
||||
_session!,
|
||||
_accountId!,
|
||||
_userName!,
|
||||
state,
|
||||
propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(
|
||||
_session!,
|
||||
_accountId!,
|
||||
),
|
||||
));
|
||||
}
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
void _storeEmailDeliveryStateAction(AccountId accountId, UserName userName, jmap.State state) {
|
||||
@@ -205,19 +214,12 @@ class EmailChangeListener extends ChangeListener {
|
||||
@override
|
||||
void handleSuccessViewState(Success success) {
|
||||
log('EmailChangeListener::_handleSuccessViewState(): $success');
|
||||
if (success is GetStoredEmailDeliveryStateSuccess && _newStateEmailDelivery != success.state) {
|
||||
_getEmailChangesAction(success.state);
|
||||
if (success is GetStoredEmailDeliveryStateSuccess) {
|
||||
_handleGetStoredEmailDeliveryStateSuccess(success);
|
||||
} else if (success is GetStoredEmailStateSuccess) {
|
||||
_getEmailChangesAction(success.state);
|
||||
} else if (success is GetEmailChangesToPushNotificationSuccess && _newStateEmailDelivery != null) {
|
||||
_storeEmailDeliveryStateAction(success.accountId, success.userName, _newStateEmailDelivery!);
|
||||
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_handleListEmailToPushNotification(
|
||||
userName: success.userName,
|
||||
emailList: success.emailList
|
||||
);
|
||||
}
|
||||
} else if (success is GetEmailChangesToPushNotificationSuccess) {
|
||||
_handleGetEmailChangesToPushNotificationSuccess(success);
|
||||
} else if (success is GetMailboxesNotPutNotificationsSuccess) {
|
||||
final listEmails = _emailsAvailablePushNotification.toEmailsAvailablePushNotification(
|
||||
mailboxIdsNotPutNotifications: success.mailboxes.mailboxIds);
|
||||
@@ -240,18 +242,44 @@ class EmailChangeListener extends ChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
void _handleGetStoredEmailDeliveryStateSuccess(GetStoredEmailDeliveryStateSuccess success) {
|
||||
if (_newStateEmailDelivery != success.state) {
|
||||
_getEmailChangesAction(success.state);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleGetEmailChangesToPushNotificationSuccess(GetEmailChangesToPushNotificationSuccess success) {
|
||||
if (_newStateEmailDelivery == null) return;
|
||||
|
||||
_storeEmailDeliveryStateAction(
|
||||
success.accountId,
|
||||
success.userName,
|
||||
_newStateEmailDelivery!,
|
||||
);
|
||||
|
||||
if (PlatformInfo.isAndroid) {
|
||||
_handleListEmailToPushNotification(
|
||||
userName: success.userName,
|
||||
emailList: success.emailList,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleListEmailToPushNotification({
|
||||
required UserName userName,
|
||||
required List<PresentationEmail> emailList
|
||||
}) {
|
||||
_emailsAvailablePushNotification = emailList;
|
||||
if (_getMailboxesNotPutNotificationsInteractor != null && _accountId != null && _session != null) {
|
||||
final canFilterByMailbox = _getMailboxesNotPutNotificationsInteractor != null
|
||||
&& _accountId != null
|
||||
&& _session != null;
|
||||
if (canFilterByMailbox) {
|
||||
consumeState(_getMailboxesNotPutNotificationsInteractor!.execute(_session!, _accountId!));
|
||||
} else {
|
||||
final listEmails = _emailsAvailablePushNotification.toEmailsAvailablePushNotification();
|
||||
_handleLocalPushNotification(
|
||||
userName: userName,
|
||||
emailList: listEmails
|
||||
emailList: listEmails,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -319,21 +347,21 @@ class EmailChangeListener extends ChangeListener {
|
||||
|
||||
void _getListDetailedEmailByIdAction(Session? session, AccountId accountId, List<EmailId> emailIds) {
|
||||
log('EmailChangeListener::_getListDetailedEmailByIdAction():emailIds: $emailIds');
|
||||
if (_getListDetailedEmailByIdInteractor != null &&
|
||||
_dynamicUrlInterceptors != null &&
|
||||
session != null) {
|
||||
try {
|
||||
final baseDownloadUrl = session.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors!.jmapUrl);
|
||||
consumeState(_getListDetailedEmailByIdInteractor!.execute(
|
||||
session,
|
||||
accountId,
|
||||
emailIds,
|
||||
baseDownloadUrl
|
||||
));
|
||||
} catch (e) {
|
||||
logWarning('EmailChangeListener::_getListDetailedEmailByIdAction(): $e');
|
||||
consumeState(Stream.value(Left(GetDetailedEmailByIdFailure(e))));
|
||||
}
|
||||
final canExecute = _getListDetailedEmailByIdInteractor != null
|
||||
&& _dynamicUrlInterceptors != null
|
||||
&& session != null;
|
||||
if (!canExecute) return;
|
||||
try {
|
||||
final baseDownloadUrl = session.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors!.jmapUrl);
|
||||
consumeState(_getListDetailedEmailByIdInteractor!.execute(
|
||||
session,
|
||||
accountId,
|
||||
emailIds,
|
||||
baseDownloadUrl,
|
||||
));
|
||||
} catch (e) {
|
||||
logWarning('EmailChangeListener::_getListDetailedEmailByIdAction(): $e');
|
||||
consumeState(Stream.value(Left(GetDetailedEmailByIdFailure(e))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,36 @@ import 'package:firebase_messaging/firebase_messaging.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';
|
||||
|
||||
Future<void>? _backgroundInitFuture;
|
||||
|
||||
Future<void> _ensureBackgroundInitialized() {
|
||||
return _backgroundInitFuture ??= (() async {
|
||||
FcmService.instance.initialStreamController();
|
||||
FcmMessageController.instance.initialize();
|
||||
await FcmMessageController.instance
|
||||
.initialAppConfig()
|
||||
.timeout(const Duration(seconds: 10));
|
||||
await FcmMessageController.instance
|
||||
.setUpSentryConfiguration()
|
||||
.timeout(const Duration(seconds: 10));
|
||||
}()).catchError((Object error, StackTrace stackTrace) {
|
||||
_backgroundInitFuture = null;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> handleFirebaseBackgroundMessage(RemoteMessage message) async {
|
||||
FcmService.instance.initialStreamController();
|
||||
FcmMessageController.instance.initialize();
|
||||
FcmService.instance.handleFirebaseBackgroundMessage(message);
|
||||
try {
|
||||
await _ensureBackgroundInitialized();
|
||||
FcmService.instance.handleFirebaseBackgroundMessage(message);
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'FcmReceiver::handleFirebaseBackgroundMessage: throw exception',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FcmReceiver {
|
||||
@@ -34,21 +59,34 @@ class FcmReceiver {
|
||||
final token = await FirebaseMessaging.instance.getToken();
|
||||
log('FcmReceiver::_getInitialToken:token: $token');
|
||||
return token;
|
||||
} catch (e) {
|
||||
logWarning('FcmReceiver::_getInitialToken: TYPE = ${e.runtimeType} | Exception = $e');
|
||||
} catch (e, st) {
|
||||
logError(
|
||||
'FcmReceiver::_getInitialToken:',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future _onHandleFcmToken() async {
|
||||
final token = await _getInitialToken();
|
||||
FcmService.instance.handleToken(token);
|
||||
var currentToken = await _getInitialToken();
|
||||
FcmService.instance.handleToken(currentToken);
|
||||
|
||||
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
|
||||
log('FcmReceiver::_onHandleFcmToken:onTokenRefresh: $newToken');
|
||||
if (newToken != token) {
|
||||
FcmService.instance.handleToken(newToken);
|
||||
FirebaseMessaging.instance.onTokenRefresh.listen(
|
||||
(newToken) {
|
||||
if (newToken != currentToken) {
|
||||
currentToken = newToken;
|
||||
FcmService.instance.handleToken(newToken);
|
||||
}
|
||||
},
|
||||
onError: (e, st) {
|
||||
logError(
|
||||
'FcmReceiver::_onHandleFcmToken:onTokenRefresh:',
|
||||
exception: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,24 @@ class FcmService {
|
||||
fcmTokenStreamController = StreamController<String?>.broadcast();
|
||||
}
|
||||
|
||||
void closeStream() {
|
||||
backgroundMessageStreamController?.close();
|
||||
fcmTokenStreamController?.close();
|
||||
|
||||
backgroundMessageStreamController = null;
|
||||
fcmTokenStreamController = null;
|
||||
Future<void> closeStream() async {
|
||||
try {
|
||||
await backgroundMessageStreamController?.close();
|
||||
} catch (e) {
|
||||
logWarning(
|
||||
'FcmService::closeStream: backgroundMessageStreamController throw exception: $e',
|
||||
);
|
||||
} finally {
|
||||
backgroundMessageStreamController = null;
|
||||
}
|
||||
try {
|
||||
await fcmTokenStreamController?.close();
|
||||
} catch (e) {
|
||||
logWarning(
|
||||
'FcmService::closeStream: fcmTokenStreamController throw exception: $e',
|
||||
);
|
||||
} finally {
|
||||
fcmTokenStreamController = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user