TF-4093 Make interface more compact

This commit is contained in:
dab246
2025-10-16 10:00:41 +07:00
committed by Dat H. Pham
parent 804f5e2a02
commit 7d80557582
23 changed files with 373 additions and 272 deletions
@@ -93,7 +93,6 @@ extension AppColor on Color {
static const colorDividerEmailView = Color(0xFFD7D8D9); static const colorDividerEmailView = Color(0xFFD7D8D9);
static const colorButton = Color(0xFF959DAD); static const colorButton = Color(0xFF959DAD);
static const colorTime = Color(0xFF92A1B4); static const colorTime = Color(0xFF92A1B4);
static const colorEmailAddressPrefix = Color(0xFF9AA7B6);
static const colorEmailAddressTag = Color(0xFFF4F4F4); static const colorEmailAddressTag = Color(0xFFF4F4F4);
static const colorLineLeftEmailView = Color(0x2999A2AD); static const colorLineLeftEmailView = Color(0x2999A2AD);
static const colorShadowComposer = Color(0x1F000000); static const colorShadowComposer = Color(0x1F000000);
@@ -258,6 +257,7 @@ extension AppColor on Color {
static const gray424244 = Color(0xFF424244); static const gray424244 = Color(0xFF424244);
static const lightGrayF4F4F4 = Color(0xFFF4F4F4); static const lightGrayF4F4F4 = Color(0xFFF4F4F4);
static const gray959DAD = Color(0xFF959DAD); static const gray959DAD = Color(0xFF959DAD);
static const gray9AA7B6 = Color(0xFF9AA7B6);
static const redFF3347 = Color(0xFFFF3347); static const redFF3347 = Color(0xFFFF3347);
static const gray686E76 = Color(0xFF686E76); static const gray686E76 = Color(0xFF686E76);
static const lightGrayEBEDF0 = Color(0xFFEBEDF0); static const lightGrayEBEDF0 = Color(0xFFEBEDF0);
@@ -142,6 +142,14 @@ class ThemeUtils {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
); );
static TextStyle textStyleBodySmallHeadline = defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w600,
letterSpacing: -0.13,
fontSize: 13,
height: 16 / 13,
color: Colors.black,
);
static TextStyle textStyleHeadingH6({ static TextStyle textStyleHeadingH6({
Color? color, Color? color,
FontWeight? fontWeight, FontWeight? fontWeight,
@@ -23,6 +23,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget {
final TextDirection? direction; final TextDirection? direction;
final double? contentPadding; final double? contentPadding;
final bool useDefaultFontStyle; final bool useDefaultFontStyle;
final double fontSize;
/// Handler for mailto: links /// Handler for mailto: links
final OnMailtoClicked? mailtoDelegate; final OnMailtoClicked? mailtoDelegate;
@@ -60,6 +61,7 @@ class HtmlContentViewerOnWeb extends StatefulWidget {
this.keepAlive = false, this.keepAlive = false,
this.disableScrolling = false, this.disableScrolling = false,
this.autoAdjustHeight = false, this.autoAdjustHeight = false,
this.fontSize = 14,
this.htmlContentMinHeight = ConstantsUI.htmlContentMinHeight, this.htmlContentMinHeight = ConstantsUI.htmlContentMinHeight,
this.htmlContentMinWidth = ConstantsUI.htmlContentMinWidth, this.htmlContentMinWidth = ConstantsUI.htmlContentMinWidth,
this.offsetHtmlContentHeight = ConstantsUI.htmlContentOffsetHeight, this.offsetHtmlContentHeight = ConstantsUI.htmlContentOffsetHeight,
@@ -406,6 +408,7 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb>
direction: widget.direction, direction: widget.direction,
contentPadding: widget.contentPadding, contentPadding: widget.contentPadding,
useDefaultFontStyle: widget.useDefaultFontStyle, useDefaultFontStyle: widget.useDefaultFontStyle,
fontSize: widget.fontSize,
); );
return htmlTemplate; return htmlTemplate;
@@ -30,6 +30,7 @@ class HtmlContentViewer extends StatefulWidget {
final bool keepWidthWhileLoading; final bool keepWidthWhileLoading;
final double? contentPadding; final double? contentPadding;
final bool useDefaultFontStyle; final bool useDefaultFontStyle;
final double fontSize;
final double? maxHtmlContentHeight; final double? maxHtmlContentHeight;
final double htmlContentMinHeight; final double htmlContentMinHeight;
final double offsetHtmlContentHeight; final double offsetHtmlContentHeight;
@@ -58,6 +59,7 @@ class HtmlContentViewer extends StatefulWidget {
this.contentPadding, this.contentPadding,
this.useDefaultFontStyle = false, this.useDefaultFontStyle = false,
this.disableScrolling = false, this.disableScrolling = false,
this.fontSize = 16,
this.maxViewHeight, this.maxViewHeight,
this.maxHtmlContentHeight, this.maxHtmlContentHeight,
this.onLoadWidthHtmlViewer, this.onLoadWidthHtmlViewer,
@@ -151,6 +153,7 @@ class HtmlContentViewState extends State<HtmlContentViewer> with AutomaticKeepAl
styleCSS: combinedCss, styleCSS: combinedCss,
contentPadding: widget.contentPadding, contentPadding: widget.contentPadding,
useDefaultFontStyle: widget.useDefaultFontStyle, useDefaultFontStyle: widget.useDefaultFontStyle,
fontSize: widget.fontSize,
); );
} }
@@ -54,6 +54,7 @@ class _IosHtmlContentViewerWidgetState extends State<IosHtmlContentViewerWidget>
direction: widget.direction, direction: widget.direction,
javaScripts: HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage, javaScripts: HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage,
useDefaultFontStyle: widget.useDefaultFontStyle, useDefaultFontStyle: widget.useDefaultFontStyle,
fontSize: 16,
)); ));
} }
@@ -18,6 +18,7 @@ class AvatarBuilder {
List<Color>? _avatarColors; List<Color>? _avatarColors;
List<BoxShadow>? _boxShadows; List<BoxShadow>? _boxShadows;
TextStyle? _textStyle; TextStyle? _textStyle;
EdgeInsetsGeometry? _padding;
void key(Key key) { void key(Key key) {
_key = key; _key = key;
@@ -55,6 +56,10 @@ class AvatarBuilder {
_textStyle = textStyle; _textStyle = textStyle;
} }
void addPadding(EdgeInsetsGeometry? padding) {
_padding = padding;
}
void addOnTapActionClick(OnTapAvatarActionClick onTapAvatarActionClick) { void addOnTapActionClick(OnTapAvatarActionClick onTapAvatarActionClick) {
_onTapAvatarActionClick = onTapAvatarActionClick; _onTapAvatarActionClick = onTapAvatarActionClick;
} }
@@ -64,7 +69,7 @@ class AvatarBuilder {
} }
Widget build() { Widget build() {
return InkWell( final bodyWidget = InkWell(
onTap: _onTapAvatarActionClick, onTap: _onTapAvatarActionClick,
onTapDown: (detail) { onTapDown: (detail) {
if (_onTapAvatarActionWithPositionClick != null && _context != null) { if (_onTapAvatarActionWithPositionClick != null && _context != null) {
@@ -102,5 +107,11 @@ class AvatarBuilder {
) )
), ),
); );
if (_padding != null) {
return Padding(padding: _padding!, child: bodyWidget);
} else {
return bodyWidget;
}
} }
} }
+4 -5
View File
@@ -103,12 +103,11 @@ class HtmlTemplate {
} }
'''; ''';
static const String defaultFontStyle = ''' static String defaultFontStyle({double fontSize = 14}) => '''
div, p, span, th, td, tr, ul, ol, li, a, button { body {
font-weight: 400; font-weight: 400;
font-size: 16px; font-size: ${fontSize}px;
line-height: 24px; font-style: normal;
letter-spacing: -0.01em; /* -1% */
} }
p { p {
+2 -1
View File
@@ -123,6 +123,7 @@ class HtmlUtils {
String? javaScripts, String? javaScripts,
bool hideScrollBar = true, bool hideScrollBar = true,
bool useDefaultFontStyle = false, bool useDefaultFontStyle = false,
double fontSize = 14,
TextDirection? direction, TextDirection? direction,
double? contentPadding, double? contentPadding,
}) { }) {
@@ -135,7 +136,7 @@ class HtmlUtils {
<style> <style>
${HtmlTemplate.fontFaceStyle} ${HtmlTemplate.fontFaceStyle}
${useDefaultFontStyle ? HtmlTemplate.defaultFontStyle : ''} ${useDefaultFontStyle ? HtmlTemplate.defaultFontStyle(fontSize: fontSize) : ''}
.tmail-content { .tmail-content {
min-height: ${minHeight ?? 0}px; min-height: ${minHeight ?? 0}px;
@@ -11,12 +11,16 @@ class EmailAvatarBuilder extends StatelessWidget {
final PresentationEmail emailSelected; final PresentationEmail emailSelected;
final OnTapAvatarActionClick? onTapAvatarActionClick; final OnTapAvatarActionClick? onTapAvatarActionClick;
final double? size; final double? size;
final TextStyle? textStyle;
final EdgeInsetsGeometry? padding;
const EmailAvatarBuilder({ const EmailAvatarBuilder({
Key? key, Key? key,
required this.emailSelected, required this.emailSelected,
this.onTapAvatarActionClick, this.onTapAvatarActionClick,
this.size, this.size,
this.textStyle,
this.padding,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -24,8 +28,9 @@ class EmailAvatarBuilder extends StatelessWidget {
return (AvatarBuilder() return (AvatarBuilder()
..text(emailSelected.getAvatarText()) ..text(emailSelected.getAvatarText())
..size(size ?? 50) ..size(size ?? 50)
..addTextStyle(ThemeUtils.textStyleHeadingH4(color: Colors.white)) ..addTextStyle(textStyle ?? ThemeUtils.textStyleHeadingH4(color: Colors.white))
..backgroundColor(AppColor.colorAvatar) ..backgroundColor(AppColor.colorAvatar)
..addPadding(padding)
..addOnTapActionClick(onTapAvatarActionClick ?? () {}) ..addOnTapActionClick(onTapAvatarActionClick ?? () {})
..avatarColor(emailSelected.avatarColors)) ..avatarColor(emailSelected.avatarColors))
.build(); .build();
@@ -244,9 +244,8 @@ class EmailView extends GetWidget<SingleEmailController> {
presentationEmail: presentationEmail.copyWith( presentationEmail: presentationEmail.copyWith(
subject: threadSubject, subject: threadSubject,
), ),
) isMobileResponsive: isMobileResponsive,
else ),
const SizedBox(height: 16),
Obx(() => InformationSenderAndReceiverBuilder( Obx(() => InformationSenderAndReceiverBuilder(
emailSelected: presentationEmail, emailSelected: presentationEmail,
imagePaths: controller.imagePaths, imagePaths: controller.imagePaths,
@@ -254,6 +253,7 @@ class EmailView extends GetWidget<SingleEmailController> {
sMimeStatus: controller.currentEmailLoaded.value?.sMimeStatus, sMimeStatus: controller.currentEmailLoaded.value?.sMimeStatus,
emailUnsubscribe: controller.emailUnsubscribe.value, emailUnsubscribe: controller.emailUnsubscribe.value,
maxBodyHeight: bodyConstraints.maxHeight, maxBodyHeight: bodyConstraints.maxHeight,
isInsideThreadCollapsed: false,
openEmailAddressDetailAction: (_, emailAddress) => controller.openEmailAddressDialog(emailAddress), openEmailAddressDetailAction: (_, emailAddress) => controller.openEmailAddressDialog(emailAddress),
onEmailActionClick: (presentationEmail, actionType) => controller.handleEmailAction(context, presentationEmail, actionType), onEmailActionClick: (presentationEmail, actionType) => controller.handleEmailAction(context, presentationEmail, actionType),
isInsideThreadDetailView: isInsideThreadDetailView, isInsideThreadDetailView: isInsideThreadDetailView,
@@ -287,8 +287,6 @@ class EmailView extends GetWidget<SingleEmailController> {
controller.mailboxDashBoardController.mapMailboxById, controller.mailboxDashBoardController.mapMailboxById,
), ),
)), )),
if (!isMobileResponsive)
const SizedBox(height: 16),
Obx(() => MailUnsubscribedBanner( Obx(() => MailUnsubscribedBanner(
presentationEmail: controller.currentEmail, presentationEmail: controller.currentEmail,
emailUnsubscribe: controller.emailUnsubscribe.value emailUnsubscribe: controller.emailUnsubscribe.value
@@ -359,6 +357,7 @@ class EmailView extends GetWidget<SingleEmailController> {
useDefaultFontStyle: true, useDefaultFontStyle: true,
scrollController: scrollController, scrollController: scrollController,
enableQuoteToggle: true, enableQuoteToggle: true,
fontSize: isMobileResponsive ? 16 : 14,
), ),
); );
} else if (PlatformInfo.isIOS) { } else if (PlatformInfo.isIOS) {
@@ -370,10 +369,7 @@ class EmailView extends GetWidget<SingleEmailController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( Padding(
padding: const EdgeInsetsDirectional.symmetric( padding: EmailViewStyles.mobileEmailContentPadding,
vertical: EmailViewStyles.mobileContentVerticalMargin,
horizontal: EmailViewStyles.mobileContentHorizontalMargin,
),
child: HtmlContentViewer( child: HtmlContentViewer(
key: PlatformInfo.isIntegrationTesting key: PlatformInfo.isIntegrationTesting
? controller.htmlContentViewKey ? controller.htmlContentViewKey
@@ -389,6 +385,7 @@ class EmailView extends GetWidget<SingleEmailController> {
onScrollHorizontalEnd: controller.onScrollHorizontalEnd, onScrollHorizontalEnd: controller.onScrollHorizontalEnd,
keepAlive: isInsideThreadDetailView, keepAlive: isInsideThreadDetailView,
enableQuoteToggle: true, enableQuoteToggle: true,
fontSize: isMobileResponsive ? 16 : 14,
), ),
), ),
Obx(() { Obx(() {
@@ -411,10 +408,7 @@ class EmailView extends GetWidget<SingleEmailController> {
}); });
} else { } else {
return Padding( return Padding(
padding: const EdgeInsetsDirectional.symmetric( padding: EmailViewStyles.mobileEmailContentPadding,
vertical: EmailViewStyles.mobileContentVerticalMargin,
horizontal: EmailViewStyles.mobileContentHorizontalMargin
),
child: HtmlContentViewer( child: HtmlContentViewer(
key: PlatformInfo.isIntegrationTesting key: PlatformInfo.isIntegrationTesting
? controller.htmlContentViewKey ? controller.htmlContentViewKey
@@ -428,6 +422,7 @@ class EmailView extends GetWidget<SingleEmailController> {
keepAlive: isInsideThreadDetailView, keepAlive: isInsideThreadDetailView,
enableQuoteToggle: true, enableQuoteToggle: true,
onScrollHorizontalEnd: controller.onScrollHorizontalEnd, onScrollHorizontalEnd: controller.onScrollHorizontalEnd,
fontSize: isMobileResponsive ? 16 : 14,
) )
); );
} }
@@ -1,12 +1,18 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class EmailSubjectStyles { class EmailSubjectStyles {
static int? maxLines = PlatformInfo.isWeb ? 2 : null; static int? maxLines = PlatformInfo.isWeb ? 2 : null;
static const Color cursorColor = AppColor.colorTextButton; static const EdgeInsetsGeometry padding = EdgeInsets.all(16);
static const EdgeInsetsGeometry mobilePadding = EdgeInsets.all(12);
static const EdgeInsetsGeometry padding = EdgeInsets.symmetric(vertical: 12, horizontal: 16); static final TextStyle textStyle = ThemeUtils.textStyleInter500().copyWith(
overflow: PlatformInfo.isWeb ? TextOverflow.ellipsis : null,
color: Colors.black,
fontSize: 24,
height: 1,
letterSpacing: -0.24,
);
} }
@@ -25,10 +25,19 @@ class EmailViewAppBarWidgetStyles {
if (responsiveUtils.isLandscapeMobile(context)) { if (responsiveUtils.isLandscapeMobile(context)) {
return EdgeInsets.zero; return EdgeInsets.zero;
} else if (responsiveUtils.isPortraitTablet(context) || responsiveUtils.isLandscapeTablet(context)) { } else if (responsiveUtils.isPortraitTablet(context) || responsiveUtils.isLandscapeTablet(context)) {
return const EdgeInsetsDirectional.only(top: 40, start: 16, end: 16, bottom: 4); return const EdgeInsetsDirectional.only(
top: 40,
start: 12,
end: 12,
bottom: 4,
);
} else { } else {
return const EdgeInsetsDirectional.only(start: 16, end: 16, bottom: 4); return const EdgeInsetsDirectional.only(start: 12, end: 12, bottom: 4);
} }
} }
static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.symmetric(horizontal: 16);
static const EdgeInsetsGeometry mobilePadding =
EdgeInsetsDirectional.only(start: 8, end: 12);
static const EdgeInsetsGeometry padding =
EdgeInsetsDirectional.symmetric(horizontal: 16);
} }
@@ -1,20 +1,8 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/icon_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class EmailViewStyles { class EmailViewStyles {
static const double mobileContentHorizontalMargin = 16;
static const double mobileContentVerticalMargin = 12;
static double pageViewIconSize = IconUtils.defaultIconSize;
static const double initialHtmlViewHeight = 200;
static const Color iconColor = AppColor.steelGrayA540;
static const EdgeInsetsGeometry emailContentPadding = static const EdgeInsetsGeometry emailContentPadding =
EdgeInsetsDirectional.only( EdgeInsetsDirectional.symmetric(horizontal: 16);
start: 16, static const EdgeInsetsGeometry mobileEmailContentPadding =
end: 16, EdgeInsetsDirectional.only(start: 12, end: 12, top: 12);
top: 8,
);
} }
@@ -63,37 +63,11 @@ class _EmailReceiverWidgetState extends State<EmailReceiverWidget> {
primary: false, primary: false,
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
children: [ children: _buildListFullDisplayRecipients(),
if (widget.emailSelected.to.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.to,
listEmailAddress: PrefixEmailAddress.to.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.cc.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.cc,
listEmailAddress: PrefixEmailAddress.cc.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.bcc.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.bcc,
listEmailAddress: PrefixEmailAddress.bcc.listEmailAddress(widget.emailSelected)
),
],
), ),
) )
), ),
TMailButtonWidget.fromText( _buildHideButton(),
text: AppLocalizations.of(context).hide,
textStyle: ThemeUtils.textStyleBodyBody1(
color: AppColor.steelGray400,
),
backgroundColor: Colors.transparent,
onTapActionCallback: () => setState(() => _isDisplayAll = false),
)
] ]
); );
} else { } else {
@@ -103,44 +77,18 @@ class _EmailReceiverWidgetState extends State<EmailReceiverWidget> {
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: _getMaxWidth(context), maxWidth: _getMaxWidth(context),
maxHeight: _responsiveUtils.isMobile(context) ? 22 : 28, maxHeight: 22,
), ),
child: ListView( child: ListView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
shrinkWrap: true, shrinkWrap: true,
children: [ children: _buildListRecipients(),
if (widget.emailSelected.to.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.to,
listEmailAddress: PrefixEmailAddress.to.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.cc.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.cc,
listEmailAddress: PrefixEmailAddress.cc.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.bcc.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.bcc,
listEmailAddress: PrefixEmailAddress.bcc.listEmailAddress(widget.emailSelected)
),
]
), ),
), ),
if (widget.emailSelected.countRecipients > 1) if (widget.emailSelected.countRecipients > 1)
TMailButtonWidget.fromIcon( _buildShowAllButton(),
icon: _imagePaths.icChevronDownOutline,
iconColor: AppColor.steelGray400,
padding: const EdgeInsets.all(3),
iconSize: 20,
backgroundColor: Colors.transparent,
onTapActionCallback: () => setState(() => _isDisplayAll = true),
)
] ]
); );
} }
@@ -153,38 +101,10 @@ class _EmailReceiverWidgetState extends State<EmailReceiverWidget> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: _buildListFullDisplayRecipients(),
if (widget.emailSelected.to.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.to,
listEmailAddress: PrefixEmailAddress.to.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.cc.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.cc,
listEmailAddress: PrefixEmailAddress.cc.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.bcc.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.bcc,
listEmailAddress: PrefixEmailAddress.bcc.listEmailAddress(widget.emailSelected)
),
],
) )
), ),
TMailButtonWidget.fromText( _buildHideButton(),
text: AppLocalizations.of(context).hide,
textStyle: Theme.of(context).textTheme.labelMedium?.copyWith(
color: AppColor.primaryColor,
fontSize: 15
),
backgroundColor: Colors.transparent,
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
onTapActionCallback: () => setState(() => _isDisplayAll = false),
)
] ]
); );
} else { } else {
@@ -193,67 +113,128 @@ class _EmailReceiverWidgetState extends State<EmailReceiverWidget> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
height: 40, height: 22,
constraints: BoxConstraints(maxWidth: _getMaxWidth(context)), constraints: BoxConstraints(maxWidth: _getMaxWidth(context)),
child: ListView( child: ListView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
shrinkWrap: true, shrinkWrap: true,
children: [ children: _buildListRecipients(),
if (widget.emailSelected.to.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.to,
listEmailAddress: PrefixEmailAddress.to.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.cc.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.cc,
listEmailAddress: PrefixEmailAddress.cc.listEmailAddress(widget.emailSelected)
),
if (widget.emailSelected.bcc.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.bcc,
listEmailAddress: PrefixEmailAddress.bcc.listEmailAddress(widget.emailSelected)
),
]
), ),
), ),
if (widget.emailSelected.countRecipients > 1) if (widget.emailSelected.countRecipients > 1)
TMailButtonWidget.fromIcon( _buildShowAllButton(),
icon: _imagePaths.icChevronDown,
backgroundColor: Colors.transparent,
padding: const EdgeInsets.all(3),
onTapActionCallback: () => setState(() => _isDisplayAll = true),
)
] ]
); );
} }
} }
} }
Widget _buildHideButton() {
return TMailButtonWidget.fromText(
text: AppLocalizations.of(context).hide,
textStyle: ThemeUtils.textStyleInter500().copyWith(
color: AppColor.gray6D7885,
fontSize: 14,
height: 1,
letterSpacing: -0.14,
),
padding: const EdgeInsetsDirectional.symmetric(
horizontal: 8,
vertical: 3,
),
backgroundColor: Colors.transparent,
onTapActionCallback: () => setState(() => _isDisplayAll = false),
);
}
Widget _buildShowAllButton() {
return TMailButtonWidget.fromIcon(
icon: _imagePaths.icChevronDownOutline,
iconColor: AppColor.steelGray400,
padding: const EdgeInsets.all(2),
iconSize: 20,
backgroundColor: Colors.transparent,
onTapActionCallback: () => setState(() => _isDisplayAll = true),
);
}
List<Widget> _buildListFullDisplayRecipients() {
return [
if (widget.emailSelected.to.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.to,
listEmailAddress: PrefixEmailAddress.to.listEmailAddress(
widget.emailSelected,
),
padding: const EdgeInsets.only(bottom: 4),
),
if (widget.emailSelected.cc.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.cc,
listEmailAddress: PrefixEmailAddress.cc.listEmailAddress(
widget.emailSelected,
),
padding: const EdgeInsets.only(bottom: 4),
),
if (widget.emailSelected.bcc.numberEmailAddress() > 0)
_buildRecipientsWidgetToDisplayFull(
context: context,
prefixEmailAddress: PrefixEmailAddress.bcc,
listEmailAddress: PrefixEmailAddress.bcc.listEmailAddress(
widget.emailSelected,
),
),
];
}
List<Widget> _buildListRecipients() {
return [
if (widget.emailSelected.to.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.to,
listEmailAddress: PrefixEmailAddress.to.listEmailAddress(
widget.emailSelected,
),
),
if (widget.emailSelected.cc.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.cc,
listEmailAddress: PrefixEmailAddress.cc.listEmailAddress(
widget.emailSelected,
),
),
if (widget.emailSelected.bcc.numberEmailAddress() > 0)
..._buildRecipientsWidget(
context: context,
prefixEmailAddress: PrefixEmailAddress.bcc,
listEmailAddress: PrefixEmailAddress.bcc.listEmailAddress(
widget.emailSelected,
),
),
];
}
List<Widget> _buildRecipientsTag({required List<EmailAddress> listEmailAddress}) { List<Widget> _buildRecipientsTag({required List<EmailAddress> listEmailAddress}) {
final isMobileResponsive = _responsiveUtils.isMobile(context);
return listEmailAddress return listEmailAddress
.mapIndexed((index, emailAddress) => TMailButtonWidget.fromText( .mapIndexed((index, emailAddress) => TMailButtonWidget.fromText(
text: index == listEmailAddress.length - 1 text: index == listEmailAddress.length - 1
? emailAddress.asString() ? emailAddress.asString()
: '${emailAddress.asString()},', : '${emailAddress.asString()},',
textStyle: ThemeUtils.textStyleHeadingHeadingSmall( textStyle: ThemeUtils.textStyleInter400.copyWith(
color: _responsiveUtils.isMobile(context) ? AppColor.gray6D7885 : Colors.black, fontSize: 14,
fontWeight: FontWeight.w400,
).copyWith(
fontSize: _responsiveUtils.isMobile(context) ? 14 : 17,
height: 1, height: 1,
letterSpacing: _responsiveUtils.isMobile(context) ? -0.14 : -0.17, letterSpacing: -0.14,
color: isMobileResponsive ? AppColor.gray6D7885 : Colors.black,
), ),
padding: EdgeInsetsDirectional.symmetric( padding: const EdgeInsetsDirectional.all(4),
vertical: _responsiveUtils.isMobile(context) ? 2.5 : 3.5,
horizontal: 8,
),
margin: EdgeInsetsDirectional.only(top: _responsiveUtils.isMobile(context) ? 1.5 : 2),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
onTapActionCallback: () => widget.openEmailAddressDetailAction?.call(context, emailAddress), onTapActionCallback: () => widget.openEmailAddressDetailAction?.call(context, emailAddress),
onLongPressActionCallback: () => AppUtils.copyEmailAddressToClipboard(context, emailAddress.emailAddress), onLongPressActionCallback: () => AppUtils.copyEmailAddressToClipboard(context, emailAddress.emailAddress),
@@ -261,26 +242,34 @@ class _EmailReceiverWidgetState extends State<EmailReceiverWidget> {
.toList(); .toList();
} }
Widget _buildRecipientsWidgetToDisplayFull({ Widget _buildRecipientsWidgetToDisplayFull({
required BuildContext context, required BuildContext context,
required PrefixEmailAddress prefixEmailAddress, required PrefixEmailAddress prefixEmailAddress,
required List<EmailAddress> listEmailAddress, required List<EmailAddress> listEmailAddress,
EdgeInsetsGeometry? padding,
}) { }) {
return Row( final bodyWidget = Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
PrefixRecipientWidget( PrefixRecipientWidget(
prefixEmailAddress: prefixEmailAddress, prefixEmailAddress: prefixEmailAddress,
responsiveUtils: _responsiveUtils, isMobileResponsive: _responsiveUtils.isMobile(context),
padding: const EdgeInsetsDirectional.symmetric(vertical: 4),
), ),
Expanded( Expanded(
child: Wrap( child: Wrap(
runSpacing: 4,
children: _buildRecipientsTag(listEmailAddress: listEmailAddress) children: _buildRecipientsTag(listEmailAddress: listEmailAddress)
) )
) )
], ],
); );
if (padding != null) {
return Padding(padding: padding, child: bodyWidget);
} else {
return bodyWidget;
}
} }
List<Widget> _buildRecipientsWidget({ List<Widget> _buildRecipientsWidget({
@@ -291,7 +280,8 @@ class _EmailReceiverWidgetState extends State<EmailReceiverWidget> {
return [ return [
PrefixRecipientWidget( PrefixRecipientWidget(
prefixEmailAddress: prefixEmailAddress, prefixEmailAddress: prefixEmailAddress,
responsiveUtils: _responsiveUtils, isMobileResponsive: _responsiveUtils.isMobile(context),
padding: const EdgeInsetsDirectional.only(top: 4),
), ),
..._buildRecipientsTag(listEmailAddress: listEmailAddress).map( ..._buildRecipientsTag(listEmailAddress: listEmailAddress).map(
(child) => Align(alignment: AlignmentDirectional.topStart, child: child), (child) => Align(alignment: AlignmentDirectional.topStart, child: child),
@@ -15,12 +15,14 @@ class EmailSenderBuilder extends StatelessWidget {
final EmailAddress emailAddress; final EmailAddress emailAddress;
final OnOpenEmailAddressDetailAction? openEmailAddressDetailAction; final OnOpenEmailAddressDetailAction? openEmailAddressDetailAction;
final bool showSenderEmail; final bool showSenderEmail;
final bool isMobileResponsive;
const EmailSenderBuilder({ const EmailSenderBuilder({
super.key, super.key,
required this.emailAddress, required this.emailAddress,
this.openEmailAddressDetailAction, this.openEmailAddressDetailAction,
this.showSenderEmail = true, this.showSenderEmail = true,
this.isMobileResponsive = false,
}); });
@override @override
@@ -43,10 +45,12 @@ class EmailSenderBuilder extends StatelessWidget {
}, },
borderRadius: 8, borderRadius: 8,
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4),
customStyle: ThemeUtils.textStyleHeadingH6( customStyle: ThemeUtils.textStyleInter500().copyWith(
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.w500, fontSize: isMobileResponsive ? 20 : 15,
).copyWith(height: 1, letterSpacing: -0.2), height: isMobileResponsive ? 1 : 20 / 15,
letterSpacing: isMobileResponsive ? -0.2 : 0.0,
),
overflow: CommonTextStyle.defaultTextOverFlow, overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap softWrap: CommonTextStyle.defaultSoftWrap
), ),
@@ -59,12 +63,11 @@ class EmailSenderBuilder extends StatelessWidget {
}, },
borderRadius: 8, borderRadius: 8,
padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4),
customStyle: ThemeUtils.textStyleBodyBody1( customStyle: ThemeUtils.textStyleInter500().copyWith(
color: AppColor.gray6D7885, color: AppColor.gray6D7885,
).copyWith( fontSize: 14,
fontSize: 17,
height: 1, height: 1,
letterSpacing: -0.17, letterSpacing: -0.14,
), ),
overflow: CommonTextStyle.defaultTextOverFlow, overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap softWrap: CommonTextStyle.defaultSoftWrap
@@ -1,5 +1,3 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:model/email/presentation_email.dart'; import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/email/presentation/styles/email_subject_styles.dart'; import 'package:tmail_ui_user/features/email/presentation/styles/email_subject_styles.dart';
@@ -7,8 +5,13 @@ import 'package:tmail_ui_user/features/email/presentation/styles/email_subject_s
class EmailSubjectWidget extends StatelessWidget { class EmailSubjectWidget extends StatelessWidget {
final PresentationEmail presentationEmail; final PresentationEmail presentationEmail;
final bool isMobileResponsive;
const EmailSubjectWidget({super.key, required this.presentationEmail}); const EmailSubjectWidget({
super.key,
required this.presentationEmail,
this.isMobileResponsive = false,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -17,12 +20,12 @@ class EmailSubjectWidget extends StatelessWidget {
} }
return Padding( return Padding(
padding: EmailSubjectStyles.padding, padding: isMobileResponsive
? EmailSubjectStyles.mobilePadding
: EmailSubjectStyles.padding,
child: Text( child: Text(
presentationEmail.getEmailTitle(), presentationEmail.getEmailTitle(),
style: ThemeUtils.textStyleHeadingH5(color: Colors.black).copyWith( style: EmailSubjectStyles.textStyle,
overflow: PlatformInfo.isWeb ? TextOverflow.ellipsis : null,
),
maxLines: EmailSubjectStyles.maxLines, maxLines: EmailSubjectStyles.maxLines,
) )
); );
@@ -42,6 +42,7 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
final VoidCallback? onToggleThreadDetailCollapseExpand; final VoidCallback? onToggleThreadDetailCollapseExpand;
final PresentationMailbox? mailboxContain; final PresentationMailbox? mailboxContain;
final bool showUnreadVisualization; final bool showUnreadVisualization;
final bool isInsideThreadCollapsed;
const InformationSenderAndReceiverBuilder({ const InformationSenderAndReceiverBuilder({
Key? key, Key? key,
@@ -60,16 +61,18 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
this.onToggleThreadDetailCollapseExpand, this.onToggleThreadDetailCollapseExpand,
this.mailboxContain, this.mailboxContain,
this.showUnreadVisualization = false, this.showUnreadVisualization = false,
this.isInsideThreadCollapsed = false,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isMobileResponsive = responsiveUtils.isMobile(context);
final crossAxisAlignment = _getAxisAlignment(isMobileResponsive);
return Padding( return Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 16, vertical: 4), padding: _getPadding(isMobileResponsive),
child: Row( child: Row(
crossAxisAlignment: emailSelected.countRecipients > 0 && (showRecipients || responsiveUtils.isMobile(context)) crossAxisAlignment: crossAxisAlignment,
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: [ children: [
EmailAvatarBuilder( EmailAvatarBuilder(
emailSelected: emailSelected, emailSelected: emailSelected,
@@ -81,7 +84,18 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
); );
} }
}, },
size: 56, size: isMobileResponsive ? 44 : 32,
textStyle: !isMobileResponsive
? ThemeUtils.textStyleHeadingHeadingSmall(color: Colors.white)
: ThemeUtils.textStyleInter600().copyWith(
fontSize: 18.86,
height: 17.29 / 18.86,
letterSpacing: -0.32,
color: Colors.white,
),
padding: crossAxisAlignment == CrossAxisAlignment.start
? const EdgeInsetsDirectional.only(top: 4)
: null,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Expanded(child: LayoutBuilder(builder: (context, constraints) { Expanded(child: LayoutBuilder(builder: (context, constraints) {
@@ -98,7 +112,7 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
children: [ children: [
if (showUnreadVisualization && if (showUnreadVisualization &&
!emailSelected.hasRead && !emailSelected.hasRead &&
responsiveUtils.isMobile(context)) isMobileResponsive)
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: imagePaths.icUnreadStatus, icon: imagePaths.icUnreadStatus,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -114,9 +128,10 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
emailAddress: emailSelected.from!.first, emailAddress: emailSelected.from!.first,
openEmailAddressDetailAction: openEmailAddressDetailAction, openEmailAddressDetailAction: openEmailAddressDetailAction,
showSenderEmail: _showSenderEmail( showSenderEmail: _showSenderEmail(
responsiveUtils.isMobile(context), isMobileResponsive,
senderEmail: emailSelected.from!.first, senderEmail: emailSelected.from!.first,
), ),
isMobileResponsive: isMobileResponsive,
), ),
), ),
if (sMimeStatus != null && sMimeStatus != SMimeSignatureStatus.notSigned) if (sMimeStatus != null && sMimeStatus != SMimeSignatureStatus.notSigned)
@@ -134,19 +149,20 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
if (!emailSelected.isSubscribed && emailUnsubscribe != null && !responsiveUtils.isPortraitMobile(context)) if (!emailSelected.isSubscribed && emailUnsubscribe != null && !responsiveUtils.isPortraitMobile(context))
TMailButtonWidget.fromText( TMailButtonWidget.fromText(
text: AppLocalizations.of(context).unsubscribe, text: AppLocalizations.of(context).unsubscribe,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( textStyle: ThemeUtils.textStyleInter400.copyWith(
fontWeight: FontWeight.normal,
fontSize: 14, fontSize: 14,
color: AppColor.colorTextBody, height: 1,
letterSpacing: -0.14,
color: AppColor.gray6D7885,
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
), ),
padding: const EdgeInsetsDirectional.symmetric(vertical: 4, horizontal: 8), padding: const EdgeInsetsDirectional.symmetric(vertical: 4, horizontal: 8),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
onTapActionCallback: () => onEmailActionClick?.call(emailSelected, EmailActionType.unsubscribe), onTapActionCallback: () => onEmailActionClick?.call(emailSelected, EmailActionType.unsubscribe),
), ),
if (_showAttachmentIcon() && !responsiveUtils.isMobile(context)) if (_showAttachmentIcon() && !isMobileResponsive)
Padding( Padding(
padding: const EdgeInsetsDirectional.only(start: 16), padding: const EdgeInsetsDirectional.only(start: 8),
child: SvgPicture.asset( child: SvgPicture.asset(
imagePaths.icAttachment, imagePaths.icAttachment,
colorFilter: AppColor.steelGray200.asFilter(), colorFilter: AppColor.steelGray200.asFilter(),
@@ -154,7 +170,7 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
height: 20, height: 20,
), ),
), ),
if (isInsideThreadDetailView && !responsiveUtils.isMobile(context)) if (isInsideThreadDetailView && !isMobileResponsive)
ReceivedTimeBuilder( ReceivedTimeBuilder(
emailSelected: emailSelected, emailSelected: emailSelected,
padding: const EdgeInsetsDirectional.only(start: 8, top: 2), padding: const EdgeInsetsDirectional.only(start: 8, top: 2),
@@ -162,7 +178,7 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
), ),
if (showUnreadVisualization && if (showUnreadVisualization &&
!emailSelected.hasRead && !emailSelected.hasRead &&
!responsiveUtils.isMobile(context)) !isMobileResponsive)
TMailButtonWidget.fromIcon( TMailButtonWidget.fromIcon(
icon: imagePaths.icUnreadStatus, icon: imagePaths.icUnreadStatus,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -208,7 +224,7 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
), ),
], ],
), ),
if (responsiveUtils.isMobile(context) && isInsideThreadDetailView) if (isMobileResponsive && isInsideThreadDetailView)
Row( Row(
children: [ children: [
if (_showAttachmentIcon()) if (_showAttachmentIcon())
@@ -244,6 +260,24 @@ class InformationSenderAndReceiverBuilder extends StatelessWidget {
); );
} }
EdgeInsetsGeometry _getPadding(bool isMobileResponsive) {
if (isInsideThreadCollapsed) {
return EdgeInsetsDirectional.symmetric(
horizontal: isMobileResponsive ? 12 : 16,
vertical: isMobileResponsive ? 12 : 8,
);
} else {
return EdgeInsetsDirectional.all(isMobileResponsive ? 12 : 16);
}
}
CrossAxisAlignment _getAxisAlignment(bool isMobileResponsive) {
return emailSelected.countRecipients > 0 &&
(showRecipients || isMobileResponsive)
? CrossAxisAlignment.start
: CrossAxisAlignment.center;
}
bool _showSenderEmail( bool _showSenderEmail(
bool isResponsiveMobile, { bool isResponsiveMobile, {
required EmailAddress senderEmail, required EmailAddress senderEmail,
@@ -1,6 +1,4 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:model/email/prefix_email_address.dart'; import 'package:model/email/prefix_email_address.dart';
@@ -8,29 +6,32 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_e
class PrefixRecipientWidget extends StatelessWidget { class PrefixRecipientWidget extends StatelessWidget {
final PrefixEmailAddress prefixEmailAddress; final PrefixEmailAddress prefixEmailAddress;
final ResponsiveUtils responsiveUtils; final EdgeInsetsGeometry? padding;
final bool isMobileResponsive;
const PrefixRecipientWidget({ const PrefixRecipientWidget({
super.key, super.key,
required this.prefixEmailAddress, required this.prefixEmailAddress,
required this.responsiveUtils, this.isMobileResponsive = false,
this.padding,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( final bodyWidget = Text(
padding: EdgeInsets.symmetric(vertical: responsiveUtils.isMobile(context) ? 4 : 5.5), '${prefixEmailAddress.asName(context)}:',
child: Text( style: ThemeUtils.textStyleInter400.copyWith(
'${prefixEmailAddress.asName(context)}:', fontSize: 14,
style: ThemeUtils.textStyleBodyBody1( height: 1,
color: AppColor.steelGray400, letterSpacing: -0.14,
fontWeight: FontWeight.w400, color: isMobileResponsive ? AppColor.gray6D7885 : AppColor.gray9AA7B6,
).copyWith(
fontSize: responsiveUtils.isMobile(context) ? 14 : 17,
height: 1,
letterSpacing: responsiveUtils.isMobile(context) ? -0.14 : -0.17
),
), ),
); );
if (padding != null) {
return Padding(padding: padding!, child: bodyWidget);
} else {
return bodyWidget;
}
} }
} }
@@ -37,9 +37,11 @@ class ReceivedTimeBuilder extends StatelessWidget {
maxLines: 1, maxLines: 1,
overflow: CommonTextStyle.defaultTextOverFlow, overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap, softWrap: CommonTextStyle.defaultSoftWrap,
style: ThemeUtils.textStyleBodyBody3(color: AppColor.steelGray400).copyWith( style: ThemeUtils.textStyleInter400.copyWith(
fontSize: 14,
height: 1, height: 1,
letterSpacing: -0.14 letterSpacing: -0.14,
color: AppColor.gray6D7885,
), ),
), ),
); );
@@ -67,37 +67,31 @@ extension GetThreadDetailEmailViews on ThreadDetailController {
} }
if (isFirstEmailInThreadDetail) { if (isFirstEmailInThreadDetail) {
return Padding( return EmailView(
padding: const EdgeInsetsDirectional.only(bottom: 16),
child: EmailView(
key: PlatformInfo.isWeb
? GlobalObjectKey(presentationEmail.id?.id.value ?? '')
: null,
isInsideThreadDetailView: true,
emailId: presentationEmail.id,
isFirstEmailInThreadDetail: true,
threadSubject: emailIdsPresentation.values.last?.subject,
onToggleThreadDetailCollapseExpand: () {
toggleThreadDetailCollapseExpand(presentationEmail);
},
scrollController: scrollController,
),
);
}
return Padding(
padding: const EdgeInsetsDirectional.only(bottom: 16),
child: EmailView(
key: PlatformInfo.isWeb key: PlatformInfo.isWeb
? GlobalObjectKey(presentationEmail.id?.id.value ?? '') ? GlobalObjectKey(presentationEmail.id?.id.value ?? '')
: null, : null,
isInsideThreadDetailView: true, isInsideThreadDetailView: true,
emailId: presentationEmail.id, emailId: presentationEmail.id,
isFirstEmailInThreadDetail: true,
threadSubject: emailIdsPresentation.values.last?.subject,
onToggleThreadDetailCollapseExpand: () { onToggleThreadDetailCollapseExpand: () {
toggleThreadDetailCollapseExpand(presentationEmail); toggleThreadDetailCollapseExpand(presentationEmail);
}, },
scrollController: scrollController, scrollController: scrollController,
), );
}
return EmailView(
key: PlatformInfo.isWeb
? GlobalObjectKey(presentationEmail.id?.id.value ?? '')
: null,
isInsideThreadDetailView: true,
emailId: presentationEmail.id,
onToggleThreadDetailCollapseExpand: () {
toggleThreadDetailCollapseExpand(presentationEmail);
},
scrollController: scrollController,
); );
}).toList(); }).toList();
} }
@@ -108,7 +108,7 @@ class ThreadDetailAppBar extends StatelessWidget {
if (isMobile) { if (isMobile) {
backButton = Expanded( backButton = Expanded(
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: AlignmentDirectional.centerStart,
child: backButton, child: backButton,
), ),
); );
@@ -141,7 +141,9 @@ class ThreadDetailAppBar extends StatelessWidget {
: EmailViewAppBarWidgetStyles.height, : EmailViewAppBarWidgetStyles.height,
padding: PlatformInfo.isIOS padding: PlatformInfo.isIOS
? EmailViewAppBarWidgetStyles.paddingIOS(context, responsiveUtils) ? EmailViewAppBarWidgetStyles.paddingIOS(context, responsiveUtils)
: EmailViewAppBarWidgetStyles.padding, : isMobile
? EmailViewAppBarWidgetStyles.mobilePadding
: EmailViewAppBarWidgetStyles.padding,
decoration: const BoxDecoration( decoration: const BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
@@ -2,7 +2,6 @@ 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/responsive_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/text/text_overflow_builder.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:model/email/presentation_email.dart'; import 'package:model/email/presentation_email.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
@@ -40,6 +39,8 @@ class ThreadDetailCollapsedEmail extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isMobileResponsive = responsiveUtils.isMobile(context);
return DecoratedBox( return DecoratedBox(
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white,
@@ -57,9 +58,10 @@ class ThreadDetailCollapsedEmail extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (showSubject) if (showSubject)
EmailSubjectWidget(presentationEmail: presentationEmail) EmailSubjectWidget(
else presentationEmail: presentationEmail,
const SizedBox(height: 16), isMobileResponsive: isMobileResponsive,
),
InformationSenderAndReceiverBuilder( InformationSenderAndReceiverBuilder(
emailSelected: presentationEmail, emailSelected: presentationEmail,
responsiveUtils: responsiveUtils, responsiveUtils: responsiveUtils,
@@ -71,22 +73,27 @@ class ThreadDetailCollapsedEmail extends StatelessWidget {
showRecipients: false, showRecipients: false,
mailboxContain: mailboxContain, mailboxContain: mailboxContain,
showUnreadVisualization: true, showUnreadVisualization: true,
isInsideThreadCollapsed: true,
), ),
Padding( Padding(
padding: const EdgeInsets.all(16), padding: isMobileResponsive
child: TextOverflowBuilder( ? const EdgeInsetsDirectional.only(
top: 12,
bottom: 21,
start: 12,
end: 12,
)
: const EdgeInsetsDirectional.only(
top: 8,
bottom: 24,
start: 16,
end: 16,
),
child: Text(
preview, preview,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: ThemeUtils.textStyleBodyBody1( style: _getPreviewTextStyle(isMobileResponsive),
fontWeight: presentationEmail.hasRead
? FontWeight.normal
: FontWeight.w600,
fontSize: presentationEmail.hasRead ? null : 15,
).copyWith(
height: presentationEmail.hasRead ? 24 / 16 : 20 / 15,
letterSpacing: presentationEmail.hasRead ? -0.16 : 0,
),
), ),
), ),
], ],
@@ -94,4 +101,29 @@ class ThreadDetailCollapsedEmail extends StatelessWidget {
), ),
); );
} }
TextStyle _getPreviewTextStyle(bool isMobileResponsive) {
if (isMobileResponsive) {
return presentationEmail.hasRead
? ThemeUtils.textStyleInter400.copyWith(
color: Colors.black,
fontSize: 16,
height: 24 / 16,
letterSpacing: -0.16,
)
: ThemeUtils.textStyleInter600().copyWith(
color: Colors.black,
fontSize: 16,
height: 24 / 16,
letterSpacing: -0.16,
);
} else {
return presentationEmail.hasRead
? ThemeUtils.textStyleBodyBody3().copyWith(color: Colors.black)
: ThemeUtils.textStyleBodyBody3().copyWith(
color: Colors.black,
fontWeight: FontWeight.w600,
);
}
}
} }
@@ -1,5 +1,7 @@
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/responsive_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/loading/cupertino_loading_widget.dart'; import 'package:core/presentation/views/loading/cupertino_loading_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';
@@ -29,6 +31,7 @@ class ThreadDetailLoadMoreCircle extends StatefulWidget {
class _ThreadDetailLoadMoreCircleState extends State<ThreadDetailLoadMoreCircle> { class _ThreadDetailLoadMoreCircleState extends State<ThreadDetailLoadMoreCircle> {
final _isHover = ValueNotifier(false); final _isHover = ValueNotifier(false);
final threadDetailController = Get.find<ThreadDetailController>(); final threadDetailController = Get.find<ThreadDetailController>();
final _responsiveUtils = Get.find<ResponsiveUtils>();
@override @override
void dispose() { void dispose() {
@@ -38,8 +41,10 @@ class _ThreadDetailLoadMoreCircleState extends State<ThreadDetailLoadMoreCircle>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isMobileResponsive = _responsiveUtils.isMobile(context);
return Container( return Container(
padding: const EdgeInsets.symmetric(vertical: 16), padding: EdgeInsets.symmetric(vertical: isMobileResponsive ? 12 : 16),
color: Colors.white, color: Colors.white,
child: Stack( child: Stack(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
@@ -57,9 +62,11 @@ class _ThreadDetailLoadMoreCircleState extends State<ThreadDetailLoadMoreCircle>
onTap: widget.onTap, onTap: widget.onTap,
onHover: (value) => _isHover.value = value, onHover: (value) => _isHover.value = value,
child: Container( child: Container(
margin: const EdgeInsetsDirectional.only(start: 16), margin: EdgeInsetsDirectional.only(
width: 56, start: isMobileResponsive ? 12 : 16,
height: 56, ),
width: isMobileResponsive ? 44 : 32,
height: isMobileResponsive ? 44 : 32,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: AppColor.colorDividerEmailView, color: AppColor.colorDividerEmailView,
@@ -80,10 +87,10 @@ class _ThreadDetailLoadMoreCircleState extends State<ThreadDetailLoadMoreCircle>
); );
if (isLoading) { if (isLoading) {
return const SizedBox( return SizedBox(
width: 24, width: isMobileResponsive ? 24 : 16,
height: 24, height: isMobileResponsive ? 24 : 16,
child: CupertinoLoadingWidget(), child: const CupertinoLoadingWidget(),
); );
} }
@@ -93,15 +100,19 @@ class _ThreadDetailLoadMoreCircleState extends State<ThreadDetailLoadMoreCircle>
return Text( return Text(
'${widget.count}', '${widget.count}',
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: ThemeUtils.textStyleBodyBody3().copyWith(
fontWeight: FontWeight.normal, color: Colors.black,
height: 24 / 16, fontSize: isMobileResponsive ? 14 : 13,
letterSpacing: -0.1, height: isMobileResponsive ? 18 / 14 : 16 / 13,
), ),
); );
}); });
}, },
child: SvgPicture.asset(widget.imagePaths.icExpandArrows), child: SvgPicture.asset(
widget.imagePaths.icExpandArrows,
width: isMobileResponsive ? 20 : 16,
height: isMobileResponsive ? 20 : 16,
),
), ),
), ),
), ),