TF-3719 Add handle export log to device if need
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -99,6 +99,7 @@ export 'presentation/views/avatar/gradient_circle_avatar_icon.dart';
|
||||
export 'presentation/views/loading/cupertino_loading_widget.dart';
|
||||
export 'presentation/views/image/image_loader_mixin.dart';
|
||||
export 'presentation/views/pull_to_refresh/pull_to_refresh_widget.dart';
|
||||
export 'presentation/views/button/multi_click_widget.dart';
|
||||
|
||||
// Resources
|
||||
export 'presentation/resources/assets_paths.dart';
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class FileException with EquatableMixin implements Exception {
|
||||
final String message;
|
||||
|
||||
FileException(this.message);
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
class NotFoundFileInFolderException extends FileException {
|
||||
NotFoundFileInFolderException() : super('No files found in the folder');
|
||||
}
|
||||
|
||||
class UserCancelShareFileException extends FileException {
|
||||
UserCancelShareFileException() : super('User cancel share file');
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../utils/app_logger.dart';
|
||||
|
||||
class MultiClickWidget extends StatefulWidget {
|
||||
final Widget child;
|
||||
final int requiredClicks;
|
||||
final Duration interval;
|
||||
final VoidCallback onMultiTap;
|
||||
|
||||
const MultiClickWidget({
|
||||
Key? key,
|
||||
required this.child,
|
||||
required this.onMultiTap,
|
||||
this.requiredClicks = 7,
|
||||
this.interval = const Duration(seconds: 2),
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MultiClickWidget> createState() => _MultiClickWidgetState();
|
||||
}
|
||||
|
||||
class _MultiClickWidgetState extends State<MultiClickWidget> {
|
||||
int _clickCount = 0;
|
||||
DateTime? _lastClickTime;
|
||||
|
||||
void _handleTap() {
|
||||
final now = DateTime.now();
|
||||
if (_lastClickTime == null ||
|
||||
now.difference(_lastClickTime!) > widget.interval) {
|
||||
_clickCount = 1;
|
||||
} else {
|
||||
_clickCount++;
|
||||
}
|
||||
|
||||
_lastClickTime = now;
|
||||
log('_MultiClickWidgetState::_handleTap: ClickCount: $_clickCount');
|
||||
if (_clickCount >= widget.requiredClicks) {
|
||||
_clickCount = 0;
|
||||
widget.onMultiTap();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: _handleTap,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,10 @@ import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:core/domain/exceptions/download_file_exception.dart';
|
||||
import 'package:core/domain/exceptions/file_exception.dart';
|
||||
import 'package:core/utils/logger/trace_log.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
@@ -147,4 +150,78 @@ class LogTracking {
|
||||
|
||||
return await file.writeAsString(content, mode: fileMode);
|
||||
}
|
||||
}
|
||||
|
||||
Future<TraceLog> getTraceLog() async {
|
||||
final folderPath = await _getInternalStorageDirPath(folderPath: logFolder);
|
||||
final directory = Directory(folderPath);
|
||||
if (directory.existsSync()) {
|
||||
final directoryInfo = await getDirInfo(directory);
|
||||
return TraceLog(
|
||||
path: folderPath,
|
||||
size: directoryInfo.$1,
|
||||
listFilePaths: directoryInfo.$2,
|
||||
);
|
||||
} else {
|
||||
throw Exception('Trace folder not exist');
|
||||
}
|
||||
}
|
||||
|
||||
Future<(int, List<String>)> getDirInfo(Directory dir) async {
|
||||
var files = await dir.list(recursive: true).toList();
|
||||
var dirSize = files.fold(0, (int sum, file) => sum + file.statSync().size);
|
||||
var listPath = files.map((file) => file.path).toList();
|
||||
return (dirSize, listPath);
|
||||
}
|
||||
|
||||
static Future<String> getExternalDocumentPath({String? folderPath}) async {
|
||||
Directory directory = Directory('');
|
||||
if (Platform.isAndroid) {
|
||||
if (folderPath?.isNotEmpty == true) {
|
||||
directory = Directory('/storage/emulated/0/Download/$folderPath');
|
||||
} else {
|
||||
directory = Directory('/storage/emulated/0/Download');
|
||||
}
|
||||
} else {
|
||||
directory = await getApplicationDocumentsDirectory();
|
||||
if (folderPath?.isNotEmpty == true) {
|
||||
directory = Directory('${directory.absolute.path}/$folderPath');
|
||||
}
|
||||
}
|
||||
|
||||
final exPath = directory.path;
|
||||
await Directory(exPath).create(recursive: true);
|
||||
return exPath;
|
||||
}
|
||||
|
||||
static Future<String> copyInternalFilesToDownloadExternal(List<String> listFilePaths) async {
|
||||
final externalPath = await getExternalDocumentPath();
|
||||
|
||||
List<String> externalListPaths = [];
|
||||
for (var filePath in listFilePaths) {
|
||||
final file = File(filePath);
|
||||
final fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
|
||||
final externalFile = File('$externalPath/$fileName');
|
||||
await externalFile.writeAsBytes(file.readAsBytesSync());
|
||||
externalListPaths.add(externalFile.path);
|
||||
}
|
||||
|
||||
if (externalListPaths.isNotEmpty) {
|
||||
return externalPath;
|
||||
} else {
|
||||
throw NotFoundFileInFolderException();
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> exportTraceLog(TraceLog traceLog) async {
|
||||
if (PlatformInfo.isIOS) {
|
||||
final savePath = getExternalDocumentPath(folderPath: logFolder);
|
||||
return savePath;
|
||||
} else {
|
||||
final savePath = await compute(
|
||||
copyInternalFilesToDownloadExternal,
|
||||
traceLog.listFilePaths,
|
||||
);
|
||||
return savePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class TraceLog with EquatableMixin {
|
||||
final String path;
|
||||
final int size;
|
||||
final List<String> listFilePaths;
|
||||
|
||||
TraceLog({
|
||||
required this.path,
|
||||
required this.size,
|
||||
required this.listFilePaths
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [path, size, listFilePaths];
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
abstract class TraceLogDataSource {
|
||||
Future<String> exportTraceLog();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
import 'package:core/data/utils/device_manager.dart';
|
||||
import 'package:core/domain/exceptions/file_exception.dart';
|
||||
import 'package:core/utils/logger/log_tracking.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/data/datasource/trace_log_datasource.dart';
|
||||
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
|
||||
import 'package:tmail_ui_user/main/exceptions/permission_exception.dart';
|
||||
import 'package:tmail_ui_user/main/permissions/permission_service.dart';
|
||||
|
||||
class TraceLogDataSourceImpl extends TraceLogDataSource {
|
||||
|
||||
final LogTracking _logTracking;
|
||||
final DeviceManager _deviceManager;
|
||||
final PermissionService _permissionService;
|
||||
final ExceptionThrower _exceptionThrower;
|
||||
|
||||
TraceLogDataSourceImpl(
|
||||
this._logTracking,
|
||||
this._deviceManager,
|
||||
this._permissionService,
|
||||
this._exceptionThrower,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<String> exportTraceLog() {
|
||||
return Future.sync(() async {
|
||||
if (PlatformInfo.isAndroid) {
|
||||
final permissionGranted = await _validateStoragePermissionOnAndroid();
|
||||
if (permissionGranted) {
|
||||
final traceLog = await _logTracking.getTraceLog();
|
||||
return await _logTracking.exportTraceLog(traceLog);
|
||||
} else {
|
||||
throw const NotGrantedPermissionStorageException();
|
||||
}
|
||||
} else {
|
||||
final traceLog = await _logTracking.getTraceLog();
|
||||
final savePath = await _logTracking.exportTraceLog(traceLog);
|
||||
final result = await Share.shareXFiles([XFile(savePath)]);
|
||||
if (result.status == ShareResultStatus.success) {
|
||||
return savePath;
|
||||
}
|
||||
throw UserCancelShareFileException();
|
||||
}
|
||||
}).catchError(_exceptionThrower.throwException);
|
||||
}
|
||||
|
||||
Future<bool> _validateStoragePermissionOnAndroid() async {
|
||||
final needRequestPermission = await _deviceManager.isNeedRequestStoragePermissionOnAndroid();
|
||||
if (needRequestPermission) {
|
||||
final isGranted = await _permissionService.isGranted(Permission.storage);
|
||||
return isGranted;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
abstract class TraceLogRepository {
|
||||
Future<String> exportTraceLog();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:tmail_ui_user/features/manage_account/data/datasource/trace_log_datasource.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/data/repository/trace_log_repository.dart';
|
||||
|
||||
class TraceLogRepositoryImpl extends TraceLogRepository {
|
||||
final TraceLogDataSource _traceLogDataSource;
|
||||
|
||||
TraceLogRepositoryImpl(this._traceLogDataSource);
|
||||
|
||||
@override
|
||||
Future<String> exportTraceLog() {
|
||||
return _traceLogDataSource.exportTraceLog();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
|
||||
class ExportTraceLogLoading extends LoadingState {}
|
||||
|
||||
class ExportTraceLogSuccess extends UIState {
|
||||
|
||||
String savePath;
|
||||
|
||||
ExportTraceLogSuccess(this.savePath);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [savePath];
|
||||
}
|
||||
|
||||
class ExportTraceLogFailure extends FeatureFailure {
|
||||
|
||||
ExportTraceLogFailure(dynamic exception) : super(exception: exception);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/data/repository/trace_log_repository.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart';
|
||||
|
||||
class ExportTraceLogInteractor {
|
||||
final TraceLogRepository _traceLogRepository;
|
||||
|
||||
ExportTraceLogInteractor(this._traceLogRepository);
|
||||
|
||||
Stream<Either<Failure, Success>> execute() async* {
|
||||
try {
|
||||
yield Right<Failure, Success>(ExportTraceLogLoading());
|
||||
final savePath = await _traceLogRepository.exportTraceLog();
|
||||
yield Right<Failure, Success>(ExportTraceLogSuccess(savePath));
|
||||
} catch (exception) {
|
||||
yield Left<Failure, Success>(ExportTraceLogFailure(exception));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/export_trace_log_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/manage_account_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/menu/trace_log_interactor_bindings.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
extension ExportTraceLogExtension on ManageAccountDashBoardController {
|
||||
|
||||
void injectTraceLogDependencies() {
|
||||
TraceLogInteractorBindings().dependencies();
|
||||
}
|
||||
|
||||
void disposeTraceLogDependencies() {
|
||||
TraceLogInteractorBindings().dispose();
|
||||
}
|
||||
|
||||
Future<void> showExportTraceLogConfirmDialog(BuildContext context) async {
|
||||
await showConfirmDialogAction(
|
||||
context,
|
||||
AppLocalizations.of(context).messageExportTraceLogDialog,
|
||||
AppLocalizations.of(context).yes,
|
||||
title: AppLocalizations.of(context).exportTraceLog,
|
||||
alignCenter: true,
|
||||
outsideDismissible: false,
|
||||
useIconAsBasicLogo: false,
|
||||
cancelTitle: AppLocalizations.of(context).no,
|
||||
onConfirmAction: _exportTraceLog,
|
||||
);
|
||||
}
|
||||
|
||||
void _exportTraceLog() {
|
||||
final exportTraceLogInteractor = getBinding<ExportTraceLogInteractor>();
|
||||
if (exportTraceLogInteractor != null) {
|
||||
consumeState(exportTraceLogInteractor.execute());
|
||||
}
|
||||
}
|
||||
|
||||
void handleExportTraceLogSuccess(ExportTraceLogSuccess success) {
|
||||
toastManager.showMessageSuccess(success);
|
||||
}
|
||||
|
||||
void handleExportTraceLogFailure(ExportTraceLogFailure failure) {
|
||||
toastManager.showMessageFailure(failure);
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@ import 'package:server_settings/server_settings/capability_server_settings.dart'
|
||||
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
|
||||
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/user_setting_popup_menu_mixin.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_vacation_state.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/update_vacation_state.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_vacation_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/action/dashboard_setting_action.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/bindings/email_rules_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/export_trace_log_extension.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/forward/bindings/forward_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/language_and_region/language_and_region_bindings.dart';
|
||||
@@ -60,6 +62,9 @@ class ManageAccountDashBoardController extends ReloadableController with UserSet
|
||||
void onInit() {
|
||||
BackButtonInterceptor.add(_onBackButtonInterceptor, name: AppRoutes.settings);
|
||||
super.onInit();
|
||||
if (AppConfig.isApiLoggingEnabled) {
|
||||
injectTraceLogDependencies();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -77,11 +82,22 @@ class ManageAccountDashBoardController extends ReloadableController with UserSet
|
||||
}
|
||||
} else if (success is UpdateVacationSuccess) {
|
||||
_handleUpdateVacationSuccess(success);
|
||||
} else if (success is ExportTraceLogSuccess) {
|
||||
handleExportTraceLogSuccess(success);
|
||||
} else {
|
||||
super.handleSuccessViewState(success);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleFailureViewState(Failure failure) {
|
||||
if (failure is ExportTraceLogFailure) {
|
||||
handleExportTraceLogFailure(failure);
|
||||
} else {
|
||||
super.handleFailureViewState(failure);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void handleReloaded(Session session) {
|
||||
log('ManageAccountDashBoardController::handleReloaded:');
|
||||
@@ -400,6 +416,9 @@ class ManageAccountDashBoardController extends ReloadableController with UserSet
|
||||
@override
|
||||
void onClose() {
|
||||
BackButtonInterceptor.removeByName(AppRoutes.settings);
|
||||
if (AppConfig.isApiLoggingEnabled) {
|
||||
disposeTraceLogDependencies();
|
||||
}
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/utils/style_utils.dart';
|
||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||
import 'package:core/presentation/views/button/multi_click_widget.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/email_rules_view.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/export_trace_log_extension.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/forward/forward_view.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/language_and_region/language_and_region_view.dart';
|
||||
@@ -21,6 +23,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/profiles/prof
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_view.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/utils/app_config.dart';
|
||||
|
||||
typedef CloseSettingsViewAction = void Function();
|
||||
|
||||
@@ -84,7 +87,7 @@ class SettingsView extends GetWidget<SettingsController> {
|
||||
}
|
||||
|
||||
Widget _buildUniversalSettingAppBar(BuildContext context) {
|
||||
return Stack(
|
||||
final appBarWidget = Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Positioned(left: 0,child: _buildCloseSettingButton(context)),
|
||||
@@ -98,10 +101,23 @@ class SettingsView extends GetWidget<SettingsController> {
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: AppColor.colorNameEmail,
|
||||
fontWeight: FontWeight.w700))
|
||||
)
|
||||
]
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (AppConfig.isApiLoggingEnabled) {
|
||||
return MultiClickWidget(
|
||||
onMultiTap: () => controller
|
||||
.manageAccountDashboardController
|
||||
.showExportTraceLogConfirmDialog(context),
|
||||
child: appBarWidget,
|
||||
);
|
||||
} else {
|
||||
return appBarWidget;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildSettingLevel1AppBar(BuildContext context) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:core/data/utils/device_manager.dart';
|
||||
import 'package:core/utils/logger/log_tracking.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tmail_ui_user/features/base/interactors_bindings.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/data/datasource/trace_log_datasource.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/data/datasource_impl/trace_log_datasource_impl.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/data/repository/trace_log_repository.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/repository/trace_log_repository_impl.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/export_trace_log_interactor.dart';
|
||||
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
|
||||
import 'package:tmail_ui_user/main/permissions/permission_service.dart';
|
||||
|
||||
class TraceLogInteractorBindings extends InteractorsBindings {
|
||||
@override
|
||||
void bindingsDataSource() {
|
||||
Get.lazyPut<TraceLogDataSource>(() => Get.find<TraceLogDataSourceImpl>());
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsDataSourceImpl() {
|
||||
Get.lazyPut(() => LogTracking());
|
||||
Get.lazyPut(() => TraceLogDataSourceImpl(
|
||||
Get.find<LogTracking>(),
|
||||
Get.find<DeviceManager>(),
|
||||
Get.find<PermissionService>(),
|
||||
Get.find<CacheExceptionThrower>(),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsInteractor() {
|
||||
Get.lazyPut(() => ExportTraceLogInteractor(Get.find<TraceLogRepository>()));
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsRepository() {
|
||||
Get.lazyPut<TraceLogRepository>(() => Get.find<TraceLogRepositoryImpl>());
|
||||
}
|
||||
|
||||
@override
|
||||
void bindingsRepositoryImpl() {
|
||||
Get.lazyPut(() => TraceLogRepositoryImpl(Get.find<TraceLogDataSource>()));
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
Get.delete<ExportTraceLogInteractor>();
|
||||
Get.delete<TraceLogRepository>();
|
||||
Get.delete<TraceLogRepositoryImpl>();
|
||||
Get.delete<TraceLogDataSource>();
|
||||
Get.delete<TraceLogDataSourceImpl>();
|
||||
Get.delete<LogTracking>();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"@@last_modified": "2025-04-14T04:16:15.237055",
|
||||
"@@last_modified": "2025-05-16T20:14:59.453269",
|
||||
"initializing_data": "Initializing data...",
|
||||
"@initializing_data": {
|
||||
"type": "text",
|
||||
@@ -4469,5 +4469,45 @@
|
||||
"placeholders": {
|
||||
"description": {}
|
||||
}
|
||||
},
|
||||
"exportTraceLog": "Export trace log",
|
||||
"@exportTraceLog": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"messageExportTraceLogDialog": "The mobile API error message log has been tracked. Do you want to export it?",
|
||||
"@messageExportTraceLogDialog": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"youNeedToGrantFilesPermissionToExportFile": "You need to grant files permission to export files",
|
||||
"@youNeedToGrantFilesPermissionToExportFile": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"exportTraceLogSuccess": "Export successful tracking logs at \"{path}\"",
|
||||
"@exportTraceLogSuccess": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"path"
|
||||
],
|
||||
"placeholders": {
|
||||
"path": {}
|
||||
}
|
||||
},
|
||||
"noLogsHaveBeenRecordedYet": "No logs have been recorded yet.",
|
||||
"@noLogsHaveBeenRecordedYet": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"exportTraceLogFailed": "Export trace log failed",
|
||||
"@exportTraceLogFailed": {
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import 'package:tmail_ui_user/features/base/before_reconnect_manager.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart';
|
||||
import 'package:tmail_ui_user/features/caching/utils/session_storage_manager.dart';
|
||||
import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart';
|
||||
import 'package:tmail_ui_user/main/permissions/permission_service.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/ios_notification_manager.dart';
|
||||
@@ -59,6 +60,7 @@ class CoreBindings extends Bindings {
|
||||
void _bindingDeviceManager() {
|
||||
Get.put(DeviceInfoPlugin());
|
||||
Get.put(DeviceManager(Get.find<DeviceInfoPlugin>()));
|
||||
Get.put(PermissionService());
|
||||
}
|
||||
|
||||
void _bindingReceivingSharingStream() {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class PermissionException with EquatableMixin implements Exception {
|
||||
|
||||
final String? message;
|
||||
|
||||
const PermissionException({this.message});
|
||||
}
|
||||
|
||||
class NotGrantedPermissionStorageException extends PermissionException {
|
||||
const NotGrantedPermissionStorageException() : super(message: 'Permission Storage has not been granted access');
|
||||
|
||||
@override
|
||||
List<Object?> get props => [super.message];
|
||||
}
|
||||
@@ -4702,4 +4702,47 @@ class AppLocalizations {
|
||||
args: [description],
|
||||
);
|
||||
}
|
||||
|
||||
String get exportTraceLog {
|
||||
return Intl.message(
|
||||
'Export trace log',
|
||||
name: 'exportTraceLog',
|
||||
);
|
||||
}
|
||||
|
||||
String get messageExportTraceLogDialog {
|
||||
return Intl.message(
|
||||
'The mobile API error message log has been tracked. Do you want to export it?',
|
||||
name: 'messageExportTraceLogDialog',
|
||||
);
|
||||
}
|
||||
|
||||
String get youNeedToGrantFilesPermissionToExportFile {
|
||||
return Intl.message(
|
||||
'You need to grant files permission to export files',
|
||||
name: 'youNeedToGrantFilesPermissionToExportFile',
|
||||
);
|
||||
}
|
||||
|
||||
String exportTraceLogSuccess(String path) {
|
||||
return Intl.message(
|
||||
'Export successful tracking logs at "$path"',
|
||||
name: 'exportTraceLogSuccess',
|
||||
args: [path],
|
||||
);
|
||||
}
|
||||
|
||||
String get noLogsHaveBeenRecordedYet {
|
||||
return Intl.message(
|
||||
'No logs have been recorded yet.',
|
||||
name: 'noLogsHaveBeenRecordedYet',
|
||||
);
|
||||
}
|
||||
|
||||
String get exportTraceLogFailed {
|
||||
return Intl.message(
|
||||
'Export trace log failed',
|
||||
name: 'exportTraceLogFailed',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:core/domain/exceptions/file_exception.dart';
|
||||
import 'package:core/domain/exceptions/web_session_exception.dart';
|
||||
import 'package:core/presentation/state/failure.dart';
|
||||
import 'package:core/presentation/state/success.dart';
|
||||
@@ -19,12 +20,14 @@ import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'
|
||||
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
|
||||
import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart';
|
||||
import 'package:tmail_ui_user/features/starting_page/domain/state/sign_in_twake_workplace_state.dart';
|
||||
import 'package:tmail_ui_user/features/starting_page/domain/state/sign_up_twake_workplace_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/empty_spam_folder_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_state.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart';
|
||||
import 'package:tmail_ui_user/main/error/error_method_response_exception_extension.dart';
|
||||
import 'package:tmail_ui_user/main/exceptions/permission_exception.dart';
|
||||
import 'package:tmail_ui_user/main/exceptions/remote_exception.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
@@ -132,6 +135,14 @@ class ToastManager {
|
||||
} else {
|
||||
message = AppLocalizations.of(currentContext!).previewEmailFromEMLFileFailed;
|
||||
}
|
||||
} else if (failure is ExportTraceLogFailure) {
|
||||
if (failure.exception is NotGrantedPermissionStorageException) {
|
||||
message = AppLocalizations.of(currentContext!).youNeedToGrantFilesPermissionToExportFile;
|
||||
} else if (failure.exception is NotFoundFileInFolderException) {
|
||||
message = AppLocalizations.of(currentContext!).noLogsHaveBeenRecordedYet;
|
||||
} else {
|
||||
message = AppLocalizations.of(currentContext!).exportTraceLogFailed;
|
||||
}
|
||||
}
|
||||
|
||||
if (message?.isNotEmpty == true) {
|
||||
@@ -151,6 +162,8 @@ class ToastManager {
|
||||
success is EmptySpamFolderSuccess ||
|
||||
success is EmptyTrashFolderSuccess) {
|
||||
message = AppLocalizations.of(currentContext!).toast_message_empty_trash_folder_success;
|
||||
} else if (success is ExportTraceLogSuccess) {
|
||||
message = AppLocalizations.of(currentContext!).exportTraceLogSuccess(success.savePath);
|
||||
}
|
||||
|
||||
if (message?.isNotEmpty == true) {
|
||||
|
||||
Reference in New Issue
Block a user