TF-4075 Apply new design for attachments view
This commit is contained in:
@@ -1532,17 +1532,6 @@ class ComposerController extends BaseController
|
||||
subjectEmailInputFocusNode?.unfocus();
|
||||
}
|
||||
|
||||
void clearFocusRecipients() {
|
||||
toAddressFocusNode?.unfocus();
|
||||
ccAddressFocusNode?.unfocus();
|
||||
bccAddressFocusNode?.unfocus();
|
||||
replyToAddressFocusNode?.unfocus();
|
||||
}
|
||||
|
||||
void clearFocusSubject() {
|
||||
subjectEmailInputFocusNode?.unfocus();
|
||||
}
|
||||
|
||||
void _closeSuggestionBox() {
|
||||
if (toEmailAddressController.text.isEmpty) {
|
||||
keyToEmailTagEditor.currentState?.closeSuggestionBox();
|
||||
|
||||
@@ -233,7 +233,8 @@ class EmailView extends GetWidget<SingleEmailController> {
|
||||
List<String>? emailAddressSender,
|
||||
ScrollController? scrollController,
|
||||
}) {
|
||||
final isMobile = controller.responsiveUtils.isMobile(context);
|
||||
final isMobileResponsive = controller.responsiveUtils.isMobile(context);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -286,7 +287,7 @@ class EmailView extends GetWidget<SingleEmailController> {
|
||||
controller.mailboxDashBoardController.mapMailboxById,
|
||||
),
|
||||
)),
|
||||
if (!isMobile)
|
||||
if (!isMobileResponsive)
|
||||
const SizedBox(height: 16),
|
||||
Obx(() => MailUnsubscribedBanner(
|
||||
presentationEmail: controller.currentEmail,
|
||||
@@ -295,7 +296,7 @@ class EmailView extends GetWidget<SingleEmailController> {
|
||||
Obx(() => EmailViewLoadingBarWidget(
|
||||
viewState: controller.emailLoadedViewState.value
|
||||
)),
|
||||
if (!isMobile)
|
||||
if (!isMobileResponsive)
|
||||
_buildAttachmentsList(context),
|
||||
if (calendarEvent != null)
|
||||
Column(
|
||||
@@ -440,7 +441,7 @@ class EmailView extends GetWidget<SingleEmailController> {
|
||||
height: 5,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
if (isMobile)
|
||||
if (isMobileResponsive)
|
||||
_buildAttachmentsList(context),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -2,6 +2,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/mail/mail_address.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get_utils/src/get_utils/get_utils.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
@@ -22,9 +23,10 @@ import 'package:tmail_ui_user/main/routes/route_utils.dart';
|
||||
|
||||
class EmailUtils {
|
||||
static const double desktopItemMaxWidth = 260;
|
||||
static const double desktopMoreButtonMaxWidth = 70;
|
||||
static const double desktopMoreButtonMaxWidth = 150;
|
||||
static const double attachmentItemSpacing = 8;
|
||||
static const double attachmentItemHeight = 36;
|
||||
static const double attachmentIcon = 20;
|
||||
static const int maxMobileVisibleAttachments = 3;
|
||||
|
||||
EmailUtils._();
|
||||
@@ -244,30 +246,38 @@ class EmailUtils {
|
||||
required double maxWidth,
|
||||
required List<Attachment> attachments,
|
||||
required bool isMobile,
|
||||
int maxVisibleAttachments = EmailUtils.maxMobileVisibleAttachments,
|
||||
double attachmentItemWidth = EmailUtils.desktopItemMaxWidth,
|
||||
double attachmentItemSpacing = EmailUtils.attachmentItemSpacing,
|
||||
double showMoreButtonMaxWidth = EmailUtils.desktopMoreButtonMaxWidth,
|
||||
double attachmentIcon = EmailUtils.attachmentIcon,
|
||||
}) {
|
||||
if (attachments.isEmpty) return [];
|
||||
|
||||
if (isMobile) {
|
||||
return attachments.length <= maxMobileVisibleAttachments
|
||||
return attachments.length <= maxVisibleAttachments
|
||||
? attachments
|
||||
: attachments.sublist(0, maxMobileVisibleAttachments);
|
||||
: attachments.sublist(0, maxVisibleAttachments);
|
||||
}
|
||||
|
||||
final totalNeededWidth = attachments.length * desktopItemMaxWidth +
|
||||
final totalNeededWidth = attachments.length * attachmentItemWidth +
|
||||
(attachments.length - 1) * attachmentItemSpacing;
|
||||
if (totalNeededWidth <= maxWidth) {
|
||||
return attachments;
|
||||
}
|
||||
|
||||
final availableWidth =
|
||||
maxWidth - desktopMoreButtonMaxWidth - attachmentItemSpacing;
|
||||
final availableWidth = maxWidth -
|
||||
showMoreButtonMaxWidth -
|
||||
attachmentIcon -
|
||||
attachmentItemSpacing * 4;
|
||||
|
||||
log('EmailUtils::getAttachmentDisplayed: availableWidth = $availableWidth, maxWidth = $maxWidth, showMoreButtonMaxWidth = $showMoreButtonMaxWidth, attachmentIcon = $attachmentIcon, attachmentItemSpacing = $attachmentItemSpacing');
|
||||
double usedWidth = 0;
|
||||
int visibleCount = 0;
|
||||
|
||||
for (int i = 0; i < attachments.length; i++) {
|
||||
final nextWidth =
|
||||
desktopItemMaxWidth + (i > 0 ? attachmentItemSpacing : 0);
|
||||
attachmentItemWidth + (i > 0 ? attachmentItemSpacing : 0);
|
||||
if (usedWidth + nextWidth <= availableWidth) {
|
||||
usedWidth += nextWidth;
|
||||
visibleCount++;
|
||||
@@ -281,6 +291,29 @@ class EmailUtils {
|
||||
return attachments.sublist(0, visibleCount);
|
||||
}
|
||||
|
||||
static double estimateTextWidth({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
TextStyle? textStyle,
|
||||
Locale? locale,
|
||||
}) {
|
||||
try {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: text,
|
||||
style: textStyle ?? DefaultTextStyle.of(context).style,
|
||||
locale: locale,
|
||||
),
|
||||
maxLines: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
return textPainter.width;
|
||||
} catch (e) {
|
||||
return desktopMoreButtonMaxWidth;
|
||||
}
|
||||
}
|
||||
|
||||
static String getDomainByEmailAddress(String emailAddress) {
|
||||
try {
|
||||
MailAddress mailAddress = MailAddress.validateAddress(emailAddress);
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'package:core/presentation/utils/theme_utils.dart';
|
||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||
import 'package:core/presentation/views/container/tmail_container_widget.dart';
|
||||
import 'package:core/presentation/views/text/middle_ellipsis_text.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:filesize/filesize.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
@@ -69,26 +68,15 @@ class AttachmentItemWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
|
||||
final attachmentTitleWithEndDots = Text(
|
||||
attachment.generateFileName(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: ThemeUtils.textStyleM3LabelLarge(
|
||||
color: AppColor.m3SurfaceBackground,
|
||||
),
|
||||
);
|
||||
|
||||
final bodyItemWidget = Row(
|
||||
children: [
|
||||
isLoading ? loadingIndicator : attachmentIcon,
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: PlatformInfo.isCanvasKit
|
||||
? attachmentTitleWithMiddleDots
|
||||
: attachmentTitleWithEndDots,
|
||||
child: attachmentTitleWithMiddleDots,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsetsDirectional.only(start: 8, end: 3),
|
||||
child: Text(
|
||||
filesize(attachment.size?.value),
|
||||
maxLines: 1,
|
||||
@@ -97,10 +85,10 @@ class AttachmentItemWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
TMailButtonWidget.fromIcon(
|
||||
icon: imagePaths.icFileDownload,
|
||||
icon: imagePaths.icDownloadAttachment,
|
||||
backgroundColor: Colors.transparent,
|
||||
padding: const EdgeInsets.all(5),
|
||||
iconColor: AppColor.steelGrayA540,
|
||||
iconColor: AppColor.primaryColor,
|
||||
iconSize: 20,
|
||||
onTapActionCallback:
|
||||
isLoading ? null : () => _onTapDownloadAction(attachment),
|
||||
|
||||
@@ -68,8 +68,10 @@ class AttachmentsInfo extends StatelessWidget {
|
||||
),
|
||||
if (onTapDownloadAllButton != null)
|
||||
TMailButtonWidget(
|
||||
text: AppLocalizations.of(context).archiveAndDownload,
|
||||
icon: imagePaths.icDownloadAll,
|
||||
text: AppLocalizations.of(context).downloadAll,
|
||||
icon: !responsiveUtils.isMobile(context)
|
||||
? imagePaths.icDownloadAttachment
|
||||
: imagePaths.icDownloadAll,
|
||||
iconSize: 20,
|
||||
iconColor: AppColor.steelGrayA540,
|
||||
iconAlignment: TextDirection.rtl,
|
||||
|
||||
@@ -62,31 +62,21 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
: null,
|
||||
);
|
||||
|
||||
bool isMobile = responsiveUtils.isMobile(context);
|
||||
bool isMobileResponsive = responsiveUtils.isMobile(context);
|
||||
|
||||
final hideButton = SizedBox(
|
||||
height: EmailUtils.attachmentItemHeight,
|
||||
width: isMobile ? double.infinity : null,
|
||||
child: ConfirmDialogButton(
|
||||
label: AppLocalizations.of(context).hideAll,
|
||||
backgroundColor: Theme.of(context).colorScheme.outline.withValues(
|
||||
alpha: 0.08,
|
||||
),
|
||||
textStyle: ThemeUtils.textStyleM3TitleSmall.copyWith(
|
||||
color: AppColor.steelGrayA540,
|
||||
),
|
||||
radius: 5,
|
||||
onTapAction: onTapHideAllAttachments,
|
||||
),
|
||||
);
|
||||
|
||||
if (isMobile) {
|
||||
if (isMobileResponsive) {
|
||||
final attachmentRecord = _getDisplayedAndHiddenAttachment(
|
||||
isMobile,
|
||||
context,
|
||||
isMobileResponsive,
|
||||
responsiveUtils.getDeviceWidth(context),
|
||||
);
|
||||
final displayedAttachments = attachmentRecord.displayedAttachments;
|
||||
final hiddenItemsCount = attachmentRecord.hiddenItemsCount;
|
||||
|
||||
final displayedAttachments = isDisplayAllAttachments
|
||||
? attachments
|
||||
: attachmentRecord.displayedAttachments;
|
||||
final hiddenItemsCount = isDisplayAllAttachments
|
||||
? 0
|
||||
: attachmentRecord.hiddenItemsCount;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsetsDirectional.only(
|
||||
@@ -108,6 +98,7 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
margin: const EdgeInsets.only(
|
||||
top: EmailUtils.attachmentItemSpacing,
|
||||
),
|
||||
width: EmailUtils.desktopItemMaxWidth,
|
||||
downloadAttachmentAction: downloadAttachmentAction,
|
||||
viewAttachmentAction: viewAttachmentAction,
|
||||
singleEmailControllerTag: singleEmailControllerTag,
|
||||
@@ -119,23 +110,37 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
if (hiddenItemsCount > 0)
|
||||
SizedBox(
|
||||
height: EmailUtils.attachmentItemHeight,
|
||||
width: double.infinity,
|
||||
child: ConfirmDialogButton(
|
||||
label: AppLocalizations.of(context).moreAttachments(
|
||||
icon: imagePaths.icAttachment,
|
||||
iconSize: 16,
|
||||
iconColor: AppColor.steelGrayA540,
|
||||
label: AppLocalizations.of(context).showMoreAttachmentButton(
|
||||
hiddenItemsCount,
|
||||
),
|
||||
backgroundColor: Theme.of(context).colorScheme.outline.withValues(
|
||||
alpha: 0.08,
|
||||
),
|
||||
textStyle: ThemeUtils.textStyleM3TitleSmall.copyWith(
|
||||
color: AppColor.steelGrayA540,
|
||||
textStyle: ThemeUtils.textStyleBodyBody1(
|
||||
color: AppColor.steelGray400,
|
||||
),
|
||||
radius: 5,
|
||||
onTapAction: onTapShowAllAttachmentFile,
|
||||
),
|
||||
),
|
||||
if (isDisplayAllAttachments)
|
||||
hideButton,
|
||||
SizedBox(
|
||||
height: EmailUtils.attachmentItemHeight,
|
||||
child: ConfirmDialogButton(
|
||||
icon: imagePaths.icAttachment,
|
||||
iconSize: 16,
|
||||
iconColor: AppColor.steelGrayA540,
|
||||
label: AppLocalizations.of(context).hideAttachmentButton(
|
||||
attachmentRecord.hiddenItemsCount,
|
||||
),
|
||||
textStyle: ThemeUtils.textStyleBodyBody1(
|
||||
color: AppColor.steelGray400,
|
||||
),
|
||||
radius: 5,
|
||||
onTapAction: onTapHideAllAttachments,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -163,11 +168,16 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final attachmentRecord = _getDisplayedAndHiddenAttachment(
|
||||
isMobile,
|
||||
context,
|
||||
isMobileResponsive,
|
||||
constraints.maxWidth,
|
||||
);
|
||||
final displayedAttachments = attachmentRecord.displayedAttachments;
|
||||
final hiddenItemsCount = attachmentRecord.hiddenItemsCount;
|
||||
final displayedAttachments = isDisplayAllAttachments
|
||||
? attachments
|
||||
: attachmentRecord.displayedAttachments;
|
||||
final hiddenItemsCount = isDisplayAllAttachments
|
||||
? 0
|
||||
: attachmentRecord.hiddenItemsCount;
|
||||
|
||||
return Wrap(
|
||||
spacing: EmailUtils.attachmentItemSpacing,
|
||||
@@ -204,19 +214,36 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
SizedBox(
|
||||
height: EmailUtils.attachmentItemHeight,
|
||||
child: ConfirmDialogButton(
|
||||
label: '+$hiddenItemsCount',
|
||||
backgroundColor: Theme.of(context).colorScheme.outline.withValues(
|
||||
alpha: 0.08,
|
||||
icon: imagePaths.icAttachment,
|
||||
iconSize: 16,
|
||||
iconColor: AppColor.steelGrayA540,
|
||||
label: AppLocalizations.of(context).showMoreAttachmentButton(
|
||||
hiddenItemsCount,
|
||||
),
|
||||
textStyle: ThemeUtils.textStyleM3TitleSmall.copyWith(
|
||||
color: AppColor.steelGrayA540,
|
||||
textStyle: ThemeUtils.textStyleBodyBody1(
|
||||
color: AppColor.steelGray400,
|
||||
),
|
||||
radius: 5,
|
||||
onTapAction: onTapShowAllAttachmentFile,
|
||||
),
|
||||
),
|
||||
if (isDisplayAllAttachments)
|
||||
hideButton,
|
||||
SizedBox(
|
||||
height: EmailUtils.attachmentItemHeight,
|
||||
child: ConfirmDialogButton(
|
||||
icon: imagePaths.icAttachment,
|
||||
iconSize: 16,
|
||||
iconColor: AppColor.steelGrayA540,
|
||||
label: AppLocalizations.of(context).hideAttachmentButton(
|
||||
attachmentRecord.hiddenItemsCount,
|
||||
),
|
||||
textStyle: ThemeUtils.textStyleBodyBody1(
|
||||
color: AppColor.steelGray400,
|
||||
),
|
||||
radius: 5,
|
||||
onTapAction: onTapHideAllAttachments,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -229,15 +256,25 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
({List<Attachment> displayedAttachments, int hiddenItemsCount})
|
||||
_getDisplayedAndHiddenAttachment(bool isMobile, double maxWidth) {
|
||||
if (isDisplayAllAttachments) {
|
||||
return (displayedAttachments: attachments, hiddenItemsCount: 0);
|
||||
}
|
||||
_getDisplayedAndHiddenAttachment(
|
||||
BuildContext context,
|
||||
bool isMobile,
|
||||
double maxWidth,
|
||||
) {
|
||||
final showMoreButtonMaxWidth = EmailUtils.estimateTextWidth(
|
||||
context: context,
|
||||
text: AppLocalizations.of(context).showMoreAttachmentButton(999),
|
||||
textStyle: ThemeUtils.textStyleBodyBody1(
|
||||
color: AppColor.steelGray400,
|
||||
),
|
||||
locale: Localizations.localeOf(context),
|
||||
);
|
||||
|
||||
final displayedAttachments = EmailUtils.getAttachmentDisplayed(
|
||||
maxWidth: maxWidth,
|
||||
attachments: attachments,
|
||||
isMobile: isMobile,
|
||||
showMoreButtonMaxWidth: showMoreButtonMaxWidth,
|
||||
);
|
||||
|
||||
int hiddenItemsCount = attachments.length - displayedAttachments.length;
|
||||
@@ -246,6 +283,7 @@ class EmailAttachmentsWidget extends StatelessWidget {
|
||||
} else if (hiddenItemsCount < 0) {
|
||||
hiddenItemsCount = 0;
|
||||
}
|
||||
|
||||
log('EmailAttachmentsWidget::_getDisplayedAndHiddenAttachment: Displayed: ${displayedAttachments.length}, Hidden: $hiddenItemsCount');
|
||||
return (
|
||||
displayedAttachments: displayedAttachments,
|
||||
|
||||
+21
-21
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"@@last_modified": "2025-09-19T09:56:48.738620",
|
||||
"@@last_modified": "2025-10-07T12:02:25.245876",
|
||||
"initializing_data": "Initializing data...",
|
||||
"@initializing_data": {
|
||||
"type": "text",
|
||||
@@ -3330,16 +3330,6 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"moreAttachments": "+ {count} more",
|
||||
"@moreAttachments": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"attachmentList": "Attachment list",
|
||||
"@attachmentList": {
|
||||
"type": "text",
|
||||
@@ -3940,16 +3930,6 @@
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"showMore": "Show more (+{count})",
|
||||
"@showMore": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"showLess": "Show less",
|
||||
"@showLess": {
|
||||
"type": "text",
|
||||
@@ -4887,5 +4867,25 @@
|
||||
"type": "text",
|
||||
"placeholders_order": [],
|
||||
"placeholders": {}
|
||||
},
|
||||
"showMoreAttachmentButton": "Show +{count} more",
|
||||
"@showMoreAttachmentButton": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"hideAttachmentButton": "Hide {count}",
|
||||
"@hideAttachmentButton": {
|
||||
"type": "text",
|
||||
"placeholders_order": [
|
||||
"count"
|
||||
],
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3419,14 +3419,6 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
String moreAttachments(int count) {
|
||||
return Intl.message(
|
||||
'+ $count more',
|
||||
name: 'moreAttachments',
|
||||
args: [count]
|
||||
);
|
||||
}
|
||||
|
||||
String get attachmentList {
|
||||
return Intl.message(
|
||||
'Attachment list',
|
||||
@@ -4099,13 +4091,6 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
String showMore(int count) {
|
||||
return Intl.message(
|
||||
'Show more (+$count)',
|
||||
name: 'showMore',
|
||||
args: [count]);
|
||||
}
|
||||
|
||||
String get showLess {
|
||||
return Intl.message(
|
||||
'Show less',
|
||||
@@ -5160,4 +5145,20 @@ class AppLocalizations {
|
||||
name: 'spamReportToggleDescription',
|
||||
);
|
||||
}
|
||||
|
||||
String showMoreAttachmentButton(int count) {
|
||||
return Intl.message(
|
||||
'Show +$count more',
|
||||
name: 'showMoreAttachmentButton',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
|
||||
String hideAttachmentButton(int count) {
|
||||
return Intl.message(
|
||||
'Hide $count',
|
||||
name: 'hideAttachmentButton',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ void main() {
|
||||
attachments: attachments,
|
||||
isMobile: false,
|
||||
);
|
||||
expect(result, attachments.sublist(0, 3));
|
||||
expect(result, attachments.sublist(0, 2));
|
||||
});
|
||||
|
||||
test(
|
||||
|
||||
Reference in New Issue
Block a user