TF-2893 Apply the standard app grid icon

This commit is contained in:
dab246
2024-07-23 13:35:02 +07:00
committed by Dat H. Pham
parent 47b006f685
commit 8b7ac1f039
27 changed files with 173 additions and 77 deletions
+1 -1
View File
@@ -2,5 +2,5 @@
import 'package:core/utils/platform_info.dart';
class IconUtils {
static const double defaultIconSize = PlatformInfo.isWeb ? 20.0 : 24.0;
static double defaultIconSize = PlatformInfo.isWeb ? 20.0 : 24.0;
}
@@ -2,7 +2,7 @@ import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
typedef OnTapAvatarActionClick = void Function();
typedef OnTapAvatarActionWithPositionClick = void Function(RelativeRect? position);
typedef OnTapAvatarActionWithPositionClick = void Function(RelativeRect position);
class AvatarBuilder {
@@ -58,7 +58,7 @@ class AvatarBuilder {
_onTapAvatarActionClick = onTapAvatarActionClick;
}
void addOnTapAvatarActionWithPositionClick(OnTapAvatarActionWithPositionClick onTapAvatarActionWithPositionClick) {
void addOnTapAvatarActionWithPositionClick(OnTapAvatarActionWithPositionClick? onTapAvatarActionWithPositionClick) {
_onTapAvatarActionWithPositionClick = onTapAvatarActionWithPositionClick;
}
@@ -1,3 +1,4 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -14,8 +15,8 @@ class SloganBuilder extends StatelessWidget {
final String? text;
final TextStyle? textStyle;
final TextAlign? textAlign;
final String? logoSVG;
final String? logo;
final Uri? publicLogoUri;
final double? sizeLogo;
final OnTapCallback? onTapCallback;
final EdgeInsetsGeometry? paddingText;
@@ -31,8 +32,8 @@ class SloganBuilder extends StatelessWidget {
this.text,
this.textStyle,
this.textAlign,
this.logoSVG,
this.logo,
this.publicLogoUri,
this.sizeLogo,
this.onTapCallback,
this.padding,
@@ -94,9 +95,9 @@ class SloganBuilder extends StatelessWidget {
}
Widget _logoApp() {
if (logoSVG != null) {
if (logo != null && logo!.endsWith('svg')) {
return SvgPicture.asset(
logoSVG!,
logo!,
width: sizeLogo ?? 150,
height: sizeLogo ?? 150);
} else if (logo != null) {
@@ -104,9 +105,22 @@ class SloganBuilder extends StatelessWidget {
image: AssetImage(logo!),
fit: BoxFit.fill,
width: sizeLogo ?? 150,
height: sizeLogo ?? 150);
} else if (publicLogoUri != null) {
return Image.network(
publicLogoUri.toString(),
fit: BoxFit.fill,
width: sizeLogo ?? 150,
height: sizeLogo ?? 150,
alignment: Alignment.center);
errorBuilder: (_, error, stackTrace) {
return Container(
width: sizeLogo ?? 150,
height: sizeLogo ?? 150,
color: AppColor.textFieldHintColor,
);
});
} else {
return const SizedBox.shrink();
}
return const SizedBox.shrink();
}
}
+4 -1
View File
@@ -3,7 +3,10 @@ import 'package:core/utils/web_renderer/canvas_kit.dart';
import 'package:flutter/foundation.dart';
abstract class PlatformInfo {
static const bool isWeb = kIsWeb;
@visibleForTesting
static bool isTestingForWeb = false;
static bool get isWeb => kIsWeb || isTestingForWeb;
static bool get isLinux => !kIsWeb && defaultTargetPlatform == TargetPlatform.linux;
static bool get isWindows => !kIsWeb && defaultTargetPlatform == TargetPlatform.windows;
static bool get isMacOS => !kIsWeb && defaultTargetPlatform == TargetPlatform.macOS;
@@ -1,17 +1,16 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/utils/application_manager.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ApplicationVersionWidget extends StatefulWidget {
final ApplicationManager applicationManager;
final EdgeInsetsGeometry? padding;
final String? title;
final TextStyle? textStyle;
const ApplicationVersionWidget({
super.key,
required this.applicationManager,
this.title,
this.textStyle,
this.padding,
@@ -23,12 +22,13 @@ class ApplicationVersionWidget extends StatefulWidget {
class _ApplicationVersionWidgetState extends State<ApplicationVersionWidget> {
final ApplicationManager _applicationManager = Get.find<ApplicationManager>();
Future<String>? _versionStream;
@override
void initState() {
super.initState();
_versionStream = widget.applicationManager.getVersion();
_versionStream = _applicationManager.getVersion();
}
@override
@@ -58,7 +58,7 @@ class TextInputFieldBuilder extends StatelessWidget {
minLines: minLines,
maxLines: maxLines,
decoration: (TextInputDecorationBuilder()
..setContentPadding(const EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 16 : 12, horizontal: 12))
..setContentPadding(EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 16 : 12, horizontal: 12))
..setHintText(hint)
..setFillColor(backgroundColor)
..setErrorText(error))
@@ -42,7 +42,7 @@ class ContactInputDecorationBuilder extends InputDecorationBuilder {
color: AppColor.colorEmailAddressFull,
fontWeight: FontWeight.w500,
fontSize: 16),
contentPadding: const EdgeInsets.symmetric(
contentPadding: EdgeInsets.symmetric(
vertical: PlatformInfo.isWeb ? 16 : 12,
horizontal: 12),
errorText: errorText,
@@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
class EmailSubjectStyles {
static const double textSize = 20;
static const int? maxLines = PlatformInfo.isWeb ? 2 : null;
static int? maxLines = PlatformInfo.isWeb ? 2 : null;
static const Color textColor = AppColor.colorNameEmail;
static const Color cursorColor = AppColor.colorTextButton;
@@ -8,7 +8,7 @@ class EmailViewAppBarWidgetStyles {
static const double bottomBorderWidth = 0.5;
static const double height = 52;
static const double radius = 20;
static const double buttonIconSize = IconUtils.defaultIconSize;
static double buttonIconSize = IconUtils.defaultIconSize;
static const double deleteButtonIconSize = 20;
static const double space = 5;
static double? heightIOS(BuildContext context, ResponsiveUtils responsiveUtils) {
@@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
class EmailViewStyles {
static const double mobileContentHorizontalMargin = 16;
static const double mobileContentVerticalMargin = 12;
static const double pageViewIconSize = IconUtils.defaultIconSize;
static double pageViewIconSize = IconUtils.defaultIconSize;
static const EdgeInsetsGeometry pageViewButtonPadding = EdgeInsets.all(5);
}
@@ -14,7 +14,7 @@ class EmailRecoveryViewStyles {
color: Colors.white,
);
static const EdgeInsetsGeometry backgroundMarginMobile = EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0);
static EdgeInsetsGeometry backgroundMarginMobile = EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0);
static const BorderRadiusGeometry clipRRectBorderRadiusMobile = BorderRadius.only(
topLeft: Radius.circular(16),
@@ -53,7 +53,7 @@ class IdentityInputFieldBuilder extends StatelessWidget {
textStyle: const TextStyle(color: Colors.black, fontSize: 16),
keyboardType: inputType ?? TextInputType.text,
decoration: (IdentityInputDecorationBuilder()
..setContentPadding(const EdgeInsets.symmetric(
..setContentPadding(EdgeInsets.symmetric(
vertical: PlatformInfo.isWeb ? 16 : 12,
horizontal: 12))
..setErrorText(_error))
@@ -46,7 +46,7 @@ class IdentityInputWithDropListFieldBuilder extends StatelessWidget {
controller: editingController,
textInputAction: TextInputAction.done,
decoration: (IdentityInputDecorationBuilder()
..setContentPadding(const EdgeInsets.symmetric(
..setContentPadding(EdgeInsets.symmetric(
vertical: PlatformInfo.isWeb ? 16 : 12,
horizontal: 12))
..setErrorText(_error))
@@ -104,7 +104,7 @@ class LoginView extends BaseLoginView {
padding: const EdgeInsets.only(top: 24),
child: SloganBuilder(
arrangedByHorizontal: true,
logoSVG: controller.imagePaths.icJMAPStandard,
logo: controller.imagePaths.icJMAPStandard,
sizeLogo: 48.0,
paddingText: const EdgeInsets.only(left: 12),
text: AppLocalizations.of(context).jmapStandard,
@@ -115,7 +115,7 @@ class LoginView extends BaseLoginView {
padding: const EdgeInsets.only(top: 16),
child: SloganBuilder(
arrangedByHorizontal: true,
logoSVG: controller.imagePaths.icEncrypted,
logo: controller.imagePaths.icEncrypted,
sizeLogo: 48.0,
paddingText: const EdgeInsets.only(left: 12),
text: AppLocalizations.of(context).encryptedMailbox,
@@ -126,7 +126,7 @@ class LoginView extends BaseLoginView {
padding: const EdgeInsets.only(top: 16),
child: SloganBuilder(
arrangedByHorizontal: true,
logoSVG: controller.imagePaths.icTeam,
logo: controller.imagePaths.icTeam,
sizeLogo: 48.0,
paddingText: const EdgeInsets.only(left: 12),
text: AppLocalizations.of(context).manageEmailAsATeam,
@@ -137,7 +137,7 @@ class LoginView extends BaseLoginView {
padding: const EdgeInsets.only(top: 16),
child: SloganBuilder(
arrangedByHorizontal: true,
logoSVG: controller.imagePaths.icIntegration,
logo: controller.imagePaths.icIntegration,
sizeLogo: 48.0,
paddingText: const EdgeInsets.only(left: 12),
text: AppLocalizations.of(context).multipleIntegrations,
@@ -93,7 +93,6 @@ class MailboxView extends BaseMailboxView {
child: SafeArea(
top: false,
child: ApplicationVersionWidget(
applicationManager: controller.applicationManager,
padding: EdgeInsets.zero,
title: '${AppLocalizations.of(context).version} ',
textStyle: Theme.of(context).textTheme.labelMedium?.copyWith(
@@ -66,9 +66,7 @@ class MailboxView extends BaseMailboxView {
),
child: Row(children: [
ApplicationLogoWidthTextWidget(),
ApplicationVersionWidget(
applicationManager: controller.mailboxDashBoardController.applicationManager,
)
const ApplicationVersionWidget()
])
);
}
@@ -2,5 +2,5 @@
import 'package:core/utils/platform_info.dart';
class MailboxIconWidgetStyles {
static const double iconSize = PlatformInfo.isWeb ? 24 : 20;
static double iconSize = PlatformInfo.isWeb ? 24 : 20;
}
@@ -9,7 +9,7 @@ class LinagoraApp with EquatableMixin{
final String appName;
@JsonKey(name: 'icon')
final String iconName;
final String? iconName;
@JsonKey(name: 'appLink')
final Uri appUri;
@@ -17,15 +17,17 @@ class LinagoraApp with EquatableMixin{
final String? androidPackageId;
final String? iosUrlScheme;
final String? iosAppStoreLink;
final Uri? publicIconUri;
LinagoraApp(
this.appName,
this.iconName,
this.appUri,
{
this.iconName,
this.androidPackageId,
this.iosUrlScheme,
this.iosAppStoreLink
this.iosAppStoreLink,
this.publicIconUri
}
);
@@ -40,6 +42,7 @@ class LinagoraApp with EquatableMixin{
appUri,
androidPackageId,
iosUrlScheme,
iosAppStoreLink
iosAppStoreLink,
publicIconUri
];
}
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
class AppGridDashboardStyle {
static const double iconSize = 42;
static const double hoverIconSize = 80;
static const EdgeInsetsGeometry padding = EdgeInsetsDirectional.symmetric(horizontal: 10, vertical: 14);
static final List<BoxShadow> cardShadow = [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 20,
offset: Offset.zero
),
];
}
@@ -0,0 +1,61 @@
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:flutter_portal/flutter_portal.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_overlay.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';
class AppGridDashboardIcon extends StatelessWidget {
final ImagePaths _imagePaths = Get.find<ImagePaths>();
final AppGridDashboardController appGridController;
final VoidCallback? onShowAppDashboardAction;
AppGridDashboardIcon({
super.key,
required this.appGridController,
this.onShowAppDashboardAction
});
@override
Widget build(BuildContext context) {
return Obx(() {
final isAppGridOpen = appGridController.isAppGridDashboardOverlayOpen.value;
return PortalTarget(
visible: isAppGridOpen,
portalFollower: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: appGridController.toggleAppGridDashboard
),
child: PortalTarget(
anchor: Aligned(
follower: AppUtils.isDirectionRTL(context)
? Alignment.topLeft
: Alignment.topRight,
target: AppUtils.isDirectionRTL(context)
? Alignment.bottomLeft
: Alignment.bottomRight
),
portalFollower: Obx(() {
final listApps = appGridController.linagoraApplications.value;
if (listApps?.apps.isNotEmpty == true) {
return AppDashboardOverlay(listApps!);
}
return const SizedBox.shrink();
}),
visible: isAppGridOpen,
child: TMailButtonWidget.fromIcon(
icon: _imagePaths.icAppDashboard,
backgroundColor: Colors.transparent,
iconSize: 30,
padding: const EdgeInsets.all(6),
onTapActionCallback: onShowAppDashboardAction,
),
)
);
});
}
}
@@ -1,11 +1,11 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/widget/link_browser_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/app_grid_dashboard_style.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';
class AppGridDashboardItem extends StatelessWidget {
@@ -26,31 +26,46 @@ class AppGridDashboardItem extends StatelessWidget {
borderRadius: const BorderRadius.all(Radius.circular(16)),
hoverColor: AppColor.colorBgMailboxSelected,
child: Container(
width: 98,
width: AppGridDashboardStyle.hoverIconSize,
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(children: [
app.iconName.endsWith("svg")
? SvgPicture.asset(
_imagePaths.getConfigurationImagePath(app.iconName),
width: 56,
height: 56,
fit: BoxFit.fill)
: Image.asset(
_imagePaths.getConfigurationImagePath(app.iconName),
width: 56,
height: 56,
fit: BoxFit.fill),
if (app.iconName?.isNotEmpty == true)
app.iconName!.endsWith("svg")
? SvgPicture.asset(
_imagePaths.getConfigurationImagePath(app.iconName!),
width: AppGridDashboardStyle.iconSize,
height: AppGridDashboardStyle.iconSize,
fit: BoxFit.fill)
: Image.asset(
_imagePaths.getConfigurationImagePath(app.iconName!),
width: AppGridDashboardStyle.iconSize,
height: AppGridDashboardStyle.iconSize,
fit: BoxFit.fill)
else if (app.publicIconUri != null)
Image.network(
app.publicIconUri.toString(),
width: AppGridDashboardStyle.iconSize,
height: AppGridDashboardStyle.iconSize,
fit: BoxFit.fill,
errorBuilder: (_, error, stackTrace) {
return Container(
width: AppGridDashboardStyle.iconSize,
height: AppGridDashboardStyle.iconSize,
color: AppColor.textFieldHintColor,
);
}
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
app.appName,
maxLines: 1,
textAlign: TextAlign.center,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
style: const TextStyle(
color: AppColor.colorNameEmail,
fontSize: 15
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
),
@@ -1,6 +1,6 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/app_grid_dashboard_style.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_item.dart';
class AppDashboardOverlay extends StatelessWidget {
@@ -17,18 +17,9 @@ class AppDashboardOverlay extends StatelessWidget {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
boxShadow: const [
BoxShadow(
color: AppColor.colorShadowLayerBottom,
blurRadius: 96,
offset: Offset.zero),
BoxShadow(
color: AppColor.colorShadowLayerTop,
blurRadius: 2,
offset: Offset.zero),
]
boxShadow: AppGridDashboardStyle.cardShadow
),
padding: const EdgeInsets.all(24),
padding: AppGridDashboardStyle.padding,
child: Wrap(children: _linagoraApplications.apps
.map((app) => AppGridDashboardItem(app))
.toList()),
@@ -38,13 +29,11 @@ class AppDashboardOverlay extends StatelessWidget {
double get _widthAppGrid {
if (_linagoraApplications.apps.length >= 3) {
return 342;
return AppGridDashboardStyle.hoverIconSize * 3 + AppGridDashboardStyle.padding.horizontal;
} else if (_linagoraApplications.apps.length == 2) {
return 244;
} else if (_linagoraApplications.apps.length == 1) {
return 146;
return AppGridDashboardStyle.hoverIconSize * 2 + AppGridDashboardStyle.padding.horizontal;
} else {
return 0;
return AppGridDashboardStyle.hoverIconSize + AppGridDashboardStyle.padding.horizontal;
}
}
}
@@ -26,8 +26,10 @@ class AppListDashboardItem extends StatelessWidget {
text: app.appName,
textAlign: TextAlign.center,
textStyle: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorNameEmail),
logo: !app.iconName.endsWith('svg') ? imagePaths.getConfigurationImagePath(app.iconName) : null,
logoSVG: app.iconName.endsWith('svg') ? imagePaths.getConfigurationImagePath(app.iconName) : null,
logo: app.iconName?.isNotEmpty == true
? imagePaths.getConfigurationImagePath(app.iconName!)
: null,
publicLogoUri: app.publicIconUri,
onTapCallback: () => _openApp(context, app),
padding: const EdgeInsetsDirectional.symmetric(vertical: 12, horizontal: 20),
hoverColor: AppColor.colorBgMailboxSelected
@@ -27,9 +27,7 @@ class ManageAccountMenuView extends GetWidget<ManageAccountMenuController> {
padding: const EdgeInsetsDirectional.only(top: 16, bottom: 16, start: 16),
child: Row(children: [
ApplicationLogoWidthTextWidget(),
ApplicationVersionWidget(
applicationManager: controller.dashBoardController.applicationManager,
)
const ApplicationVersionWidget()
])
),
if (!controller.responsiveUtils.isWebDesktop(context))
@@ -26,7 +26,7 @@ class DeleteIdentityDialogBuilder extends StatelessWidget {
responsiveUtils: responsiveUtils,
mobile: (_buildDeleteDialog(context)
..alignment(Alignment.bottomCenter)
..outsideDialogPadding(const EdgeInsets.only(bottom: PlatformInfo.isWeb ? 42 : 16))
..outsideDialogPadding(EdgeInsets.only(bottom: PlatformInfo.isWeb ? 42 : 16))
..widthDialog(MediaQuery.of(context).size.width - 16))
.build(),
landscapeMobile: _buildDeleteDialog(context).build(),
@@ -50,7 +50,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
topLeft: Radius.circular(16),
topRight: Radius.circular(16)),
color: Colors.white),
margin: const EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0),
margin: EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0),
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
@@ -36,7 +36,7 @@ class RulesFilterInputField extends StatelessWidget {
focusNode: focusNode,
maxLines: 1,
decoration: (RulesFilterInputDecorationBuilder()
..setContentPadding(const EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 14 : 12, horizontal: 12))
..setContentPadding(EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 14 : 12, horizontal: 12))
..setHintText(hintText)
..setErrorText(errorText))
.build(),