diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index c8f39b7db..263e4857a 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -98,7 +98,6 @@ class ComposerController extends BaseController with DragDropFileMixin { final networkConnectionController = Get.find(); final _dynamicUrlInterceptors = Get.find(); - final expandModeAttachments = ExpandMode.EXPAND.obs; final composerArguments = Rxn(); final isEnableEmailSendButton = false.obs; final isInitialRecipient = false.obs; @@ -1320,12 +1319,6 @@ class ComposerController extends BaseController with DragDropFileMixin { mailboxDashBoardController.closeComposerOverlay(); } - void toggleDisplayAttachments() { - final newExpandMode = expandModeAttachments.value == ExpandMode.COLLAPSE - ? ExpandMode.EXPAND : ExpandMode.COLLAPSE; - expandModeAttachments.value = newExpandMode; - } - void addEmailAddressType(PrefixEmailAddress prefixEmailAddress) { switch(prefixEmailAddress) { case PrefixEmailAddress.from: diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 7c408ae9b..c42f3b650 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -185,6 +185,16 @@ class ComposerView extends GetWidget { margin: ComposerStyle.mobileSubjectMargin, onTapOutside: controller.onTapOutsideSubject, ), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return MobileAttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, + ); + } else { + return const SizedBox.shrink(); + } + }), Obx(() => Center( child: InsertImageLoadingBarWidget( uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, @@ -201,16 +211,6 @@ class ComposerView extends GetWidget { onLoadCompletedEditorAction: controller.onLoadCompletedMobileEditorAction, ), )), - Obx(() { - if (controller.uploadController.listUploadAttachments.isNotEmpty) { - return MobileAttachmentComposerWidget( - listFileUploaded: controller.uploadController.listUploadAttachments, - onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), - ); - } else { - return const SizedBox.shrink(); - } - }), const SizedBox(height: ComposerStyle.keyboardMaxHeight), ], ), @@ -331,6 +331,16 @@ class ComposerView extends GetWidget { margin: ComposerStyle.mobileSubjectMargin, onTapOutside: controller.onTapOutsideSubject, ), + Obx(() { + if (controller.uploadController.listUploadAttachments.isNotEmpty) { + return MobileAttachmentComposerWidget( + listFileUploaded: controller.uploadController.listUploadAttachments, + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, + ); + } else { + return const SizedBox.shrink(); + } + }), Obx(() => Center( child: InsertImageLoadingBarWidget( uploadInlineViewState: controller.uploadController.uploadInlineViewState.value, @@ -347,16 +357,6 @@ class ComposerView extends GetWidget { onLoadCompletedEditorAction: controller.onLoadCompletedMobileEditorAction, ), )), - Obx(() { - if (controller.uploadController.listUploadAttachments.isNotEmpty) { - return MobileAttachmentComposerWidget( - listFileUploaded: controller.uploadController.listUploadAttachments, - onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), - ); - } else { - return const SizedBox.shrink(); - } - }) ], ), ) diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 394f99125..8a49da6b6 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -190,7 +190,7 @@ class ComposerView extends GetWidget { return AttachmentComposerWidget( listFileUploaded: controller.uploadController.listUploadAttachments, isCollapsed: controller.isAttachmentCollapsed, - onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, ); } else { @@ -423,7 +423,7 @@ class ComposerView extends GetWidget { return AttachmentComposerWidget( listFileUploaded: controller.uploadController.listUploadAttachments, isCollapsed: controller.isAttachmentCollapsed, - onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, ); } else { @@ -675,7 +675,7 @@ class ComposerView extends GetWidget { return AttachmentComposerWidget( listFileUploaded: controller.uploadController.listUploadAttachments, isCollapsed: controller.isAttachmentCollapsed, - onDeleteAttachmentAction: (fileState) => controller.deleteAttachmentUploaded(fileState.uploadTaskId), + onDeleteAttachmentAction: controller.deleteAttachmentUploaded, onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed, ); } else { diff --git a/lib/features/composer/presentation/widgets/attachment_item_composer_widget.dart b/lib/features/composer/presentation/widgets/attachment_item_composer_widget.dart index c77b6e7a4..548b93474 100644 --- a/lib/features/composer/presentation/widgets/attachment_item_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/attachment_item_composer_widget.dart @@ -9,9 +9,10 @@ import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_item_composer_widget_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_progress_loading_composer_widget.dart'; +import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; -typedef OnDeleteAttachmentAction = void Function(UploadFileState fileState); +typedef OnDeleteAttachmentAction = void Function(UploadTaskId uploadTaskId); class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin { @@ -100,7 +101,7 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin { borderRadius: AttachmentItemComposerWidgetStyle.deleteIconRadius, padding: AttachmentItemComposerWidgetStyle.deleteIconPadding, iconColor: AttachmentItemComposerWidgetStyle.deleteIconColor, - onTapActionCallback: () => onDeleteAttachmentAction?.call(fileState), + onTapActionCallback: () => onDeleteAttachmentAction?.call(fileState.uploadTaskId), ) ], ), diff --git a/lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart b/lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart index e219c6f3f..5c14f4bc4 100644 --- a/lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/mobile/mobile_attachment_composer_widget.dart @@ -1,4 +1,7 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -6,20 +9,47 @@ import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_i import 'package:tmail_ui_user/features/composer/presentation/styles/mobile/mobile_attachment_composer_widget_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_item_composer_widget.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; +import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class MobileAttachmentComposerWidget extends StatelessWidget { +class MobileAttachmentComposerWidget extends StatefulWidget { final List listFileUploaded; final OnDeleteAttachmentAction onDeleteAttachmentAction; - final _responsiveUtils = Get.find(); - - MobileAttachmentComposerWidget({ + const MobileAttachmentComposerWidget({ super.key, required this.listFileUploaded, required this.onDeleteAttachmentAction, }); + @override + State createState() => _MobileAttachmentComposerWidgetState(); +} + +class _MobileAttachmentComposerWidgetState extends State { + static const int _maxCountDisplayedAttachments = 2; + + final _responsiveUtils = Get.find(); + final _imagePaths = Get.find(); + + List _listFileDisplayed = []; + bool _isCollapsed = false; + + @override + void initState() { + super.initState(); + _updateListFileDisplayed(); + } + + @override + void didUpdateWidget(covariant MobileAttachmentComposerWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.listFileUploaded != widget.listFileUploaded) { + _updateListFileDisplayed(); + } + } + @override Widget build(BuildContext context) { return Container( @@ -33,10 +63,9 @@ class MobileAttachmentComposerWidget extends StatelessWidget { SizedBox( width: _responsiveUtils.getSizeScreenWidth(context) * 0.7, child: GridView.builder( - reverse: true, primary: false, shrinkWrap: true, - itemCount: listFileUploaded.length, + itemCount: _listFileDisplayed.length, gridDelegate: const SliverGridDelegateFixedHeight( height: MobileAttachmentComposerWidgetStyle.listItemHeight, crossAxisCount: MobileAttachmentComposerWidgetStyle.maxItemRow, @@ -44,32 +73,93 @@ class MobileAttachmentComposerWidget extends StatelessWidget { ), itemBuilder: (context, index) { return AttachmentItemComposerWidget( - fileState: listFileUploaded[index], + fileState: _listFileDisplayed[index], itemMargin: MobileAttachmentComposerWidgetStyle.itemMargin, - onDeleteAttachmentAction: onDeleteAttachmentAction + onDeleteAttachmentAction: widget.onDeleteAttachmentAction ); } ), ) else - SizedBox( - width: AttachmentItemComposerWidgetStyle.width, - child: ListView.builder( - reverse: true, - shrinkWrap: true, - primary: false, - itemCount: listFileUploaded.length, - itemBuilder: (context, index) { - return AttachmentItemComposerWidget( - fileState: listFileUploaded[index], - itemMargin: MobileAttachmentComposerWidgetStyle.itemMargin, - onDeleteAttachmentAction: onDeleteAttachmentAction - ); - } + ...[ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Flexible(child: SizedBox( + width: AttachmentItemComposerWidgetStyle.width, + child: ListView.builder( + shrinkWrap: true, + primary: false, + itemCount: _listFileDisplayed.length, + itemBuilder: (context, index) { + return AttachmentItemComposerWidget( + fileState: _listFileDisplayed[index], + itemMargin: MobileAttachmentComposerWidgetStyle.itemMargin, + onDeleteAttachmentAction: widget.onDeleteAttachmentAction + ); + } + ), + )), + if (!_isCollapsed && _isExceededDisplayedAttachments) + TMailButtonWidget( + text: AppLocalizations.of(context).showLess, + icon: _imagePaths.icChevronUp, + iconAlignment: TextDirection.rtl, + iconSpace: 2, + iconSize: 24, + iconColor: AppColor.primaryColor, + backgroundColor: Colors.transparent, + textStyle: Theme.of(context).textTheme.labelSmall?.copyWith( + fontSize: 15, + color: AppColor.primaryColor + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + margin: const EdgeInsetsDirectional.only(start: 8), + onTapActionCallback: _toggleListAttachments + ) + ], ), - ) + if (_isCollapsed && _isExceededDisplayedAttachments) + TMailButtonWidget.fromText( + text: AppLocalizations.of(context).showMore(_countRemainingAttachments), + backgroundColor: Colors.transparent, + textStyle: Theme.of(context).textTheme.labelSmall?.copyWith( + fontSize: 15, + color: AppColor.primaryColor + ), + margin: const EdgeInsetsDirectional.only(top: 5), + onTapActionCallback: _toggleListAttachments + ) + ] ] ), ); } + + bool get _listFileUploadedSuccess => + widget.listFileUploaded.every((uploadFile) => uploadFile.uploadStatus.completed); + + bool get _isExceededDisplayedAttachments => + _listFileUploadedSuccess && + widget.listFileUploaded.length > _maxCountDisplayedAttachments; + + int get _countRemainingAttachments => + widget.listFileUploaded.length - _maxCountDisplayedAttachments; + + void _updateListFileDisplayed() { + final reversedList = widget.listFileUploaded.reversed.toList(); + if (_isCollapsed) { + _listFileDisplayed = reversedList.sublist(0, _maxCountDisplayedAttachments); + } else { + _listFileDisplayed = reversedList; + } + } + + void _toggleListAttachments() { + setState(() { + _isCollapsed = !_isCollapsed; + _updateListFileDisplayed(); + }); + } } diff --git a/lib/features/composer/presentation/widgets/recipient_composer_widget.dart b/lib/features/composer/presentation/widgets/recipient_composer_widget.dart index b7c77eee4..d2427489e 100644 --- a/lib/features/composer/presentation/widgets/recipient_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/recipient_composer_widget.dart @@ -338,9 +338,9 @@ class _RecipientComposerWidgetState extends State { TMailButtonWidget.fromIcon( icon: _isAllRecipientInputEnabled ? _imagePaths.icChevronUp - : _imagePaths.icChevronDown, + : _imagePaths.icChevronDownOutline, backgroundColor: Colors.transparent, - iconSize: 20, + iconSize: 24, padding: const EdgeInsets.all(5), iconColor: AppColor.colorLabelComposer, margin: RecipientComposerWidgetStyle.enableRecipientButtonMargin, diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 4fc020a49..b563d04a0 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2024-03-13T01:12:22.224880", + "@@last_modified": "2024-03-19T12:10:23.549474", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -3857,5 +3857,21 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "showMore": "Show more (+{count})", + "@showMore": { + "type": "text", + "placeholders_order": [ + "count" + ], + "placeholders": { + "count": {} + } + }, + "showLess": "Show less", + "@showLess": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index f2147bf8e..5dcf3481c 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4030,4 +4030,17 @@ class AppLocalizations { name: 'mailToAttendees' ); } + + String showMore(int count) { + return Intl.message( + 'Show more (+$count)', + name: 'showMore', + args: [count]); + } + + String get showLess { + return Intl.message( + 'Show less', + name: 'showLess'); + } } \ No newline at end of file