From 3eb1e20b5bd3922779abe66400b1f6ae0d5ea98e Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 11 Apr 2023 14:21:42 +0700 Subject: [PATCH] TF-1665 Enable app grid on mobile (cherry picked from commit c9f9a2c214316606c8d3f9d63cf38c326369b112) --- android/app/src/main/AndroidManifest.xml | 2 + configurations/app_dashboard.json | 5 +- core/lib/presentation/utils/app_toast.dart | 2 + .../views/text/slogan_builder.dart | 163 ++++++++++-------- .../presentation/views/toast/tmail_toast.dart | 12 +- ios/Podfile.lock | 6 + ios/Runner/Info.plist | 3 +- .../login/presentation/login_view_web.dart | 60 ++++--- .../presentation/privacy_link_widget.dart | 5 +- .../mailbox/presentation/mailbox_view.dart | 46 ++--- .../presentation/mailbox_view_web.dart | 103 +---------- .../mixin/mailbox_widget_mixin.dart | 95 ++++++++++ .../domain/app_dashboard/linagora_app.dart | 26 ++- .../mailbox_dashboard_view_web.dart | 17 +- .../app_grid_dashboard_item.dart | 2 +- .../app_list_dashboard_item.dart | 96 +++++------ .../manage_account_dashboard_view.dart | 16 +- .../menu/manage_account_menu_view.dart | 14 +- lib/l10n/intl_messages.arb | 8 +- pubspec.lock | 8 + pubspec.yaml | 2 + 21 files changed, 383 insertions(+), 308 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cdbc364e8..5639ceb1c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -11,6 +11,8 @@ + + diff --git a/configurations/app_dashboard.json b/configurations/app_dashboard.json index f9be0850f..57c3178ba 100644 --- a/configurations/app_dashboard.json +++ b/configurations/app_dashboard.json @@ -8,7 +8,10 @@ { "appName": "LinShare", "icon": "ic_linshare_app.png", - "appLink": "https://linshare.linagora.com/" + "appLink": "https://linshare.linagora.com/", + "androidPackageId": "com.linagora.android.linshare", + "iosUrlScheme": "linshare.mobile", + "iosAppStoreLink": "itms-apps://itunes.apple.com/us/app/linshare/id1534003175" }, { "appName": "Inbox", diff --git a/core/lib/presentation/utils/app_toast.dart b/core/lib/presentation/utils/app_toast.dart index 0bfe88cd1..3bdff7d88 100644 --- a/core/lib/presentation/utils/app_toast.dart +++ b/core/lib/presentation/utils/app_toast.dart @@ -65,6 +65,7 @@ class AppToast { Color? textActionColor, TextStyle? textStyle, EdgeInsets? padding, + TextAlign? textAlign } ) { final responsiveUtils = Get.find(); @@ -151,6 +152,7 @@ class AppToast { trailing: trailingWidget, leading: leadingWidget, padding: padding, + textAlign: textAlign, toastDuration: infinityToast ? null : 3, ); } diff --git a/core/lib/presentation/views/text/slogan_builder.dart b/core/lib/presentation/views/text/slogan_builder.dart index c2ca25097..98ffaa713 100644 --- a/core/lib/presentation/views/text/slogan_builder.dart +++ b/core/lib/presentation/views/text/slogan_builder.dart @@ -1,3 +1,4 @@ +import 'package:core/presentation/utils/style_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -7,94 +8,110 @@ import 'package:flutter_svg/flutter_svg.dart'; typedef OnTapCallback = void Function(); -class SloganBuilder { +class SloganBuilder extends StatelessWidget { final bool arrangedByHorizontal; + final String? text; + final TextStyle? textStyle; + final TextAlign? textAlign; + final String? logoSVG; + final String? logo; + final double? sizeLogo; + final OnTapCallback? onTapCallback; + final EdgeInsets? paddingText; + final EdgeInsets? padding; + final bool enableOverflow; + final Color? hoverColor; + final double? hoverRadius; - Key? _key; - String? _text; - TextStyle? _textStyle; - TextAlign? _textAlign; - String? _logoSVG; - String? _logo; - double? _sizeLogo; - OnTapCallback? _onTapCallback; - EdgeInsetsGeometry? _padding; + const SloganBuilder({ + super.key, + this.arrangedByHorizontal = true, + this.enableOverflow = false, + this.text, + this.textStyle, + this.textAlign, + this.logoSVG, + this.logo, + this.sizeLogo, + this.onTapCallback, + this.padding, + this.paddingText, + this.hoverColor, + this.hoverRadius + }); - SloganBuilder({this.arrangedByHorizontal = false}); - - void key(Key key) { - _key = key; - } - - void setSloganText(String text) { - _text = text; - } - - void setSloganTextStyle(TextStyle textStyle) { - _textStyle = textStyle; - } - - void setSloganTextAlign(TextAlign textAlign) { - _textAlign = textAlign; - } - - void setLogo(String logo) { - _logo = logo; - } - - void setLogoSVG(String logoSVG) { - _logoSVG = logoSVG; - } - - void setSizeLogo(double? size) { - _sizeLogo = size; - } - - void addOnTapCallback(OnTapCallback? onTapCallback) { - _onTapCallback = onTapCallback; - } - - void setPadding(EdgeInsetsGeometry? padding) { - _padding = padding; - } - - Widget build() { + @override + Widget build(BuildContext context) { if (!arrangedByHorizontal) { - return InkWell( - onTap: () => _onTapCallback?.call(), - child: Column(children: [ - _logoApp(), - Padding( - padding: _padding ?? const EdgeInsets.only(top: 16, left: 16, right: 16), - child: Text(_text ?? '', key: _key, style: _textStyle, textAlign: _textAlign), + return Material( + color: Colors.transparent, + child: InkWell( + onTap: onTapCallback, + hoverColor: hoverColor, + borderRadius: BorderRadius.all(Radius.circular(hoverRadius ?? 8)), + child: Padding( + padding: padding ?? EdgeInsets.zero, + child: Column(children: [ + _logoApp(), + Padding( + padding: paddingText ?? const EdgeInsets.only(top: 16, left: 16, right: 16), + child: Text( + text ?? '', + style: textStyle, + textAlign: textAlign, + overflow: enableOverflow ? CommonTextStyle.defaultTextOverFlow : null, + softWrap: enableOverflow ? CommonTextStyle.defaultSoftWrap : null, + maxLines: enableOverflow ? 1 : null, + ), + ), + ]), ), - ]), + ), ); } else { - return InkWell( - onTap: () => _onTapCallback?.call(), - child: Row(children: [ - _logoApp(), - Padding( - padding: _padding ?? const EdgeInsets.symmetric(horizontal: 10), - child: Text(_text ?? '', key: _key, style: _textStyle, textAlign: _textAlign), + return Material( + color: Colors.transparent, + child: InkWell( + onTap: onTapCallback, + hoverColor: hoverColor, + radius: hoverRadius ?? 8, + borderRadius: BorderRadius.all(Radius.circular(hoverRadius ?? 8)), + child: Padding( + padding: padding ?? EdgeInsets.zero, + child: Row(children: [ + _logoApp(), + Padding( + padding: paddingText ?? const EdgeInsets.symmetric(horizontal: 10), + child: Text( + text ?? '', + style: textStyle, + textAlign: textAlign, + overflow: enableOverflow ? CommonTextStyle.defaultTextOverFlow : null, + softWrap: enableOverflow ? CommonTextStyle.defaultSoftWrap : null, + maxLines: enableOverflow ? 1 : null, + ), + ), + ]), ), - ]), + ), ); } } Widget _logoApp() { - if (_logoSVG != null) { - return SvgPicture.asset(_logoSVG!, width: _sizeLogo ?? 150, height: _sizeLogo ?? 150); - } else if (_logo != null) { + if (logoSVG != null) { + return SvgPicture.asset( + logoSVG!, + width: sizeLogo ?? 150, + height: sizeLogo ?? 150); + } else if (logo != null) { return Image( - image: AssetImage(_logo!), - fit: BoxFit.fill, - width: _sizeLogo ?? 150, - height: _sizeLogo ?? 150, - alignment: Alignment.center); + image: AssetImage(logo!), + fit: BoxFit.fill, + width: sizeLogo ?? 150, + height: sizeLogo ?? 150, + alignment: Alignment.center); } return const SizedBox.shrink(); } diff --git a/core/lib/presentation/views/toast/tmail_toast.dart b/core/lib/presentation/views/toast/tmail_toast.dart index 537282f82..45645089d 100644 --- a/core/lib/presentation/views/toast/tmail_toast.dart +++ b/core/lib/presentation/views/toast/tmail_toast.dart @@ -38,6 +38,9 @@ class TMailToast { /// defines the padding widget of the toast EdgeInsets? padding; + /// defines the align text of the toast + TextAlign? textAlign; + static showToast( text, BuildContext context, { @@ -55,6 +58,7 @@ class TMailToast { leading, width, padding, + textAlign }) { assert(text != null); ToastView.dismiss(); @@ -70,6 +74,7 @@ class TMailToast { leading, width, padding, + textAlign, toastDuration: toastDuration, ); } @@ -96,6 +101,7 @@ class ToastView { Widget? leading, double? width, EdgeInsets? padding, + TextAlign? textAlign, {int? toastDuration} ) async { overlayState = Overlay.of(context, rootOverlay: false); @@ -130,7 +136,8 @@ class ToastView { fontSize: 15, color: Colors.white, fontWeight: FontWeight.normal - ) + ), + textAlign: textAlign ) : Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -144,7 +151,8 @@ class ToastView { fontSize: 15, color: Colors.white, fontWeight: FontWeight.normal - ) + ), + textAlign: textAlign ) )), if (trailing != null) trailing diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c0946f4ce..390896e64 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -45,6 +45,8 @@ PODS: - DKPhotoGallery/Resource (0.0.17): - SDWebImage - SwiftyGif + - external_app_launcher (0.0.1): + - Flutter - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter @@ -176,6 +178,7 @@ DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - contacts_service (from `.symlinks/plugins/contacts_service/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - external_app_launcher (from `.symlinks/plugins/external_app_launcher/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) @@ -227,6 +230,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/contacts_service/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" + external_app_launcher: + :path: ".symlinks/plugins/external_app_launcher/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" firebase_core: @@ -274,6 +279,7 @@ SPEC CHECKSUMS: device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + external_app_launcher: ad55ac844aa21f2d2197d7cec58ff0d0dc40bbc0 file_picker: ce3938a0df3cc1ef404671531facef740d03f920 Firebase: f92fc551ead69c94168d36c2b26188263860acd9 firebase_core: 128d8c43c3a453a4a67463314fc3761bedff860b diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 763638451..e96e78643 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -47,7 +47,8 @@ http mailto sms - tel + tel + linshare.mobile LSRequiresIPhoneOS diff --git a/lib/features/login/presentation/login_view_web.dart b/lib/features/login/presentation/login_view_web.dart index 00eb35b30..a1c0af396 100644 --- a/lib/features/login/presentation/login_view_web.dart +++ b/lib/features/login/presentation/login_view_web.dart @@ -120,43 +120,47 @@ class LoginView extends BaseLoginView { ), Padding( padding: const EdgeInsets.only(top: 24), - child: (SloganBuilder(arrangedByHorizontal: true, ) - ..setLogoSVG(imagePaths.icJMAPStandard) - ..setSizeLogo(48.0) - ..setPadding(const EdgeInsets.only(left: 12)) - ..setSloganText(AppLocalizations.of(context).jmapStandard) - ..setSloganTextStyle(const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail))) - .build() + child: SloganBuilder( + arrangedByHorizontal: true, + logoSVG: imagePaths.icJMAPStandard, + sizeLogo: 48.0, + paddingText: const EdgeInsets.only(left: 12), + text: AppLocalizations.of(context).jmapStandard, + textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail) + ) ), Padding( padding: const EdgeInsets.only(top: 16), - child: (SloganBuilder(arrangedByHorizontal: true, ) - ..setLogoSVG(imagePaths.icEncrypted) - ..setSizeLogo(48.0) - ..setPadding(const EdgeInsets.only(left: 12)) - ..setSloganText(AppLocalizations.of(context).encryptedMailbox) - ..setSloganTextStyle(const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail))) - .build() + child: SloganBuilder( + arrangedByHorizontal: true, + logoSVG: imagePaths.icEncrypted, + sizeLogo: 48.0, + paddingText: const EdgeInsets.only(left: 12), + text: AppLocalizations.of(context).encryptedMailbox, + textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail) + ) ), Padding( padding: const EdgeInsets.only(top: 16), - child: (SloganBuilder(arrangedByHorizontal: true, ) - ..setLogoSVG(imagePaths.icTeam) - ..setSizeLogo(48.0) - ..setPadding(const EdgeInsets.only(left: 12)) - ..setSloganText(AppLocalizations.of(context).manageEmailAsATeam) - ..setSloganTextStyle(const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail))) - .build() + child: SloganBuilder( + arrangedByHorizontal: true, + logoSVG: imagePaths.icTeam, + sizeLogo: 48.0, + paddingText: const EdgeInsets.only(left: 12), + text: AppLocalizations.of(context).manageEmailAsATeam, + textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail) + ) ), Padding( padding: const EdgeInsets.only(top: 16), - child: (SloganBuilder(arrangedByHorizontal: true, ) - ..setLogoSVG(imagePaths.icIntegration) - ..setSizeLogo(48.0) - ..setPadding(const EdgeInsets.only(left: 12)) - ..setSloganText(AppLocalizations.of(context).multipleIntegrations) - ..setSloganTextStyle(const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail))) - .build() + child: SloganBuilder( + arrangedByHorizontal: true, + logoSVG: imagePaths.icIntegration, + sizeLogo: 48.0, + paddingText: const EdgeInsets.only(left: 12), + text: AppLocalizations.of(context).multipleIntegrations, + textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w400, color: AppColor.colorNameEmail) + ) ), Padding( padding: const EdgeInsets.only(top: 44), diff --git a/lib/features/login/presentation/privacy_link_widget.dart b/lib/features/login/presentation/privacy_link_widget.dart index c6c05ac5d..86c2c41cc 100644 --- a/lib/features/login/presentation/privacy_link_widget.dart +++ b/lib/features/login/presentation/privacy_link_widget.dart @@ -21,7 +21,10 @@ class PrivacyLinkWidget extends StatelessWidget { fontSize: 14), recognizer: TapGestureRecognizer()..onTap = () async { if (await canLaunchUrlString(privacyUrlString)) { - launchUrlString(privacyUrlString); + launchUrlString( + privacyUrlString, + mode: LaunchMode.externalApplication + ); } } ) diff --git a/lib/features/mailbox/presentation/mailbox_view.dart b/lib/features/mailbox/presentation/mailbox_view.dart index 0f1fceb6c..53777a059 100644 --- a/lib/features/mailbox/presentation/mailbox_view.dart +++ b/lib/features/mailbox/presentation/mailbox_view.dart @@ -14,6 +14,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_fold import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget_builder.dart'; import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_footer_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/utils/app_config.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; class MailboxView extends GetWidget @@ -189,20 +190,25 @@ class MailboxView extends GetWidget return _buildUserInformation(context); }), _buildLoadingView(), + AppConfig.appGridDashboardAvailable + ? Column(children: [ + buildAppGridDashboard(context, _responsiveUtils, _imagePaths, controller), + const Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2), + ]) + : const SizedBox.shrink(), + const SizedBox(height: 8), Obx(() { if (controller.defaultMailboxIsNotEmpty) { - return Padding( - padding: const EdgeInsets.only(top: 16), - child: _buildMailboxCategory( - context, - MailboxCategories.exchange, - controller.defaultRootNode - ), + return _buildMailboxCategory( + context, + MailboxCategories.exchange, + controller.defaultRootNode ); } else { return const SizedBox.shrink(); } }), + const SizedBox(height: 8), const Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2), const SizedBox(height: 12), Container( @@ -247,19 +253,19 @@ class MailboxView extends GetWidget ]), ]), ), - const SizedBox(height: 8), - Obx(() { - if (controller.personalMailboxIsNotEmpty) { - return _buildMailboxCategory( - context, - MailboxCategories.personalMailboxes, - controller.personalRootNode - ); - } else { - return const SizedBox.shrink(); - } - }), - const SizedBox(height: 8), + const SizedBox(height: 8), + Obx(() { + if (controller.personalMailboxIsNotEmpty) { + return _buildMailboxCategory( + context, + MailboxCategories.personalMailboxes, + controller.personalRootNode + ); + } else { + return const SizedBox.shrink(); + } + }), + const SizedBox(height: 8), Obx(() { if (controller.teamMailboxesIsNotEmpty) { return _buildMailboxCategory( diff --git a/lib/features/mailbox/presentation/mailbox_view_web.dart b/lib/features/mailbox/presentation/mailbox_view_web.dart index 925bcd798..5838b3d96 100644 --- a/lib/features/mailbox/presentation/mailbox_view_web.dart +++ b/lib/features/mailbox/presentation/mailbox_view_web.dart @@ -11,7 +11,6 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_catego import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget_builder.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart'; import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_footer_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; @@ -79,13 +78,13 @@ class MailboxView extends GetWidget right: AppUtils.isDirectionRTL(context) ? _responsiveUtils.isDesktop(context) ? 32 : 16 : 0 ), child: Row(children: [ - (SloganBuilder(arrangedByHorizontal: true) - ..setSloganText(AppLocalizations.of(context).app_name) - ..setSloganTextAlign(TextAlign.center) - ..setSloganTextStyle(const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold)) - ..setSizeLogo(24) - ..setLogo(_imagePaths.icLogoTMail)) - .build(), + SloganBuilder( + sizeLogo: 24, + text: AppLocalizations.of(context).app_name, + textAlign: TextAlign.center, + textStyle: const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold), + logo: _imagePaths.icLogoTMail + ), Obx(() { if (controller.mailboxDashBoardController.appInformation.value != null) { return _buildVersionInformation(context, controller.mailboxDashBoardController.appInformation.value!); @@ -122,7 +121,7 @@ class MailboxView extends GetWidget _buildLoadingView(), AppConfig.appGridDashboardAvailable && _responsiveUtils.isWebNotDesktop(context) ? Column(children: [ - _buildAppGridDashboard(context), + buildAppGridDashboard(context, _responsiveUtils, _imagePaths, controller), const SizedBox(height: 8), const Divider(color: AppColor.colorDividerMailbox, height: 0.5, thickness: 0.2), const SizedBox(height: 8), @@ -389,90 +388,4 @@ class MailboxView extends GetWidget ), ); } - - Widget _buildAppGridDashboard(BuildContext context) { - return Column( - children: [ - _buildGoToApplicationsCategory(context, MailboxCategories.appGrid), - AnimatedContainer( - padding: const EdgeInsets.only(top: 8), - duration: const Duration(milliseconds: 400), - child: Obx(() { - return controller.mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value == ExpandMode.EXPAND - ? _buildAppGridInMailboxView(context) - : const Offstage(); - }) - ) - ]); - } - - Widget _buildGoToApplicationsCategory(BuildContext context, MailboxCategories categories) { - return Padding( - padding: EdgeInsets.only( - top: 8, - left: _responsiveUtils.isDesktop(context) ? 0 : 36, - right: _responsiveUtils.isDesktop(context) ? 0 : 28 - ), - child: Row( - children: [ - buildIconWeb( - splashRadius: 5, - iconPadding: EdgeInsets.zero, - minSize: 12, - iconSize: 28, - icon: SvgPicture.asset( - _imagePaths.icAppDashboard, - colorFilter: AppColor.primaryColor.asFilter(), - fit: BoxFit.fill - ), - tooltip: AppLocalizations.of(context).appGridTittle), - Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 12), - child: Text(categories.getTitle(context), - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 16, - color: AppColor.colorTextButton, - fontWeight: FontWeight.w500 - ) - ) - ) - ), - buildIconWeb( - splashRadius: 5, - iconPadding: EdgeInsets.zero, - minSize: 12, - iconSize: 28, - icon: Obx(() => SvgPicture.asset( - controller.mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value == ExpandMode.COLLAPSE - ? _imagePaths.icCollapseFolder - : _imagePaths.icExpandFolder, - colorFilter: AppColor.primaryColor.asFilter(), - fit: BoxFit.fill - )), - tooltip: AppLocalizations.of(context).appGridTittle, - onTap: () => controller.toggleMailboxCategories(categories) - ), - ] - ) - ); - } - - Widget _buildAppGridInMailboxView(BuildContext context) { - return Obx(() { - final linagoraApps = controller.mailboxDashBoardController.appGridDashboardController.linagoraApplications.value; - if (linagoraApps != null && linagoraApps.apps.isNotEmpty) { - return ListView.builder( - shrinkWrap: true, - itemCount: linagoraApps.apps.length, - itemBuilder: (context, index) { - return AppListDashboardItem(linagoraApps.apps[index]); - } - ); - } - return const SizedBox.shrink(); - }); - } } \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart b/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart index 4bc2bf4ee..f197627c2 100644 --- a/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart +++ b/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart @@ -17,6 +17,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/context_item_m import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_bottom_sheet_action_tile_builder.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; mixin MailboxWidgetMixin { @@ -321,4 +322,98 @@ mixin MailboxWidgetMixin { ]) ); } + + Widget buildAppGridDashboard( + BuildContext context, + ResponsiveUtils responsiveUtils, + ImagePaths imagePaths, + MailboxController controller + ) { + return Column(children: [ + _buildGoToApplicationsCategory( + context, + responsiveUtils, + imagePaths, + MailboxCategories.appGrid, + controller), + AnimatedContainer( + padding: const EdgeInsets.only(top: 8), + duration: const Duration(milliseconds: 400), + child: Obx(() { + return controller.mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value == ExpandMode.EXPAND + ? _buildAppGridInMailboxView(context, controller) + : const Offstage(); + }) + ) + ]); + } + + Widget _buildGoToApplicationsCategory( + BuildContext context, + ResponsiveUtils responsiveUtils, + ImagePaths imagePaths, + MailboxCategories categories, + MailboxController controller + ) { + return Padding( + padding: EdgeInsets.only( + top: 8, + left: responsiveUtils.isDesktop(context) ? 0 : 36, + right: responsiveUtils.isDesktop(context) ? 0 : 28 + ), + child: Row(children: [ + buildIconWeb( + splashRadius: 5, + iconPadding: EdgeInsets.zero, + minSize: 12, + iconSize: 28, + icon: SvgPicture.asset( + imagePaths.icAppDashboard, + colorFilter: AppColor.primaryColor.asFilter(), + fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).appGridTittle), + Expanded(child: Padding( + padding: const EdgeInsets.only(left: 12), + child: Text(categories.getTitle(context), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 16, + color: AppColor.colorTextButton, + fontWeight: FontWeight.w500 + ) + ) + )), + buildIconWeb( + icon: Obx(() => SvgPicture.asset( + controller.mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value == ExpandMode.COLLAPSE + ? imagePaths.icCollapseFolder + : imagePaths.icExpandFolder, + colorFilter: AppColor.primaryColor.asFilter(), + fit: BoxFit.fill + )), + tooltip: AppLocalizations.of(context).appGridTittle, + onTap: () => controller.toggleMailboxCategories(categories) + ) + ]) + ); + } + + Widget _buildAppGridInMailboxView(BuildContext context, MailboxController controller) { + return Obx(() { + final linagoraApps = controller.mailboxDashBoardController.appGridDashboardController.linagoraApplications.value; + if (linagoraApps != null && linagoraApps.apps.isNotEmpty) { + return ListView.builder( + shrinkWrap: true, + primary: false, + padding: const EdgeInsets.symmetric(horizontal: 16), + itemCount: linagoraApps.apps.length, + itemBuilder: (context, index) { + return AppListDashboardItem(linagoraApps.apps[index]); + } + ); + } + return const SizedBox.shrink(); + }); + } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart b/lib/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart index 8624b6d40..29484fd4a 100644 --- a/lib/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart +++ b/lib/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart @@ -3,7 +3,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'linagora_app.g.dart'; -@JsonSerializable() +@JsonSerializable(explicitToJson: true, includeIfNull: false) class LinagoraApp with EquatableMixin{ @JsonKey(name: 'appName') final String appName; @@ -14,12 +14,32 @@ class LinagoraApp with EquatableMixin{ @JsonKey(name: 'appLink') final Uri appUri; - LinagoraApp(this.appName, this.iconName, this.appUri); + final String? androidPackageId; + final String? iosUrlScheme; + final String? iosAppStoreLink; + + LinagoraApp( + this.appName, + this.iconName, + this.appUri, + { + this.androidPackageId, + this.iosUrlScheme, + this.iosAppStoreLink + } + ); factory LinagoraApp.fromJson(Map json) => _$LinagoraAppFromJson(json); Map toJson() => _$LinagoraAppToJson(this); @override - List get props => [appName, iconName, appUri]; + List get props => [ + appName, + iconName, + appUri, + androidPackageId, + iosUrlScheme, + iosAppStoreLink + ]; } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart index 32c470a21..156ac5ab2 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -69,16 +69,13 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { alignment: Alignment.center, height: 80, child: Row(children: [ - (SloganBuilder(arrangedByHorizontal: true) - ..setSloganText(AppLocalizations.of(context).app_name) - ..setSloganTextAlign(TextAlign.center) - ..setSloganTextStyle(const TextStyle( - color: Colors.black, - fontSize: 20, - fontWeight: FontWeight.bold)) - ..setSizeLogo(24) - ..setLogo(imagePaths.icLogoTMail)) - .build(), + SloganBuilder( + sizeLogo: 24, + text: AppLocalizations.of(context).app_name, + textAlign: TextAlign.center, + textStyle: const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold), + logo: imagePaths.icLogoTMail + ), Obx(() { if (controller.appInformation.value != null) { return Padding(padding: const EdgeInsets.only(top: 6), diff --git a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_item.dart b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_item.dart index a52ac5ced..faf61125d 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_item.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_item.dart @@ -24,7 +24,7 @@ class AppGridDashboardItem extends StatelessWidget { child: InkWell( onTap: _openApp, borderRadius: const BorderRadius.all(Radius.circular(16)), - hoverColor: AppColor.colorItemSelected, + hoverColor: AppColor.colorBgMailboxSelected, child: Container( width: 98, padding: const EdgeInsets.symmetric(vertical: 8), diff --git a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart index 8b72224be..4bc9c5f0f 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart @@ -1,76 +1,58 @@ -import 'package:core/core.dart'; +import 'dart:io'; + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/text/slogan_builder.dart'; +import 'package:core/utils/build_utils.dart'; +import 'package:external_app_launcher/external_app_launcher.dart'; import 'package:flutter/material.dart'; import 'package:get/get_core/get_core.dart'; import 'package:get/get_instance/get_instance.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart'; import 'package:url_launcher/url_launcher.dart' as launcher; -class AppListDashboardItem extends StatefulWidget { +class AppListDashboardItem extends StatelessWidget { + final LinagoraApp app; const AppListDashboardItem(this.app, {Key? key}) : super(key: key); - @override - State createState() => _AppListDashboardItemState(); -} - -class _AppListDashboardItemState extends State { - final _imagePaths = Get.find(); - final _responsiveUtils = Get.find(); - bool _isHoverItem = false; - @override Widget build(BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return InkWell( - onTap: () => _openApp(widget.app.appUri), - onHover: (value) => setState(() => _isHoverItem = value), - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(10)), - color: _backgroundColorItem(context)), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 18), - margin: const EdgeInsets.symmetric(horizontal: 16), - child: _buildAppItem(context) - ), - ); - } + final imagePaths = Get.find(); + return SloganBuilder( + sizeLogo: 32, + paddingText: const EdgeInsets.only(left: 12), + 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, + onTapCallback: () => _openApp(context, app), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), + hoverColor: AppColor.colorBgMailboxSelected ); } - Widget _buildAppItem(BuildContext context) { - final builder = SloganBuilder(arrangedByHorizontal: true) - ..setSizeLogo(32.0) - ..setPadding(const EdgeInsets.only(left: 12)) - ..setSloganText(widget.app.appName) - ..addOnTapCallback(() async { - final url = widget.app.appUri; - if (await launcher.canLaunchUrl(url)) { - await launcher.launchUrl(url); - } - }) - ..setSloganTextStyle(const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorNameEmail)); - if (widget.app.iconName.endsWith('svg')) { - builder.setLogoSVG(_imagePaths.getConfigurationImagePath(widget.app.iconName)); + void _openApp(BuildContext context, LinagoraApp app) async { + if (BuildUtils.isWeb) { + if (await launcher.canLaunchUrl(app.appUri)) { + await launcher.launchUrl(app.appUri); + } + } else if (Platform.isAndroid && app.androidPackageId?.isNotEmpty == true) { + await LaunchApp.openApp(androidPackageName: app.androidPackageId); + } else if (Platform.isIOS && app.iosUrlScheme?.isNotEmpty == true) { + await LaunchApp.openApp( + iosUrlScheme: '${app.iosUrlScheme}://', + appStoreLink: app.iosAppStoreLink + ); } else { - builder.setLogo(_imagePaths.getConfigurationImagePath(widget.app.iconName)); - } - return builder.build(); - } - - Color _backgroundColorItem(BuildContext context) { - if (_isHoverItem) { - return AppColor.colorBgMailboxSelected; - } - return _responsiveUtils.isDesktop(context) - ? AppColor.colorBgDesktop - : Colors.white; - } - - void _openApp(Uri appLink) async { - if (await launcher.canLaunchUrl(appLink)) { - await launcher.launchUrl(appLink); + if (await launcher.canLaunchUrl(app.appUri)) { + await launcher.launchUrl( + app.appUri, + mode: launcher.LaunchMode.externalApplication + ); + } } } } diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart index c1d2377c7..2415428d2 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart @@ -49,14 +49,14 @@ class ManageAccountDashBoardView extends GetWidget controller.backToMailboxDashBoard(context)) - ..setLogo(_imagePaths.icLogoTMail)) - .build(), + SloganBuilder( + sizeLogo: 24, + text: AppLocalizations.of(context).app_name, + textAlign: TextAlign.center, + textStyle: const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold), + logo: _imagePaths.icLogoTMail, + onTapCallback: () => controller.backToMailboxDashBoard(context), + ), Obx(() { if (controller.appInformation.value != null) { return Padding(padding: const EdgeInsets.only(top: 6), diff --git a/lib/features/manage_account/presentation/menu/manage_account_menu_view.dart b/lib/features/manage_account/presentation/menu/manage_account_menu_view.dart index ed6562ff8..4194eda73 100644 --- a/lib/features/manage_account/presentation/menu/manage_account_menu_view.dart +++ b/lib/features/manage_account/presentation/menu/manage_account_menu_view.dart @@ -29,13 +29,13 @@ class ManageAccountMenuView extends GetWidget { color: Colors.white, padding: const EdgeInsets.only(top: 16, bottom: 16, left: 16), child: Row(children: [ - (SloganBuilder(arrangedByHorizontal: true) - ..setSloganText(AppLocalizations.of(context).app_name) - ..setSloganTextAlign(TextAlign.center) - ..setSloganTextStyle(const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold)) - ..setSizeLogo(24) - ..setLogo(_imagePaths.icLogoTMail)) - .build(), + SloganBuilder( + sizeLogo: 24, + text: AppLocalizations.of(context).app_name, + textAlign: TextAlign.center, + textStyle: const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold), + logo: _imagePaths.icLogoTMail + ), Obx(() { if (controller.dashBoardController.appInformation.value != null) { return Padding( diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 4db12f6dc..67c34bc47 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-04-06T22:36:07.919763", + "@@last_modified": "2023-04-12T12:10:00.382074", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2751,5 +2751,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "emptyListEmailForward": "Please input at least one recipient", + "@emptyListEmailForward": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index ab2baaed3..69506d8b2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -385,6 +385,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.5" + external_app_launcher: + dependency: "direct main" + description: + name: external_app_launcher + sha256: fb55cddd706c62ede11056750d5e018ef379820e09739e967873211dd537d833 + url: "https://pub.dev" + source: hosted + version: "3.1.0" external_path: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 01d207e5b..7676a5362 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -193,6 +193,8 @@ dependencies: super_tag_editor: 0.1.1 + external_app_launcher: 3.1.0 + dev_dependencies: flutter_test: sdk: flutter