TF-3488 Add hide download taskbar feature

This commit is contained in:
DatDang
2025-02-14 14:43:42 +07:00
committed by Dat H. Pham
parent d51f06f6f0
commit ca3905cb7a
10 changed files with 122 additions and 67 deletions
@@ -11,32 +11,22 @@ class CircleLoadingWidget extends StatelessWidget {
super.key, super.key,
this.size, this.size,
this.padding, this.padding,
this.strokeWidth this.strokeWidth,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (padding != null) { final loadingSize = size ?? CircleLoadingWidgetStyles.size;
return Padding( final loadingStrokeWidth = strokeWidth ?? CircleLoadingWidgetStyles.width;
padding: padding!,
child: SizedBox( return Container(
width: size ?? CircleLoadingWidgetStyles.size, padding: padding,
height: size ?? CircleLoadingWidgetStyles.size, width: loadingSize,
child: CircularProgressIndicator( height: loadingSize,
color: CircleLoadingWidgetStyles.progressColor, child: CircularProgressIndicator(
strokeWidth: strokeWidth ?? CircleLoadingWidgetStyles.width, color: CircleLoadingWidgetStyles.progressColor,
) strokeWidth: loadingStrokeWidth,
), )
); );
} else {
return SizedBox(
width: size ?? CircleLoadingWidgetStyles.size,
height: size ?? CircleLoadingWidgetStyles.size,
child: CircularProgressIndicator(
color: CircleLoadingWidgetStyles.progressColor,
strokeWidth: strokeWidth ?? CircleLoadingWidgetStyles.width,
)
);
}
} }
} }
@@ -1,6 +1,7 @@
import 'dart:typed_data'; import 'dart:typed_data';
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:dio/dio.dart';
import 'package:model/download/download_task_id.dart'; import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
@@ -10,11 +11,12 @@ class StartDownloadAttachmentForWeb extends UIState {
final DownloadTaskId taskId; final DownloadTaskId taskId;
final Attachment attachment; final Attachment attachment;
final CancelToken? cancelToken;
StartDownloadAttachmentForWeb(this.taskId, this.attachment); StartDownloadAttachmentForWeb(this.taskId, this.attachment, [this.cancelToken]);
@override @override
List<Object> get props => [taskId, attachment]; List<Object?> get props => [taskId, attachment, cancelToken];
} }
class DownloadingAttachmentForWeb extends UIState { class DownloadingAttachmentForWeb extends UIState {
@@ -59,13 +61,15 @@ class DownloadAttachmentForWebFailure extends FeatureFailure {
final DownloadTaskId? taskId; final DownloadTaskId? taskId;
final Attachment? attachment; final Attachment? attachment;
final CancelToken? cancelToken;
DownloadAttachmentForWebFailure({ DownloadAttachmentForWebFailure({
this.attachment, this.attachment,
this.taskId, this.taskId,
dynamic exception this.cancelToken,
}) : super(exception: exception); super.exception
});
@override @override
List<Object?> get props => [attachment, taskId, ...super.props]; List<Object?> get props => [attachment, taskId, cancelToken, ...super.props];
} }
@@ -38,8 +38,8 @@ class DownloadAttachmentForWebInteractor {
{CancelToken? cancelToken} {CancelToken? cancelToken}
) async* { ) async* {
try { try {
yield Right<Failure, Success>(StartDownloadAttachmentForWeb(taskId, attachment)); yield Right<Failure, Success>(StartDownloadAttachmentForWeb(taskId, attachment, cancelToken));
onReceiveController.add(Right(StartDownloadAttachmentForWeb(taskId, attachment))); onReceiveController.add(Right(StartDownloadAttachmentForWeb(taskId, attachment, cancelToken)));
final currentAccount = await _accountRepository.getCurrentAccount(); final currentAccount = await _accountRepository.getCurrentAccount();
AccountRequest? accountRequest; AccountRequest? accountRequest;
@@ -77,7 +77,8 @@ class DownloadAttachmentForWebInteractor {
DownloadAttachmentForWebFailure( DownloadAttachmentForWebFailure(
attachment: attachment, attachment: attachment,
taskId: taskId, taskId: taskId,
exception: exception exception: exception,
cancelToken: cancelToken,
) )
); );
} }
@@ -426,9 +426,12 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
(success) { (success) {
if (success is StartDownloadAttachmentForWeb) { if (success is StartDownloadAttachmentForWeb) {
emailSupervisorController.mailboxDashBoardController.addDownloadTask( emailSupervisorController.mailboxDashBoardController.addDownloadTask(
DownloadTaskState( DownloadTaskState(
taskId: success.taskId, taskId: success.taskId,
attachment: success.attachment)); attachment: success.attachment,
onCancel: () => success.cancelToken?.cancel(),
),
);
if (currentOverlayContext != null && currentContext != null) { if (currentOverlayContext != null && currentContext != null) {
appToast.showToastMessage( appToast.showToastMessage(
@@ -896,12 +899,15 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
final baseDownloadUrl = session!.getDownloadUrl( final baseDownloadUrl = session!.getDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl, jmapUrl: dynamicUrlInterceptors.jmapUrl,
); );
final cancelToken = CancelToken();
consumeState(_downloadAttachmentForWebInteractor.execute( consumeState(_downloadAttachmentForWebInteractor.execute(
generateTaskId, generateTaskId,
attachment, attachment,
accountId!, accountId!,
baseDownloadUrl, baseDownloadUrl,
_downloadProgressStateController)); _downloadProgressStateController,
cancelToken: cancelToken,
));
} catch (e) { } catch (e) {
logError('SingleEmailController::downloadAttachmentForWeb(): $e'); logError('SingleEmailController::downloadAttachmentForWeb(): $e');
consumeState(Stream.value(Left(DownloadAttachmentForWebFailure(attachment: attachment, taskId: generateTaskId, exception: e)))); consumeState(Stream.value(Left(DownloadAttachmentForWebFailure(attachment: attachment, taskId: generateTaskId, exception: e))));
@@ -945,13 +951,16 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_updateAttachmentsViewState(failure.attachment?.blobId, Left(failure)); _updateAttachmentsViewState(failure.attachment?.blobId, Left(failure));
} }
if (currentOverlayContext != null && currentContext != null) { if (currentOverlayContext == null || currentContext == null) return;
appToast.showToastErrorMessage(
currentOverlayContext!, String message = AppLocalizations.of(currentContext!).attachment_download_failed;
failure.attachment is EMLAttachment if (failure.attachment is EMLAttachment) {
? AppLocalizations.of(currentContext!).downloadMessageAsEMLFailed message = AppLocalizations.of(currentContext!).downloadMessageAsEMLFailed;
: AppLocalizations.of(currentContext!).attachment_download_failed); } else if (failure.cancelToken?.isCancelled == true) {
message = AppLocalizations.of(currentContext!).downloadAttachmentHasBeenCancelled;
} }
appToast.showToastErrorMessage(currentOverlayContext!, message);
} }
void _updateAttachmentsViewState( void _updateAttachmentsViewState(
@@ -9,12 +9,14 @@ typedef UpdateDownloadTaskStateCallback = DownloadTaskState Function(DownloadTas
class DownloadController extends GetxController { class DownloadController extends GetxController {
final listDownloadTaskState = RxList<DownloadTaskState>(); final listDownloadTaskState = RxList<DownloadTaskState>();
final hideDownloadTaskbar = RxBool(false);
bool get notEmptyListDownloadTask => listDownloadTaskState.isNotEmpty; bool get notEmptyListDownloadTask => listDownloadTaskState.isNotEmpty;
void addDownloadTask(DownloadTaskState task) { void addDownloadTask(DownloadTaskState task) {
log('DownloadController::addDownloadTask(): ${task.taskId}'); log('DownloadController::addDownloadTask(): ${task.taskId}');
listDownloadTaskState.add(task); listDownloadTaskState.add(task);
hideDownloadTaskbar.value = false;
} }
void updateDownloadTaskByTaskId( void updateDownloadTaskByTaskId(
@@ -37,5 +39,8 @@ class DownloadController extends GetxController {
listDownloadTaskState.removeAt(matchIndex); listDownloadTaskState.removeAt(matchIndex);
listDownloadTaskState.refresh(); listDownloadTaskState.refresh();
} }
if (listDownloadTaskState.isEmpty) {
hideDownloadTaskbar.value = true;
}
} }
} }
@@ -202,7 +202,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
? const SearchMailboxView() ? const SearchMailboxView()
: const SizedBox.shrink() : const SizedBox.shrink()
), ),
_buildDownloadTaskStateWidget(), _buildDownloadTaskStateWidget(AppLocalizations.of(context)),
]), ]),
); );
} }
@@ -413,30 +413,50 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
controller.dispatchAction(FilterMessageAction(FilterMessageOption.all)); controller.dispatchAction(FilterMessageAction(FilterMessageOption.all));
} }
Widget _buildDownloadTaskStateWidget() { Widget _buildDownloadTaskStateWidget(AppLocalizations appLocalizations) {
return Obx(() { return Obx(() {
final listDownloadTasks = controller.downloadController.listDownloadTaskState; final listDownloadTasks = controller.downloadController.listDownloadTaskState;
if (listDownloadTasks.isNotEmpty) { final hideDownloadTaskbar = controller.downloadController.hideDownloadTaskbar;
if (listDownloadTasks.isNotEmpty && !hideDownloadTaskbar.value) {
return Align( return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Container( child: Container(
color: AppColor.colorBackgroundSnackBar, color: AppColor.colorBackgroundSnackBar,
height: 60, height: 60,
width: double.infinity, width: double.infinity,
child: ListView.separated( child: Row(
scrollDirection: Axis.horizontal, children: [
padding: EdgeInsets.zero, Expanded(
itemCount: listDownloadTasks.length, child: ListView.separated(
separatorBuilder: (context, index) => scrollDirection: Axis.horizontal,
const Padding( padding: EdgeInsets.zero,
padding: EdgeInsets.symmetric(vertical: 5), itemCount: listDownloadTasks.length,
child: VerticalDivider( separatorBuilder: (context, index) =>
color: Colors.grey, const Padding(
width: 2.5, padding: EdgeInsets.symmetric(vertical: 5),
thickness: 0.2), child: VerticalDivider(
color: Colors.grey,
width: 2.5,
thickness: 0.2),
),
itemBuilder: (context, index) =>
DownloadTaskItemWidget(listDownloadTasks[index])
), ),
itemBuilder: (context, index) => ),
DownloadTaskItemWidget(listDownloadTasks[index]) Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TMailButtonWidget.fromText(
text: appLocalizations.hide,
backgroundColor: Colors.transparent,
textStyle: const TextStyle(
color: AppColor.colorCancelButton,
),
onTapActionCallback: () {
controller.downloadController.hideDownloadTaskbar.value = true;
},
),
),
],
), ),
), ),
); );
@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
import 'package:model/download/download_task_id.dart'; import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
@@ -9,13 +10,15 @@ class DownloadTaskState with EquatableMixin {
final double progress; final double progress;
final int downloaded; final int downloaded;
final int total; final int total;
final VoidCallback? onCancel;
DownloadTaskState({ DownloadTaskState({
required this.taskId, required this.taskId,
required this.attachment, required this.attachment,
this.progress = 0, this.progress = 0,
this.downloaded = 0, this.downloaded = 0,
this.total = 0 this.total = 0,
this.onCancel,
}); });
DownloadTaskState copyWith({ DownloadTaskState copyWith({
@@ -23,14 +26,16 @@ class DownloadTaskState with EquatableMixin {
Attachment? attachment, Attachment? attachment,
double? progress, double? progress,
int? downloaded, int? downloaded,
int? total int? total,
VoidCallback? onCancel
}) { }) {
return DownloadTaskState( return DownloadTaskState(
taskId: taskId ?? this.taskId, taskId: taskId ?? this.taskId,
attachment: attachment ?? this.attachment, attachment: attachment ?? this.attachment,
progress: progress ?? this.progress, progress: progress ?? this.progress,
downloaded: downloaded ?? this.downloaded, downloaded: downloaded ?? this.downloaded,
total: total ?? this.total total: total ?? this.total,
onCancel: onCancel ?? this.onCancel,
); );
} }
@@ -46,5 +51,5 @@ class DownloadTaskState with EquatableMixin {
} }
@override @override
List<Object?> get props => [taskId, attachment, progress, downloaded, total]; List<Object?> get props => [taskId, attachment, progress, downloaded, total, onCancel];
} }
@@ -3,10 +3,12 @@ import 'package:byte_converter/byte_converter.dart';
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:percent_indicator/circular_percent_indicator.dart'; import 'package:percent_indicator/circular_percent_indicator.dart';
import 'package:tmail_ui_user/features/base/widget/circle_loading_widget.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.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';
@@ -43,7 +45,7 @@ class DownloadTaskItemWidget extends StatelessWidget {
fit: BoxFit.fill), fit: BoxFit.fill),
Center( Center(
child: taskState.percentDownloading == 0 child: taskState.percentDownloading == 0
? const CircularProgressIndicator(color: AppColor.primaryColor, strokeWidth: 3) ? const CircleLoadingWidget(strokeWidth: 3)
: CircularPercentIndicator( : CircularPercentIndicator(
percent: taskState.percentDownloading, percent: taskState.percentDownloading,
backgroundColor: AppColor.colorBgMailboxSelected, backgroundColor: AppColor.colorBgMailboxSelected,
@@ -83,7 +85,13 @@ class DownloadTaskItemWidget extends StatelessWidget {
) )
], ],
), ),
) ),
if (taskState.onCancel != null)
TMailButtonWidget.fromIcon(
icon: imagePaths.icClose,
onTapActionCallback: taskState.onCancel,
backgroundColor: Colors.transparent,
),
], ],
), ),
); );
+6
View File
@@ -4211,5 +4211,11 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"downloadAttachmentHasBeenCancelled": "Download attachment has been cancelled",
"@downloadAttachmentHasBeenCancelled": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -4419,4 +4419,11 @@ class AppLocalizations {
name: 'thisHtmlAttachmentCannotBePreviewed', name: 'thisHtmlAttachmentCannotBePreviewed',
); );
} }
String get downloadAttachmentHasBeenCancelled {
return Intl.message(
'Download attachment has been cancelled',
name: 'downloadAttachmentHasBeenCancelled',
);
}
} }