TF-3427 Implement print draft

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-01-15 16:46:50 +07:00
committed by Dat H. Pham
parent 78600f09e9
commit 6fa66efce2
14 changed files with 454 additions and 66 deletions
@@ -1,13 +1,15 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/web/bottom_bar_composer_widget_style.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class BottomBarComposerWidget extends StatelessWidget {
final ImagePaths imagePaths;
final bool isCodeViewEnabled;
final bool isPrintDraftEnabled;
final bool isFormattingOptionsEnabled;
final bool hasReadReceipt;
final VoidCallback openRichToolbarAction;
@@ -18,12 +20,13 @@ class BottomBarComposerWidget extends StatelessWidget {
final VoidCallback saveToDraftAction;
final VoidCallback sendMessageAction;
final VoidCallback requestReadReceiptAction;
final VoidCallback onPrintDraftAction;
final _imagePaths = Get.find<ImagePaths>();
BottomBarComposerWidget({
const BottomBarComposerWidget({
super.key,
required this.imagePaths,
required this.isCodeViewEnabled,
required this.isPrintDraftEnabled,
required this.isFormattingOptionsEnabled,
required this.hasReadReceipt,
required this.openRichToolbarAction,
@@ -34,6 +37,7 @@ class BottomBarComposerWidget extends StatelessWidget {
required this.saveToDraftAction,
required this.sendMessageAction,
required this.requestReadReceiptAction,
required this.onPrintDraftAction,
});
@override
@@ -45,7 +49,7 @@ class BottomBarComposerWidget extends StatelessWidget {
child: Row(
children: [
TMailButtonWidget.fromIcon(
icon: _imagePaths.icRichToolbar,
icon: imagePaths.icRichToolbar,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.richTextIconPadding,
backgroundColor: isFormattingOptionsEnabled
@@ -60,7 +64,7 @@ class BottomBarComposerWidget extends StatelessWidget {
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icAttachFile,
icon: imagePaths.icAttachFile,
iconColor: BottomBarComposerWidgetStyle.iconColor,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
@@ -73,7 +77,7 @@ class BottomBarComposerWidget extends StatelessWidget {
AbsorbPointer(
absorbing: isCodeViewEnabled,
child: TMailButtonWidget.fromIcon(
icon: _imagePaths.icInsertImage,
icon: imagePaths.icInsertImage,
iconColor: BottomBarComposerWidgetStyle.iconColor,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
@@ -85,7 +89,7 @@ class BottomBarComposerWidget extends StatelessWidget {
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icStyleCodeView,
icon: imagePaths.icStyleCodeView,
iconColor: isCodeViewEnabled
? BottomBarComposerWidgetStyle.selectedIconColor
: BottomBarComposerWidgetStyle.iconColor,
@@ -100,7 +104,7 @@ class BottomBarComposerWidget extends StatelessWidget {
),
const Spacer(),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icDeleteMailbox,
icon: imagePaths.icDeleteMailbox,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
@@ -109,7 +113,7 @@ class BottomBarComposerWidget extends StatelessWidget {
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icReadReceipt,
icon: imagePaths.icReadReceipt,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
@@ -122,8 +126,21 @@ class BottomBarComposerWidget extends StatelessWidget {
onTapActionCallback: requestReadReceiptAction,
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
if (isPrintDraftEnabled)
TMailButtonWidget.fromIcon(
icon: imagePaths.icPrinter,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
iconColor: AppColor.steelGrayA540,
margin: const EdgeInsetsDirectional.only(
end: BottomBarComposerWidgetStyle.space,
),
tooltipMessage: AppLocalizations.of(context).print,
onTapActionCallback: onPrintDraftAction,
),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icSaveToDraft,
icon: imagePaths.icSaveToDraft,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
@@ -133,7 +150,7 @@ class BottomBarComposerWidget extends StatelessWidget {
const SizedBox(width: BottomBarComposerWidgetStyle.sendButtonSpace),
TMailButtonWidget(
text: AppLocalizations.of(context).send,
icon: _imagePaths.icSend,
icon: imagePaths.icSend,
iconAlignment: TextDirection.rtl,
padding: BottomBarComposerWidgetStyle.sendButtonPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
@@ -1,7 +1,6 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/mobile_app_bar_composer_widget_style.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -9,6 +8,7 @@ typedef OnOpenContextMenuAction = Function(RelativeRect position);
class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
final ImagePaths imagePaths;
final bool isCodeViewEnabled;
final bool isSendButtonEnabled;
final bool isFormattingOptionsEnabled;
@@ -19,10 +19,9 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
final OnOpenContextMenuAction openContextMenuAction;
final VoidCallback openRichToolbarAction;
final _imagePaths = Get.find<ImagePaths>();
MobileResponsiveAppBarComposerWidget({
const MobileResponsiveAppBarComposerWidget({
super.key,
required this.imagePaths,
required this.isCodeViewEnabled,
required this.isFormattingOptionsEnabled,
required this.isSendButtonEnabled,
@@ -43,7 +42,7 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
child: Row(
children: [
TMailButtonWidget.fromIcon(
icon: _imagePaths.icCancel,
icon: imagePaths.icCancel,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).saveAndClose,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
@@ -52,7 +51,7 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
),
const Spacer(),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icRichToolbar,
icon: imagePaths.icRichToolbar,
padding: MobileAppBarComposerWidgetStyle.richTextIconPadding,
backgroundColor: isFormattingOptionsEnabled
? MobileAppBarComposerWidgetStyle.selectedBackgroundColor
@@ -66,7 +65,7 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icAttachFile,
icon: imagePaths.icAttachFile,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
backgroundColor: Colors.transparent,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
@@ -76,7 +75,7 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
if (!isCodeViewEnabled)
TMailButtonWidget.fromIcon(
icon: _imagePaths.icInsertImage,
icon: imagePaths.icInsertImage,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
backgroundColor: Colors.transparent,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
@@ -86,8 +85,8 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: isSendButtonEnabled
? _imagePaths.icSendMobile
: _imagePaths.icSendDisable,
? imagePaths.icSendMobile
: imagePaths.icSendDisable,
backgroundColor: Colors.transparent,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
iconSize: MobileAppBarComposerWidgetStyle.sendButtonIconSize,
@@ -96,7 +95,7 @@ class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icMore,
icon: imagePaths.icMore,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
backgroundColor: Colors.transparent,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
@@ -0,0 +1,202 @@
import 'dart:async';
import 'dart:math';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/email/domain/model/email_print.dart';
import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class PrintDraftDialogView extends StatefulWidget {
final EmailPrint emailPrint;
final PrintEmailInteractor printEmailInteractor;
const PrintDraftDialogView({
super.key,
required this.emailPrint,
required this.printEmailInteractor,
});
@override
State<PrintDraftDialogView> createState() => _PrintDraftDialogViewState();
}
class _PrintDraftDialogViewState extends State<PrintDraftDialogView> {
StreamSubscription? _streamSubscription;
final ValueNotifier<dartz.Either<Failure, Success>?> _viewStateNotifier = ValueNotifier(null);
@override
void initState() {
super.initState();
_streamSubscription = widget.printEmailInteractor
.execute(widget.emailPrint)
.listen(
_handleDataStream,
onError: _handleErrorStream
);
}
void _handleDataStream(dartz.Either<Failure, Success> newState) {
_viewStateNotifier.value = newState;
newState.fold(
(failure) {
if (failure is PrintEmailFailure) {
popBack(result: failure);
}
},
(success) {
if (success is PrintEmailSuccess) {
popBack(result: success);
}
}
);
}
void _handleErrorStream(Object error, StackTrace stackTrace) {
logError('_PrintDraftDialogViewState::_handleErrorStream: Exception = $error');
popBack(result: PrintEmailFailure(exception: error));
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
insetPadding: const EdgeInsets.symmetric(
horizontal: 24.0,
vertical: 16.0
),
alignment: Alignment.center,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: Colors.white,
),
width: min(context.width, 400),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: double.infinity,
clipBehavior: Clip.antiAlias,
padding: const EdgeInsetsDirectional.symmetric(vertical: 8, horizontal: 12),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: AppColor.colorItemSelected,
),
alignment: Alignment.center,
child: Text(
AppLocalizations.of(context).print,
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 17
),
),
),
const Divider(),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsetsDirectional.only(start: 16, end: 16, top: 12, bottom: 4),
child: Row(
children: [
Text(
'${AppLocalizations.of(context).status}:',
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 14
),
),
const SizedBox(width: 8),
Expanded(
child: ValueListenableBuilder(
valueListenable: _viewStateNotifier,
builder: (context, value, child) {
if (value == null) {
return child!;
}
return value.fold(
(failure) => child!,
(success) {
return Text(
'${_getStatusMessage(success)}...',
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: AppColor.labelColor,
fontSize: 14
),
);
}
);
},
child: Text(
'...',
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: AppColor.labelColor,
fontSize: 14
),
),
),
)
],
),
),
Padding(
padding: const EdgeInsetsDirectional.only(start: 16, end: 16, top: 4, bottom: 16),
child: Row(
children: [
Text(
'${AppLocalizations.of(context).progress}:',
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 14
),
),
const SizedBox(width: 8),
Expanded(
child: LinearProgressIndicator(
color: Colors.white.withOpacity(0.6),
backgroundColor: AppColor.primaryColor,
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
)
],
),
),
],
)
],
),
),
);
}
String _getStatusMessage(Success success) {
if (success is PrintEmailLoading) {
return AppLocalizations.of(context).printingInProgress;
} else {
return AppLocalizations.of(context).printingFailed;
}
}
@override
void dispose() {
_streamSubscription?.cancel();
_viewStateNotifier.dispose();
super.dispose();
}
}