diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index bb7ec1782e..0e5e7e687d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -11,8 +11,6 @@ - - diff --git a/backend-docker/docker-compose.yaml b/backend-docker/docker-compose.yaml index c42db448ca..74acc5279b 100644 --- a/backend-docker/docker-compose.yaml +++ b/backend-docker/docker-compose.yaml @@ -2,7 +2,7 @@ version: "3" services: tmail-backend: - image: linagora/tmail-backend:memory-1.0.0 + image: linagora/tmail-backend:memory-1.0.1-rc1 container_name: tmail-backend volumes: - ./jwt_publickey:/root/conf/jwt_publickey @@ -10,6 +10,7 @@ services: - ./mailetcontainer.xml:/root/conf/mailetcontainer.xml - ./imapserver.xml:/root/conf/imapserver.xml - ./jmap.properties:/root/conf/jmap.properties + - ./linagora-ecosystem.properties:/root/conf/linagora-ecosystem.properties - ../provisioning/integration_test/search_email_with_sort_order/provisioning.sh:/root/conf/integration_test/search_email_with_sort_order/provisioning.sh - ../provisioning/integration_test/search_email_with_sort_order/eml:/root/conf/integration_test/search_email_with_sort_order/eml ports: diff --git a/backend-docker/linagora-ecosystem.properties b/backend-docker/linagora-ecosystem.properties new file mode 100644 index 0000000000..3ca4d78ce5 --- /dev/null +++ b/backend-docker/linagora-ecosystem.properties @@ -0,0 +1,11 @@ +Twake_Drive.appName=Twake Drive +Twake_Drive.logoURL=https://sign-up.stg.lin-saas.com/images/tdrive.svg +Twake_Drive.webLink=https://drive.stg.lin-saas.com/ +mobileApps.Twake_Chat.appName=Twake Chat +mobileApps.Twake_Chat.logoURL=https://sign-up.stg.lin-saas.com/images/twakechat.svg +mobileApps.Twake_Chat.androidPackageId=app.twake.android.chat +mobileApps.Twake_Chat.iosUrlScheme=twake.chat +mobileApps.Twake_Chat.iosAppStoreLink=itms-apps://itunes.apple.com/us/app/twake-chat/id6473384641 +mobileApps.Twake_Sync.appName=Twake Sync +mobileApps.Twake_Sync.logoURL=https://twake.app/tild3364-6130-4763-b634-343435643861__twp-logo_1.svg +mobileApps.Twake_Sync.androidPackageId=com.twake.android.sync \ No newline at end of file diff --git a/backend-docker/mailetcontainer.xml b/backend-docker/mailetcontainer.xml index 383173ef8b..a0c694fd62 100644 --- a/backend-docker/mailetcontainer.xml +++ b/backend-docker/mailetcontainer.xml @@ -65,6 +65,10 @@ bcc ignore + + X-SMIME-Status + ignore + rrt-error @@ -88,9 +92,6 @@ - - ContactAttribute1 - @@ -151,5 +152,4 @@ - - + \ No newline at end of file diff --git a/core/lib/core.dart b/core/lib/core.dart index 6245d16725..a919c323db 100644 --- a/core/lib/core.dart +++ b/core/lib/core.dart @@ -94,6 +94,7 @@ export 'presentation/views/container/tmail_container_widget.dart'; export 'presentation/views/clipper/side_arrow_clipper.dart'; export 'presentation/views/avatar/gradient_circle_avatar_icon.dart'; export 'presentation/views/loading/cupertino_loading_widget.dart'; +export 'presentation/views/image/image_loader_mixin.dart'; // Resources export 'presentation/resources/assets_paths.dart'; diff --git a/core/lib/data/constants/constant.dart b/core/lib/data/constants/constant.dart index 06b5d45eae..575efb4eae 100644 --- a/core/lib/data/constants/constant.dart +++ b/core/lib/data/constants/constant.dart @@ -1,12 +1,14 @@ class Constant { - static const acceptHeaderDefault = 'application/json'; - static const contentTypeHeaderDefault = 'application/json'; - static const pdfMimeType = 'application/pdf'; - static const base64Charset = 'base64'; - static const textHtmlMimeType = 'text/html'; - static const octetStreamMimeType = 'application/octet-stream'; - static const pdfExtension = '.pdf'; - static const imageType = 'image'; - static const textVCardMimeType = 'text/x-vcard'; - static const textPlainMimeType = 'text/plain'; + static const String acceptHeaderDefault = 'application/json'; + static const String contentTypeHeaderDefault = 'application/json'; + static const String pdfMimeType = 'application/pdf'; + static const String base64Charset = 'base64'; + static const String textHtmlMimeType = 'text/html'; + static const String octetStreamMimeType = 'application/octet-stream'; + static const String pdfExtension = '.pdf'; + static const String imageType = 'image'; + static const String textVCardMimeType = 'text/x-vcard'; + static const String textPlainMimeType = 'text/plain'; + static const String slashCharacter = '/'; + static const String andCharacter = '&'; } \ No newline at end of file diff --git a/core/lib/presentation/views/image/image_loader_mixin.dart b/core/lib/presentation/views/image/image_loader_mixin.dart new file mode 100644 index 0000000000..faee9cca79 --- /dev/null +++ b/core/lib/presentation/views/image/image_loader_mixin.dart @@ -0,0 +1,70 @@ + +import 'package:core/utils/app_logger.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +mixin ImageLoaderMixin { + + Widget buildImage({ + required String imagePath, + double? imageSize + }) { + if (isImageNetworkLink(imagePath) && isImageSVG(imagePath)) { + return SvgPicture.network( + imagePath, + width: imageSize ?? 150, + height: imageSize ?? 150, + fit: BoxFit.fill, + placeholderBuilder: (_) { + return const CupertinoActivityIndicator(); + }, + ); + } else if (isImageNetworkLink(imagePath)) { + return Image.network( + imagePath, + fit: BoxFit.fill, + width: imageSize ?? 150, + height: imageSize ?? 150, + loadingBuilder: (_, child, loadingProgress) { + if (loadingProgress != null && + loadingProgress.cumulativeBytesLoaded != loadingProgress.expectedTotalBytes) { + return const Center( + child: CupertinoActivityIndicator(), + ); + } + return child; + }, + errorBuilder: (context, error, stackTrace) { + logError('ImageLoaderMixin::buildImage:Exception = $error'); + return Container( + width: imageSize ?? 150, + height: imageSize ?? 150, + alignment: Alignment.center, + child: const Icon(Icons.error_outline), + ); + }, + ); + } else if (isImageSVG(imagePath)) { + return SvgPicture.asset( + imagePath, + width: imageSize ?? 150, + height: imageSize ?? 150, + ); + } else { + return Image.asset( + imagePath, + fit: BoxFit.fill, + width: imageSize ?? 150, + height: imageSize ?? 150, + ); + } + } + + bool isImageNetworkLink(String imagePath) { + return imagePath.startsWith('http') == true || + imagePath.startsWith('https') == true; + } + + bool isImageSVG(String imagePath) => imagePath.endsWith('svg') == true; +} \ No newline at end of file diff --git a/core/lib/presentation/views/text/slogan_builder.dart b/core/lib/presentation/views/text/slogan_builder.dart index 170b64b13e..82348cd586 100644 --- a/core/lib/presentation/views/text/slogan_builder.dart +++ b/core/lib/presentation/views/text/slogan_builder.dart @@ -1,7 +1,5 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/utils/style_utils.dart'; +import 'package:core/presentation/views/image/image_loader_mixin.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; /// A builder which builds a reusable slogan widget. /// This contains the logo and the slogan text. @@ -9,14 +7,13 @@ import 'package:flutter_svg/flutter_svg.dart'; typedef OnTapCallback = void Function(); -class SloganBuilder extends StatelessWidget { +class SloganBuilder extends StatelessWidget with ImageLoaderMixin { final bool arrangedByHorizontal; final String? text; final TextStyle? textStyle; final TextAlign? textAlign; final String? logo; - final Uri? publicLogoUri; final double? sizeLogo; final OnTapCallback? onTapCallback; final EdgeInsetsGeometry? paddingText; @@ -33,7 +30,6 @@ class SloganBuilder extends StatelessWidget { this.textStyle, this.textAlign, this.logo, - this.publicLogoUri, this.sizeLogo, this.onTapCallback, this.padding, @@ -45,82 +41,64 @@ class SloganBuilder extends StatelessWidget { @override Widget build(BuildContext context) { if (!arrangedByHorizontal) { - return 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 EdgeInsetsDirectional.only(top: 16, start: 16, end: 16), - child: Text( - text ?? '', - style: textStyle, - textAlign: textAlign, - overflow: enableOverflow ? CommonTextStyle.defaultTextOverFlow : null, - softWrap: enableOverflow ? CommonTextStyle.defaultSoftWrap : null, - maxLines: enableOverflow ? 1 : null, + return Material( + type: MaterialType.transparency, + child: InkWell( + onTap: onTapCallback, + hoverColor: hoverColor, + borderRadius: BorderRadius.all(Radius.circular(hoverRadius ?? 8)), + child: Padding( + padding: padding ?? EdgeInsets.zero, + child: Column(children: [ + if (logo != null) + buildImage( + imagePath: logo!, + imageSize: sizeLogo, + ), + Padding( + padding: paddingText ?? const EdgeInsetsDirectional.only(top: 16, start: 16, end: 16), + child: Text( + text ?? '', + style: textStyle, + textAlign: textAlign, + overflow: enableOverflow ? TextOverflow.ellipsis : null, + maxLines: enableOverflow ? 1 : null, + ), ), - ), - ]), + ]), + ), ), ); } else { - return 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, + return Material( + type: MaterialType.transparency, + 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: [ + if (logo != null) + buildImage( + imagePath: logo!, + imageSize: sizeLogo, + ), + Padding( + padding: paddingText ?? const EdgeInsets.symmetric(horizontal: 10), + child: Text( + text ?? '', + style: textStyle, + textAlign: textAlign, + overflow: enableOverflow ? TextOverflow.ellipsis : null, + maxLines: enableOverflow ? 1 : null, + ), ), - ), - ]), + ]), + ), ), ); } } - - Widget _logoApp() { - if (logo != null && logo!.endsWith('svg')) { - return SvgPicture.asset( - logo!, - width: sizeLogo ?? 150, - height: sizeLogo ?? 150); - } else if (logo != null) { - return Image( - 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, - errorBuilder: (_, error, stackTrace) { - return Container( - width: sizeLogo ?? 150, - height: sizeLogo ?? 150, - color: AppColor.textFieldHintColor, - ); - }); - } else { - return const SizedBox.shrink(); - } - } } diff --git a/core/lib/utils/string_convert.dart b/core/lib/utils/string_convert.dart index a025322cbb..cf22d9cf19 100644 --- a/core/lib/utils/string_convert.dart +++ b/core/lib/utils/string_convert.dart @@ -20,4 +20,8 @@ class StringConvert { return text; } } + + static String toUrlScheme(String hostScheme) { + return '$hostScheme://'; + } } diff --git a/integration_test/base/base_scenario.dart b/integration_test/base/base_scenario.dart index 1bd8e923f5..7422522c16 100644 --- a/integration_test/base/base_scenario.dart +++ b/integration_test/base/base_scenario.dart @@ -12,4 +12,8 @@ abstract class BaseScenario { await $.waitUntilVisible(patrolFinder); expect(patrolFinder, findsWidgets); } + + Future expectViewInVisible(PatrolFinder patrolFinder) async { + expect(patrolFinder, findsNothing); + } } \ No newline at end of file diff --git a/integration_test/robots/app_grid_robot.dart b/integration_test/robots/app_grid_robot.dart new file mode 100644 index 0000000000..724f87b04c --- /dev/null +++ b/integration_test/robots/app_grid_robot.dart @@ -0,0 +1,12 @@ + +import 'package:flutter_test/flutter_test.dart'; + +import '../base/core_robot.dart'; + +class AppGridRobot extends CoreRobot { + AppGridRobot(super.$); + + Future openAppInAppGridByAppName(String appName) async { + await $(find.text(appName)).tap(); + } +} \ No newline at end of file diff --git a/integration_test/robots/mailbox_menu_robot.dart b/integration_test/robots/mailbox_menu_robot.dart new file mode 100644 index 0000000000..f2a7a1a512 --- /dev/null +++ b/integration_test/robots/mailbox_menu_robot.dart @@ -0,0 +1,10 @@ + +import '../base/core_robot.dart'; + +class MailboxMenuRobot extends CoreRobot { + MailboxMenuRobot(super.$); + + Future openAppGrid() async { + await $(#toggle_app_grid_button).tap(); + } +} \ No newline at end of file diff --git a/integration_test/robots/thread_robot.dart b/integration_test/robots/thread_robot.dart index 8789a7af76..cc2f9c595c 100644 --- a/integration_test/robots/thread_robot.dart +++ b/integration_test/robots/thread_robot.dart @@ -19,4 +19,8 @@ class ThreadRobot extends CoreRobot { Future tapOnSearchField() async { await $(ThreadView).$(SearchBarView).tap(); } + + Future openMailbox() async { + await $(#mobile_mailbox_menu_button).tap(); + } } \ No newline at end of file diff --git a/integration_test/scenarios/app_grid_scenario.dart b/integration_test/scenarios/app_grid_scenario.dart new file mode 100644 index 0000000000..2dd6f65b83 --- /dev/null +++ b/integration_test/scenarios/app_grid_scenario.dart @@ -0,0 +1,88 @@ + +import 'package:core/utils/platform_info.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/app_grid_view.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart'; + +import '../base/base_scenario.dart'; +import '../robots/app_grid_robot.dart'; +import '../robots/mailbox_menu_robot.dart'; +import '../robots/thread_robot.dart'; +import 'login_with_basic_auth_scenario.dart'; + +class AppGridScenario extends BaseScenario { + + final LoginWithBasicAuthScenario loginWithBasicAuthScenario; + + AppGridScenario( + super.$, + { + required this.loginWithBasicAuthScenario + } + ); + + @override + Future execute() async { + final threadRobot = ThreadRobot($); + final mailboxMenuRobot = MailboxMenuRobot($); + final appGridRobot = AppGridRobot($); + + await loginWithBasicAuthScenario.execute(); + + await threadRobot.openMailbox(); + await _expectMailboxViewVisible(); + await _expectAppGridViewVisible(); + + await Future.delayed(const Duration(seconds: 2)); + + await mailboxMenuRobot.openAppGrid(); + await _expectListViewAppGridVisible(); + await _expectAllAppInAppGridDisplayedIsFull(); + + await appGridRobot.openAppInAppGridByAppName('Twake Drive'); + await Future.delayed(const Duration(seconds: 2)); + + if (PlatformInfo.isAndroid) { + await $.native.pressBack(); + + await appGridRobot.openAppInAppGridByAppName('Twake Sync'); + await Future.delayed(const Duration(seconds: 2)); + + await $.native.pressBack(); + + await appGridRobot.openAppInAppGridByAppName('Twake Chat'); + await Future.delayed(const Duration(seconds: 2)); + + await $.native.pressBack(); + + await _expectMailboxViewVisible(); + } else if (PlatformInfo.isIOS) { + await _expectMailboxViewInVisible(); + } + } + + Future _expectMailboxViewVisible() => expectViewVisible($(MailboxView)); + + Future _expectAppGridViewVisible() => expectViewVisible($(AppGridView)); + + Future _expectListViewAppGridVisible() => expectViewVisible($(#list_view_app_grid)); + + Future _expectAllAppInAppGridDisplayedIsFull() async { + int totalApp = PlatformInfo.isIOS ? 2 : 3; + expect(find.byType(AppListDashboardItem), findsNWidgets(totalApp)); + + final listAppItem = $.tester + .widgetList(find.byType(AppListDashboardItem)); + + final listAppNames = listAppItem.map((item) => item.app.appName).toList(); + + if (PlatformInfo.isIOS) { + expect(listAppNames, equals(['Twake Drive', 'Twake Chat'])); + } else { + expect(listAppNames, equals(['Twake Drive', 'Twake Chat', 'Twake Sync'])); + } + } + + Future _expectMailboxViewInVisible() => expectViewInVisible($(MailboxView)); +} \ No newline at end of file diff --git a/integration_test/tests/app_grid/app_grid_test.dart b/integration_test/tests/app_grid/app_grid_test.dart new file mode 100644 index 0000000000..3ea0b7a692 --- /dev/null +++ b/integration_test/tests/app_grid/app_grid_test.dart @@ -0,0 +1,24 @@ +import '../../base/test_base.dart'; +import '../../scenarios/app_grid_scenario.dart'; +import '../../scenarios/login_with_basic_auth_scenario.dart'; + +void main() { + TestBase().runPatrolTest( + description: 'Should display and navigate app grid correctly when clicked', + test: ($) async { + final loginWithBasicAuthScenario = LoginWithBasicAuthScenario($, + username: const String.fromEnvironment('USERNAME'), + hostUrl: const String.fromEnvironment('BASIC_AUTH_URL'), + email: const String.fromEnvironment('BASIC_AUTH_EMAIL'), + password: const String.fromEnvironment('PASSWORD'), + ); + + final appGridScenario = AppGridScenario( + $, + loginWithBasicAuthScenario: loginWithBasicAuthScenario, + ); + + await appGridScenario.execute(); + } + ); +} \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 9fad3f0dfc..0264ea0f9e 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -50,6 +50,7 @@ sms tel linshare.mobile + twake.chat LSRequiresIPhoneOS diff --git a/lib/features/base/mixin/launcher_application_mixin.dart b/lib/features/base/mixin/launcher_application_mixin.dart new file mode 100644 index 0000000000..c3f20a9666 --- /dev/null +++ b/lib/features/base/mixin/launcher_application_mixin.dart @@ -0,0 +1,58 @@ + +import 'package:core/utils/platform_info.dart'; +import 'package:core/utils/string_convert.dart'; +import 'package:external_app_launcher/external_app_launcher.dart'; +import 'package:rich_text_composer/views/commons/logger.dart'; +import 'package:url_launcher/url_launcher.dart' as launcher; + +mixin LauncherApplicationMixin { + + Future launchApplication({ + String? androidPackageId, + String? iosScheme, + String? iosStoreLink, + Uri? uri, + }) async { + try { + if (PlatformInfo.isWeb && uri != null) { + await openWebApplication(uri); + } else if (PlatformInfo.isAndroid && androidPackageId != null) { + await openAndroidApplication(androidPackageId); + } else if (PlatformInfo.isIOS && + (iosScheme != null || iosStoreLink != null)) { + await openIOSApplication( + iosScheme, + iosStoreLink, + ); + } else if (uri != null) { + await openOtherApplication(uri); + } + } catch (e) { + logError('LauncherApplicationMixin::launchApplication:Exception = $e'); + } + } + + Future openAndroidApplication(String androidPackageId) async { + await LaunchApp.openApp(androidPackageName: androidPackageId); + } + + Future openIOSApplication(String? iosScheme, String? iosStoreLink) async { + await LaunchApp.openApp( + iosUrlScheme: iosScheme != null + ? StringConvert.toUrlScheme(iosScheme) + : null, + appStoreLink: iosStoreLink, + ); + } + + Future openWebApplication(Uri uri) async { + await launcher.launchUrl(uri); + } + + Future openOtherApplication(Uri uri) async { + await launcher.launchUrl( + uri, + mode: launcher.LaunchMode.externalApplication, + ); + } +} \ No newline at end of file diff --git a/lib/features/login/data/extensions/service_path_extension.dart b/lib/features/login/data/extensions/service_path_extension.dart index 55d80db042..bc2742c7a7 100644 --- a/lib/features/login/data/extensions/service_path_extension.dart +++ b/lib/features/login/data/extensions/service_path_extension.dart @@ -1,4 +1,5 @@ +import 'package:core/data/constants/constant.dart'; import 'package:core/data/model/query/query_parameter.dart'; import 'package:core/data/network/config/service_path.dart'; @@ -7,24 +8,32 @@ extension ServicePathExtension on ServicePath { return path; } - ServicePath generateOIDCPath(Uri baseUrl) { - return ServicePath(baseUrl.toString() + path); + ServicePath usingBaseUrl(String baseUrl) { + String normalizedBaseUrl = baseUrl.endsWith(Constant.slashCharacter) + ? baseUrl.substring(0, baseUrl.length - 1) + : baseUrl; + + String normalizedPath = path.startsWith(Constant.slashCharacter) + ? path.substring(1) + : path; + + return ServicePath('$normalizedBaseUrl${Constant.slashCharacter}$normalizedPath'); } ServicePath withQueryParameters(List queryParameters) { if (queryParameters.isEmpty) { return this; } - if (path.lastIndexOf('/') == path.length - 1) { + if (path.lastIndexOf(Constant.slashCharacter) == path.length - 1) { final newPath = path.substring(0, path.length - 1); return ServicePath('$newPath?${queryParameters .map((query) => '${query.queryName}=${query.queryValue}') - .join('&')}'); + .join(Constant.andCharacter)}'); } else { return ServicePath('$path?${queryParameters .map((query) => '${query.queryName}=${query.queryValue}') - .join('&')}'); + .join(Constant.andCharacter)}'); } } @@ -33,10 +42,10 @@ extension ServicePathExtension on ServicePath { return this; } - if (path.lastIndexOf('/') == path.length - 1) { + if (path.lastIndexOf(Constant.slashCharacter) == path.length - 1) { return ServicePath('$path$pathParameter'); } else { - return ServicePath('$path/$pathParameter'); + return ServicePath('$path${Constant.slashCharacter}$pathParameter'); } } } \ No newline at end of file diff --git a/lib/features/login/data/network/endpoint.dart b/lib/features/login/data/network/endpoint.dart index 3429b27d44..d69e1386f2 100644 --- a/lib/features/login/data/network/endpoint.dart +++ b/lib/features/login/data/network/endpoint.dart @@ -1,5 +1,6 @@ import 'package:core/data/network/config/service_path.dart'; class Endpoint { - static final ServicePath webFinger = ServicePath('/.well-known/webfinger'); + static final ServicePath webFinger = ServicePath('.well-known/webfinger'); + static final ServicePath linagoraEcosystem = ServicePath('.well-known/linagora-ecosystem'); } diff --git a/lib/features/login/data/network/oidc_http_client.dart b/lib/features/login/data/network/oidc_http_client.dart index 1d14c38421..5a6c32c3e3 100644 --- a/lib/features/login/data/network/oidc_http_client.dart +++ b/lib/features/login/data/network/oidc_http_client.dart @@ -25,7 +25,7 @@ class OIDCHttpClient { try { final result = await _dioClient.get( Endpoint.webFinger - .generateOIDCPath(Uri.parse(oidcRequest.baseUrl)) + .usingBaseUrl(oidcRequest.baseUrl) .withQueryParameters([ StringQueryParameter('resource', oidcRequest.resourceUrl), StringQueryParameter('rel', OIDCRequest.relUrl), diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index ef6fc7cb7a..4692ee88d4 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -1002,14 +1002,6 @@ class MailboxController extends BaseMailboxController _triggerToggleMailboxCategories(); } break; - case MailboxCategories.appGrid: - final currentExpandMode = mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value; - if (currentExpandMode == ExpandMode.COLLAPSE) { - _showAppDashboardAction(); - } else { - mailboxDashBoardController.appGridDashboardController.toggleAppGridDashboard(); - } - break; } } @@ -1030,10 +1022,6 @@ class MailboxController extends BaseMailboxController } } - void _showAppDashboardAction() { - mailboxDashBoardController.showAppDashboardAction(); - } - void handleMailboxAction( BuildContext context, MailboxActions actions, diff --git a/lib/features/mailbox/presentation/mailbox_view.dart b/lib/features/mailbox/presentation/mailbox_view.dart index 6117a92acd..8d58187e40 100644 --- a/lib/features/mailbox/presentation/mailbox_view.dart +++ b/lib/features/mailbox/presentation/mailbox_view.dart @@ -8,6 +8,7 @@ import 'package:tmail_ui_user/features/base/widget/application_version_widget.da import 'package:tmail_ui_user/features/mailbox/presentation/base_mailbox_view.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/app_grid_view.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/bottom_bar_selection_mailbox_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_loading_bar_widget.dart'; @@ -16,7 +17,6 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_informa import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; import 'package:tmail_ui_user/features/quotas/presentation/quotas_view.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/utils/app_config.dart'; class MailboxView extends BaseMailboxView { @@ -238,9 +238,18 @@ class MailboxView extends BaseMailboxView { ); }), Obx(() => MailboxLoadingBarWidget(viewState: controller.viewState.value)), - AppConfig.appGridDashboardAvailable && !PlatformInfo.isMobile - ? buildAppGridDashboard(context, controller.responsiveUtils, controller.imagePaths, controller) - : const SizedBox.shrink(), + Obx(() { + final linagoraApps = controller + .mailboxDashBoardController + .appGridDashboardController + .listLinagoraApp; + + if (linagoraApps.isNotEmpty) { + return AppGridView(linagoraApps: linagoraApps); + } else { + return const SizedBox.shrink(); + } + }), const SizedBox(height: 8), Obx(() { if (controller.defaultMailboxIsNotEmpty) { diff --git a/lib/features/mailbox/presentation/mailbox_view_web.dart b/lib/features/mailbox/presentation/mailbox_view_web.dart index 0be90fdbf6..432a6b1f65 100644 --- a/lib/features/mailbox/presentation/mailbox_view_web.dart +++ b/lib/features/mailbox/presentation/mailbox_view_web.dart @@ -9,13 +9,13 @@ import 'package:tmail_ui_user/features/base/widget/scrollbar_list_view.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/base_mailbox_view.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/app_grid_view.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_loading_bar_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget.dart'; import 'package:tmail_ui_user/features/quotas/presentation/quotas_view.dart'; import 'package:tmail_ui_user/features/quotas/presentation/styles/quotas_view_styles.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/utils/app_config.dart'; class MailboxView extends BaseMailboxView { @@ -154,9 +154,18 @@ class MailboxView extends BaseMailboxView { ), )), Obx(() => MailboxLoadingBarWidget(viewState: controller.viewState.value)), - AppConfig.appGridDashboardAvailable && controller.responsiveUtils.isWebNotDesktop(context) - ? buildAppGridDashboard(context, controller.responsiveUtils, controller.imagePaths, controller) - : const SizedBox.shrink(), + Obx(() { + final linagoraApps = controller + .mailboxDashBoardController + .appGridDashboardController + .listLinagoraApp; + + if (linagoraApps.isNotEmpty && !controller.responsiveUtils.isDesktop(context)) { + return AppGridView(linagoraApps: linagoraApps); + } else { + return const SizedBox.shrink(); + } + }), const SizedBox(height: 8), Obx(() { if (controller.defaultMailboxIsNotEmpty) { diff --git a/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart b/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart index f313f9d465..da099fc735 100644 --- a/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart +++ b/lib/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart @@ -16,7 +16,6 @@ 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/error/capability_validator.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -363,93 +362,4 @@ mixin MailboxWidgetMixin { ]) ); } - - Widget buildAppGridDashboard( - BuildContext context, - ResponsiveUtils responsiveUtils, - ImagePaths imagePaths, - MailboxController controller - ) { - return Column(children: [ - _buildGoToApplicationsCategory( - context, - responsiveUtils, - imagePaths, - MailboxCategories.appGrid, - controller), - AnimatedContainer( - duration: const Duration(milliseconds: 400), - child: Obx(() { - return controller.mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value == ExpandMode.EXPAND - ? _buildAppGridInMailboxView(context, controller) - : const Offstage(); - }) - ), - const Divider(color: AppColor.colorDividerMailbox, height: 1) - ]); - } - - Widget _buildGoToApplicationsCategory( - BuildContext context, - ResponsiveUtils responsiveUtils, - ImagePaths imagePaths, - MailboxCategories categories, - MailboxController controller - ) { - return Padding( - padding: const EdgeInsetsDirectional.only(start: 32, end: 4), - child: Row(children: [ - SvgPicture.asset( - imagePaths.icAppDashboard, - colorFilter: AppColor.primaryColor.asFilter(), - width: 20, - height: 20, - fit: BoxFit.fill), - Expanded(child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text(categories.getTitle(context), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - style: const TextStyle( - fontSize: 16, - color: AppColor.colorTextButton, - fontWeight: FontWeight.w500 - ) - ) - )), - buildIconWeb( - icon: Obx(() => SvgPicture.asset( - controller.mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value == ExpandMode.COLLAPSE - ? DirectionUtils.isDirectionRTLByLanguage(context) ? imagePaths.icBack : imagePaths.icCollapseFolder - : imagePaths.icExpandFolder, - colorFilter: controller.mailboxDashBoardController.appGridDashboardController.appDashboardExpandMode.value == ExpandMode.COLLAPSE - ? AppColor.colorIconUnSubscribedMailbox.asFilter() - : 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 EdgeInsetsDirectional.only(start: 16, end: 16, bottom: 8), - 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/model/mailbox_categories.dart b/lib/features/mailbox/presentation/model/mailbox_categories.dart index 0c05ec080f..e37af365ee 100644 --- a/lib/features/mailbox/presentation/model/mailbox_categories.dart +++ b/lib/features/mailbox/presentation/model/mailbox_categories.dart @@ -7,7 +7,6 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; enum MailboxCategories { exchange, personalFolders, - appGrid, teamMailboxes } @@ -19,8 +18,6 @@ extension MailboxCategoriessExtension on MailboxCategories { return 'exchange'; case MailboxCategories.personalFolders: return 'personalFolders'; - case MailboxCategories.appGrid: - return 'appGrid'; case MailboxCategories.teamMailboxes: return 'teamMailboxes'; } @@ -32,8 +29,6 @@ extension MailboxCategoriessExtension on MailboxCategories { return AppLocalizations.of(context).exchange; case MailboxCategories.personalFolders: return AppLocalizations.of(context).personalFolders; - case MailboxCategories.appGrid: - return AppLocalizations.of(context).appGridTittle; case MailboxCategories.teamMailboxes: return AppLocalizations.of(context).teamMailBoxes; } diff --git a/lib/features/mailbox/presentation/widgets/app_grid_view.dart b/lib/features/mailbox/presentation/widgets/app_grid_view.dart new file mode 100644 index 0000000000..3603e210a1 --- /dev/null +++ b/lib/features/mailbox/presentation/widgets/app_grid_view.dart @@ -0,0 +1,133 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:core/utils/direction_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/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.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'; + +class AppGridView extends StatefulWidget { + + final List linagoraApps; + + const AppGridView({super.key, required this.linagoraApps}); + + @override + State createState() => _AppGridViewState(); +} + +class _AppGridViewState extends State { + final ValueNotifier _isCollapsedNotifier = ValueNotifier(true); + final _imagePaths = Get.find(); + + @override + void dispose() { + _isCollapsedNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column(children: [ + Padding( + padding: const EdgeInsetsDirectional.only( + start: 32, + end: 4, + top: 4, + bottom: 4, + ), + child: Row( + children: [ + SvgPicture.asset( + _imagePaths.icAppDashboard, + colorFilter: AppColor.primaryColor.asFilter(), + width: 20, + height: 20, + fit: BoxFit.fill, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + AppLocalizations.of(context).appGridTittle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontSize: 16, + color: AppColor.colorTextButton, + fontWeight: FontWeight.w500, + ), + ), + ) + ), + ValueListenableBuilder( + valueListenable: _isCollapsedNotifier, + builder: (context, isCollapsed, child) { + return TMailButtonWidget.fromIcon( + key: const Key('toggle_app_grid_button'), + icon: _getCollapseIcon(context, isCollapsed), + iconColor: isCollapsed + ? AppColor.colorIconUnSubscribedMailbox + : AppColor.primaryColor, + iconSize: 32, + padding: const EdgeInsets.all(3), + backgroundColor: Colors.transparent, + tooltipMessage: AppLocalizations.of(context).appGridTittle, + onTapActionCallback: _toggleAppGridDashboard, + ); + }, + ), + ], + ), + ), + AnimatedContainer( + duration: const Duration(milliseconds: 400), + child: ValueListenableBuilder( + valueListenable: _isCollapsedNotifier, + builder: (context, isCollapsed, child) { + if (isCollapsed) { + return const Offstage(); + } else { + return child ?? const Offstage(); + } + }, + child: ListView.builder( + key: const Key('list_view_app_grid'), + shrinkWrap: true, + primary: false, + padding: const EdgeInsetsDirectional.only( + start: 16, + end: 16, + bottom: 8, + ), + itemCount: widget.linagoraApps.length, + itemBuilder: (context, index) { + return AppListDashboardItem( + app: widget.linagoraApps[index], + imagePaths: _imagePaths, + ); + }, + ), + ), + ), + const Divider(color: AppColor.colorDividerMailbox, height: 1) + ]); + } + + String _getCollapseIcon(BuildContext context, bool isCollapsed) { + if (isCollapsed) { + return DirectionUtils.isDirectionRTLByLanguage(context) + ? _imagePaths.icArrowLeft + : _imagePaths.icArrowRight; + } else { + return _imagePaths.icArrowBottom; + } + } + + void _toggleAppGridDashboard() { + _isCollapsedNotifier.value = !_isCollapsedNotifier.value; + } +} diff --git a/lib/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart b/lib/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart new file mode 100644 index 0000000000..2921ff2cf1 --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart @@ -0,0 +1,9 @@ + +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; + +abstract class AppGridDatasource { + Future getLinagoraApplications(String path); + + Future getLinagoraEcosystem(String baseUrl); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/data/datasource_impl/app_grid_datasource_impl.dart b/lib/features/mailbox_dashboard/data/datasource_impl/app_grid_datasource_impl.dart new file mode 100644 index 0000000000..aa63435848 --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource_impl/app_grid_datasource_impl.dart @@ -0,0 +1,26 @@ + +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/network/linagora_ecosystem_api.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; + +class AppGridDatasourceImpl extends AppGridDatasource { + + final LinagoraEcosystemApi _linagoraEcosystemApi; + final ExceptionThrower _exceptionThrower; + + AppGridDatasourceImpl(this._linagoraEcosystemApi, this._exceptionThrower); + + @override + Future getLinagoraApplications(String path) { + throw UnimplementedError(); + } + + @override + Future getLinagoraEcosystem(String baseUrl) { + return Future.sync(() async { + return await _linagoraEcosystemApi.getLinagoraEcosystem(baseUrl); + }).catchError(_exceptionThrower.throwException); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/data/datasource_impl/local_app_grid_datasource_impl.dart b/lib/features/mailbox_dashboard/data/datasource_impl/local_app_grid_datasource_impl.dart new file mode 100644 index 0000000000..34f4c0bb6d --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource_impl/local_app_grid_datasource_impl.dart @@ -0,0 +1,30 @@ + +import 'package:core/utils/config/app_config_loader.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/app_dashboard_configuration_parser.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; + +class LocalAppGridDatasourceImpl extends AppGridDatasource { + + final AppConfigLoader _appConfigLoader; + final ExceptionThrower _exceptionThrower; + + LocalAppGridDatasourceImpl(this._appConfigLoader, this._exceptionThrower); + + @override + Future getLinagoraApplications(String path) { + return Future.sync(() async { + return await _appConfigLoader.load( + path, + AppDashboardConfigurationParser(), + ); + }).catchError(_exceptionThrower.throwException); + } + + @override + Future getLinagoraEcosystem(String baseUrl) { + throw UnimplementedError(); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/data/network/linagora_ecosystem_api.dart b/lib/features/mailbox_dashboard/data/network/linagora_ecosystem_api.dart new file mode 100644 index 0000000000..3d86b73c32 --- /dev/null +++ b/lib/features/mailbox_dashboard/data/network/linagora_ecosystem_api.dart @@ -0,0 +1,29 @@ + +import 'dart:convert'; + +import 'package:core/data/network/dio_client.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/service_path_extension.dart'; +import 'package:tmail_ui_user/features/login/data/network/endpoint.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/linagora_ecosystem_exceptions.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; + +class LinagoraEcosystemApi { + final DioClient _dioClient; + + LinagoraEcosystemApi(this._dioClient); + + Future getLinagoraEcosystem(String baseUrl) async { + final result = await _dioClient.get( + Endpoint.linagoraEcosystem.usingBaseUrl(baseUrl).generateEndpointPath(), + ); + log('LinagoraEcosystemApi::getLinagoraEcosystem: $result'); + if (result is Map) { + return LinagoraEcosystem.deserialize(result); + } else if (result is String) { + return LinagoraEcosystem.deserialize(jsonDecode(result)); + } else { + throw NotFoundLinagoraEcosystem(); + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/data/repository/app_grid_repository_impl.dart b/lib/features/mailbox_dashboard/data/repository/app_grid_repository_impl.dart new file mode 100644 index 0000000000..d851007f79 --- /dev/null +++ b/lib/features/mailbox_dashboard/data/repository/app_grid_repository_impl.dart @@ -0,0 +1,23 @@ + +import 'package:core/data/model/source_type/data_source_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/app_grid_repository.dart'; + +class AppGridRepositoryImpl extends AppGridRepository { + + final Map _mapDataSource; + + AppGridRepositoryImpl(this._mapDataSource); + + @override + Future getLinagoraApplications(String path) { + return _mapDataSource[DataSourceType.local]!.getLinagoraApplications(path); + } + + @override + Future getLinagoraEcosystem(String baseUrl) { + return _mapDataSource[DataSourceType.network]!.getLinagoraEcosystem(baseUrl); + } +} \ 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 dc82543592..c3e24b092d 100644 --- a/lib/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart +++ b/lib/features/mailbox_dashboard/domain/app_dashboard/linagora_app.dart @@ -1,40 +1,42 @@ -import 'package:equatable/equatable.dart'; +import 'package:core/presentation/resources/image_paths.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; part 'linagora_app.g.dart'; @JsonSerializable(explicitToJson: true, includeIfNull: false) -class LinagoraApp with EquatableMixin{ - @JsonKey(name: 'appName') - final String appName; - +class LinagoraApp extends AppLinagoraEcosystem { @JsonKey(name: 'icon') final String? iconName; @JsonKey(name: 'appLink') final Uri appUri; - final String? androidPackageId; - final String? iosUrlScheme; - final String? iosAppStoreLink; final Uri? publicIconUri; - LinagoraApp( - this.appName, - this.appUri, - { - this.iconName, - this.androidPackageId, - this.iosUrlScheme, - this.iosAppStoreLink, - this.publicIconUri - } - ); + LinagoraApp({ + required this.appUri, + this.iconName, + this.publicIconUri, + super.appName, + super.androidPackageId, + super.iosUrlScheme, + super.iosAppStoreLink, + }); factory LinagoraApp.fromJson(Map json) => _$LinagoraAppFromJson(json); + @override Map toJson() => _$LinagoraAppToJson(this); + @override + String? getIconPath(ImagePaths imagePaths) => iconName != null + ? imagePaths.getConfigurationImagePath(iconName!) + : publicIconUri?.toString(); + + @override + Uri? get appRedirectLink => appUri; + @override List get props => [ appName, diff --git a/lib/features/mailbox_dashboard/domain/exceptions/linagora_ecosystem_exceptions.dart b/lib/features/mailbox_dashboard/domain/exceptions/linagora_ecosystem_exceptions.dart new file mode 100644 index 0000000000..edd236cc5c --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/exceptions/linagora_ecosystem_exceptions.dart @@ -0,0 +1,2 @@ + +class NotFoundLinagoraEcosystem implements Exception {} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/api_key_linagora_ecosystem.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/api_key_linagora_ecosystem.dart new file mode 100644 index 0000000000..fb5eb49b00 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/api_key_linagora_ecosystem.dart @@ -0,0 +1,19 @@ +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_key_linagora_ecosystem_converter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; + +class ApiKeyLinagoraEcosystem extends LinagoraEcosystemProperties { + final String value; + + ApiKeyLinagoraEcosystem(this.value); + + static LinagoraEcosystemProperties? deserialize(dynamic json) { + if (json is String) { + return const ApiKeyLinagoraEcosystemConverter().fromJson(json); + } else { + return null; + } + } + + @override + List get props => [value]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/api_url_linagora_ecosystem.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/api_url_linagora_ecosystem.dart new file mode 100644 index 0000000000..9559595707 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/api_url_linagora_ecosystem.dart @@ -0,0 +1,20 @@ +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_url_linagora_ecosystem_converter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/empty_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; + +class ApiUrlLinagoraEcosystem extends LinagoraEcosystemProperties { + final String value; + + ApiUrlLinagoraEcosystem(this.value); + + static LinagoraEcosystemProperties? deserialize(dynamic json) { + if (json is String) { + return const ApiUrlLinagoraEcosystemConverter().fromJson(json); + } else { + return EmptyLinagoraEcosystem(); + } + } + + @override + List get props => [value]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart new file mode 100644 index 0000000000..d01b49abab --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart @@ -0,0 +1,58 @@ +import 'package:core/core.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/empty_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; + +part 'app_linagora_ecosystem.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class AppLinagoraEcosystem extends LinagoraEcosystemProperties { + final String? appName; + final String? logoURL; + final String? androidPackageId; + final String? iosUrlScheme; + final String? iosAppStoreLink; + final Uri? webLink; + + AppLinagoraEcosystem({ + this.appName, + this.logoURL, + this.androidPackageId, + this.iosUrlScheme, + this.iosAppStoreLink, + this.webLink, + }); + + factory AppLinagoraEcosystem.fromJson(Map json) => _$AppLinagoraEcosystemFromJson(json); + + Map toJson() => _$AppLinagoraEcosystemToJson(this); + + static LinagoraEcosystemProperties? deserialize(dynamic json) { + if (json is Map) { + return AppLinagoraEcosystem.fromJson(json); + } else { + return EmptyLinagoraEcosystem(); + } + } + + String? getIconPath(ImagePaths imagePaths) => logoURL; + + Uri? get appRedirectLink => webLink; + + bool get isAppIOSEnabled => iosUrlScheme?.isNotEmpty == true || + iosAppStoreLink?.isNotEmpty == true || + appRedirectLink != null; + + bool get isAppAndroidEnabled => androidPackageId?.isNotEmpty == true || + appRedirectLink != null; + + @override + List get props => [ + appName, + logoURL, + androidPackageId, + iosUrlScheme, + iosAppStoreLink, + webLink, + ]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_key_linagora_ecosystem_converter.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_key_linagora_ecosystem_converter.dart new file mode 100644 index 0000000000..d0c3e35522 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_key_linagora_ecosystem_converter.dart @@ -0,0 +1,12 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/api_key_linagora_ecosystem.dart'; + +class ApiKeyLinagoraEcosystemConverter implements JsonConverter { + const ApiKeyLinagoraEcosystemConverter(); + + @override + ApiKeyLinagoraEcosystem? fromJson(String? json) => json != null ? ApiKeyLinagoraEcosystem(json) : null; + + @override + String? toJson(ApiKeyLinagoraEcosystem? object) => object?.value; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_url_linagora_ecosystem_converter.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_url_linagora_ecosystem_converter.dart new file mode 100644 index 0000000000..d723c0365c --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/api_url_linagora_ecosystem_converter.dart @@ -0,0 +1,12 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/api_url_linagora_ecosystem.dart'; + +class ApiUrlLinagoraEcosystemConverter implements JsonConverter { + const ApiUrlLinagoraEcosystemConverter(); + + @override + ApiUrlLinagoraEcosystem? fromJson(String? json) => json != null ? ApiUrlLinagoraEcosystem(json) : null; + + @override + String? toJson(ApiUrlLinagoraEcosystem? object) => object?.value; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/linagora_ecosystem_converter.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/linagora_ecosystem_converter.dart new file mode 100644 index 0000000000..8513f3b083 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/linagora_ecosystem_converter.dart @@ -0,0 +1,62 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/api_key_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/api_url_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/converters/mobile_apps_linagora_ecosystem_converter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/default_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/empty_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_identifier.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; + +class LinagoraEcosystemConverter { + static final defaultConverter = LinagoraEcosystemConverter(); + + BuiltMap? _mapLinagoraEcosystemConverter; + final mapLinagoraEcosystemConverterBuilder = MapBuilder(); + + LinagoraEcosystemConverter() { + mapLinagoraEcosystemConverterBuilder.addAll( + { + LinagoraEcosystemIdentifier.linShareApiUrl: ApiUrlLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.linToApiUrl: ApiUrlLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.twakeApiUrl: ApiUrlLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.linToApiKey: ApiKeyLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.twakeDrive: AppLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.twakeChat: AppLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.twakeSync: AppLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.linShare: AppLinagoraEcosystem.deserialize, + LinagoraEcosystemIdentifier.mobileApps: MobileAppsLinagoraEcosystemConverter.deserialize, + }); + } + + void build() { + _mapLinagoraEcosystemConverter = mapLinagoraEcosystemConverterBuilder.build(); + } + + MapEntry convert(String key, dynamic value) { + if (_mapLinagoraEcosystemConverter == null) { + build(); + } + + final identifier = LinagoraEcosystemIdentifier(key); + if (_mapLinagoraEcosystemConverter!.containsKey(identifier)) { + try { + return MapEntry(identifier, _mapLinagoraEcosystemConverter![identifier]!.call(value)); + } catch (e) { + return MapEntry(identifier, EmptyLinagoraEcosystem()); + } + } else { + if (value is Map) { + return MapEntry(identifier, AppLinagoraEcosystem.deserialize(value)); + } else { + return MapEntry(identifier, DefaultLinagoraEcosystem(value)); + } + } + } + + static LinagoraEcosystem deserialize(Map? json) { + final apps = json?.map((key, value) => defaultConverter.convert(key, value)); + return LinagoraEcosystem(apps); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/mobile_apps_linagora_ecosystem_converter.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/mobile_apps_linagora_ecosystem_converter.dart new file mode 100644 index 0000000000..98937e6c86 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/converters/mobile_apps_linagora_ecosystem_converter.dart @@ -0,0 +1,15 @@ +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/converters/linagora_ecosystem_converter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/mobile_apps_linagora_ecosystem.dart'; + +class MobileAppsLinagoraEcosystemConverter extends LinagoraEcosystemConverter { + static final defaultConverter = MobileAppsLinagoraEcosystemConverter(); + + static MobileAppsLinagoraEcosystem? deserialize(dynamic json) { + if (json is Map) { + final apps = json.map((key, value) => defaultConverter.convert(key, value)); + return MobileAppsLinagoraEcosystem(apps); + } else { + return null; + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/default_linagora_ecosystem.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/default_linagora_ecosystem.dart new file mode 100644 index 0000000000..0927fefea6 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/default_linagora_ecosystem.dart @@ -0,0 +1,20 @@ + +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; + +part 'default_linagora_ecosystem.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class DefaultLinagoraEcosystem extends LinagoraEcosystemProperties with EquatableMixin { + final dynamic properties; + + DefaultLinagoraEcosystem(this.properties); + + factory DefaultLinagoraEcosystem.fromJson(Map json) => _$DefaultLinagoraEcosystemFromJson(json); + + Map toJson() => _$DefaultLinagoraEcosystemToJson(this); + + @override + List get props => [properties]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/empty_linagora_ecosystem.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/empty_linagora_ecosystem.dart new file mode 100644 index 0000000000..5f515799b9 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/empty_linagora_ecosystem.dart @@ -0,0 +1,18 @@ + +import 'package:json_annotation/json_annotation.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; + +part 'empty_linagora_ecosystem.g.dart'; + +@JsonSerializable() +class EmptyLinagoraEcosystem extends LinagoraEcosystemProperties { + + EmptyLinagoraEcosystem(); + + factory EmptyLinagoraEcosystem.fromJson(Map json) => _$EmptyLinagoraEcosystemFromJson(json); + + Map toJson() => _$EmptyLinagoraEcosystemToJson(this); + + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart new file mode 100644 index 0000000000..6519a1d534 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart @@ -0,0 +1,47 @@ + +import 'package:equatable/equatable.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/converters/linagora_ecosystem_converter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_identifier.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/mobile_apps_linagora_ecosystem.dart'; + +class LinagoraEcosystem with EquatableMixin { + final Map? properties; + + LinagoraEcosystem(this.properties); + + factory LinagoraEcosystem.deserialize(Map? json) { + return LinagoraEcosystemConverter.deserialize(json); + } + + @override + List get props => [properties]; +} + +extension LinagoraEcosystemExtension on LinagoraEcosystem { + List get listAppLinagoraEcosystem { + if (properties == null) return []; + + final listWebAppLinagora = properties + !.values + .whereType() + .toList(); + + final listMobileAppLinagora = (properties![LinagoraEcosystemIdentifier.mobileApps] as MobileAppsLinagoraEcosystem?) + ?.apps + ?.values + .whereType() + .toList() ?? []; + + return listWebAppLinagora + listMobileAppLinagora; + } + + List get listAppLinagoraEcosystemOnIOS { + return listAppLinagoraEcosystem.where((app) => app.isAppIOSEnabled).toList(); + } + + List get listAppLinagoraEcosystemOnAndroid { + return listAppLinagoraEcosystem.where((app) => app.isAppAndroidEnabled).toList(); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_identifier.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_identifier.dart new file mode 100644 index 0000000000..ed931e0c05 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_identifier.dart @@ -0,0 +1,21 @@ + +import 'package:equatable/equatable.dart'; + +class LinagoraEcosystemIdentifier with EquatableMixin { + static final linShareApiUrl = LinagoraEcosystemIdentifier('linShareApiUrl'); + static final linToApiUrl = LinagoraEcosystemIdentifier('linToApiUrl'); + static final twakeApiUrl = LinagoraEcosystemIdentifier('twakeApiUrl'); + static final linToApiKey = LinagoraEcosystemIdentifier('linToApiKey'); + static final mobileApps = LinagoraEcosystemIdentifier('mobileApps'); + static final twakeDrive = LinagoraEcosystemIdentifier('Twake Drive'); + static final twakeChat = LinagoraEcosystemIdentifier('Twake Chat'); + static final twakeSync = LinagoraEcosystemIdentifier('Twake Sync'); + static final linShare = LinagoraEcosystemIdentifier('LinShare'); + + final String value; + + LinagoraEcosystemIdentifier(this.value); + + @override + List get props => [value]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart new file mode 100644 index 0000000000..219d5a9ad1 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart @@ -0,0 +1,4 @@ + +import 'package:equatable/equatable.dart'; + +abstract class LinagoraEcosystemProperties with EquatableMixin {} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/linagora_ecosystem/mobile_apps_linagora_ecosystem.dart b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/mobile_apps_linagora_ecosystem.dart new file mode 100644 index 0000000000..9762341663 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/linagora_ecosystem/mobile_apps_linagora_ecosystem.dart @@ -0,0 +1,11 @@ +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_identifier.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_properties.dart'; + +class MobileAppsLinagoraEcosystem extends LinagoraEcosystemProperties { + final Map? apps; + + MobileAppsLinagoraEcosystem(this.apps); + + @override + List get props => [apps]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/repository/app_grid_repository.dart b/lib/features/mailbox_dashboard/domain/repository/app_grid_repository.dart new file mode 100644 index 0000000000..d9dec79fc6 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/repository/app_grid_repository.dart @@ -0,0 +1,9 @@ + +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; + +abstract class AppGridRepository { + Future getLinagoraApplications(String path); + + Future getLinagoraEcosystem(String baseUrl); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart b/lib/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart index 644fc1ff0f..3e7ba06b7d 100644 --- a/lib/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart +++ b/lib/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart @@ -1,17 +1,17 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; -class LoadingAppDashboardConfiguration extends UIState {} +class LoadingAppDashboardConfiguration extends LoadingState {} class GetAppDashboardConfigurationSuccess extends UIState { - final LinagoraApplications linagoraApplications; + final List listLinagoraApp; - GetAppDashboardConfigurationSuccess(this.linagoraApplications); + GetAppDashboardConfigurationSuccess(this.listLinagoraApp); @override - List get props => [linagoraApplications]; + List get props => [listLinagoraApp]; } class GetAppDashboardConfigurationFailure extends FeatureFailure { diff --git a/lib/features/mailbox_dashboard/domain/state/get_app_grid_linagora_ecosystem_state.dart b/lib/features/mailbox_dashboard/domain/state/get_app_grid_linagora_ecosystem_state.dart new file mode 100644 index 0000000000..2e75f644aa --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/state/get_app_grid_linagora_ecosystem_state.dart @@ -0,0 +1,20 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; + +class LoadingAppGridLinagraEcosystem extends LoadingState {} + +class GetAppGridLinagraEcosystemSuccess extends UIState { + + final List listAppLinagoraEcosystem; + + GetAppGridLinagraEcosystemSuccess(this.listAppLinagoraEcosystem); + + @override + List get props => [listAppLinagoraEcosystem]; +} + +class GetAppGridLinagraEcosystemFailure extends FeatureFailure { + + GetAppGridLinagraEcosystemFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart b/lib/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart index fec5053e43..81e4d0702d 100644 --- a/lib/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart +++ b/lib/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart @@ -1,27 +1,20 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; -import 'package:core/utils/config/app_config_loader.dart'; import 'package:dartz/dartz.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/app_dashboard_configuration_parser.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/app_grid_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart'; class GetAppDashboardConfigurationInteractor { - final AppConfigLoader _appConfigLoader; + final AppGridRepository _appGridRepository; - GetAppDashboardConfigurationInteractor(this._appConfigLoader); + GetAppDashboardConfigurationInteractor(this._appGridRepository); - Stream> execute(String appDashboardConfigurationPath) async* { + Stream> execute(String path) async* { try { yield Right(LoadingAppDashboardConfiguration()); - - final linagoraApps = await _appConfigLoader.load( - appDashboardConfigurationPath, - AppDashboardConfigurationParser() - ); - - yield Right(GetAppDashboardConfigurationSuccess(linagoraApps)); + final linagoraApps = await _appGridRepository.getLinagoraApplications(path); + yield Right(GetAppDashboardConfigurationSuccess(linagoraApps.apps)); } catch (e) { logError('GetAppDashboardConfigurationInteractor::execute(): $e'); yield Left(GetAppDashboardConfigurationFailure(e)); diff --git a/lib/features/mailbox_dashboard/domain/usecases/get_app_grid_linagra_ecosystem_interactor.dart b/lib/features/mailbox_dashboard/domain/usecases/get_app_grid_linagra_ecosystem_interactor.dart new file mode 100644 index 0000000000..1a648d59e9 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/usecases/get_app_grid_linagra_ecosystem_interactor.dart @@ -0,0 +1,30 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/app_grid_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_app_grid_linagora_ecosystem_state.dart'; + +class GetAppGridLinagraEcosystemInteractor { + final AppGridRepository _appGridRepository; + + GetAppGridLinagraEcosystemInteractor(this._appGridRepository); + + Stream> execute(String baseUrl) async* { + try { + yield Right(LoadingAppGridLinagraEcosystem()); + final linagoraEcosystem = await _appGridRepository.getLinagoraEcosystem(baseUrl); + List listMobileAppLinagora = linagoraEcosystem.listAppLinagoraEcosystem; + if (PlatformInfo.isAndroid) { + listMobileAppLinagora = linagoraEcosystem.listAppLinagoraEcosystemOnAndroid; + } else if (PlatformInfo.isIOS) { + listMobileAppLinagora = linagoraEcosystem.listAppLinagoraEcosystemOnIOS; + } + yield Right(GetAppGridLinagraEcosystemSuccess(listMobileAppLinagora)); + } catch (e) { + yield Left(GetAppGridLinagraEcosystemFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index 61f04d8fcc..85d9e24cff 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -50,24 +50,31 @@ import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repositor import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/search_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/spam_report_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/app_grid_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/hive_spam_report_datasource_impl.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/local_app_grid_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/local_spam_report_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/search_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/spam_report_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/local/local_spam_report_manager.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/network/linagora_ecosystem_api.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/network/spam_report_api.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/app_grid_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/search_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/app_grid_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/search_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/spam_report_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_app_grid_linagra_ecosystem_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_report_state_interactor.dart'; @@ -143,7 +150,10 @@ class MailboxDashBoardBindings extends BaseBindings { @override void bindingsController() { - Get.put(AppGridDashboardController(Get.find())); + Get.put(AppGridDashboardController( + Get.find(), + Get.find(), + )); Get.put(DownloadController()); Get.put(SearchController( Get.find(), @@ -199,11 +209,10 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); - Get.lazyPut(() => Get.find()); - Get.lazyPut(() => Get.find()); Get.lazyPut( () => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -270,6 +279,14 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => LocalIdentityCreatorDataSourceImpl( Get.find() )); + Get.lazyPut(() => AppGridDatasourceImpl( + Get.find(), + Get.find(), + )); + Get.lazyPut(() => LocalAppGridDatasourceImpl( + Get.find(), + Get.find(), + )); } @override @@ -321,8 +338,8 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find() )); - Get.lazyPut(() => GetAppDashboardConfigurationInteractor( - Get.find())); + Get.lazyPut(() => GetAppDashboardConfigurationInteractor(Get.find())); + Get.lazyPut(() => GetAppGridLinagraEcosystemInteractor(Get.find())); Get.lazyPut(() => GetEmailByIdInteractor( Get.find(), Get.find())); @@ -373,6 +390,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -420,5 +438,9 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => IdentityCreatorRepositoryImpl( Get.find() )); + Get.lazyPut(() => AppGridRepositoryImpl({ + DataSourceType.network: Get.find(), + DataSourceType.local: Get.find() + },)); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart index bb6c7317c9..efe81cc10d 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart @@ -1,38 +1,48 @@ -import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; import 'package:get/get_rx/get_rx.dart'; -import 'package:model/mailbox/expand_mode.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/domain/app_dashboard/linagora_applications.dart'; +import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_app_grid_linagora_ecosystem_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_app_grid_linagra_ecosystem_interactor.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; -class AppGridDashboardController { - final isAppGridDashboardOverlayOpen = false.obs; - final appDashboardExpandMode = ExpandMode.COLLAPSE.obs; - final linagoraApplications = Rxn(); +class AppGridDashboardController extends BaseController { + final listLinagoraApp = RxList(); final GetAppDashboardConfigurationInteractor _getAppDashboardConfigurationInteractor; + final GetAppGridLinagraEcosystemInteractor _getAppGridLinagraEcosystemInteractor; - AppGridDashboardController(this._getAppDashboardConfigurationInteractor); + AppGridDashboardController( + this._getAppDashboardConfigurationInteractor, + this._getAppGridLinagraEcosystemInteractor, + ); - void toggleAppGridDashboard() { - isAppGridDashboardOverlayOpen.toggle(); - final newExpandMode = appDashboardExpandMode.value == ExpandMode.EXPAND - ? ExpandMode.COLLAPSE - : ExpandMode.EXPAND; - appDashboardExpandMode.value = newExpandMode; + @override + void handleSuccessViewState(Success success) { + if (success is GetAppDashboardConfigurationSuccess) { + syncLinagoraApps(success.listLinagoraApp); + } else if (success is GetAppGridLinagraEcosystemSuccess) { + syncLinagoraApps(success.listAppLinagoraEcosystem); + } else { + super.handleSuccessViewState(success); + } } - Stream> showDashboardAction() { - return _getAppDashboardConfigurationInteractor.execute(AppConfig.appDashboardConfigurationPath); + void loadAppDashboardConfiguration() { + consumeState(_getAppDashboardConfigurationInteractor.execute( + AppConfig.appDashboardConfigurationPath, + )); } - void handleShowAppDashboard(LinagoraApplications linagoraApps) { - log('AppGridDashboardController::handleShowAppDashboard(): $linagoraApps'); - isAppGridDashboardOverlayOpen.value = true; - appDashboardExpandMode.value = ExpandMode.EXPAND; - linagoraApplications.value = linagoraApps; + void loadAppGridLinagraEcosystem(String baseUrl) { + consumeState(_getAppGridLinagraEcosystemInteractor.execute(baseUrl)); + } + + void syncLinagoraApps(List linagoraApps) { + log('AppGridDashboardController::setListLinagoraApp:linagoraApps = $linagoraApps'); + listLinagoraApp.value = linagoraApps; } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 1bf8d4d8c2..21bdf45b1f 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -90,7 +90,6 @@ import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentat import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart'; @@ -319,6 +318,7 @@ class MailboxDashBoardController extends ReloadableController _registerPendingCurrentEmailIdInNotification(); } _handleArguments(); + _loadAppGrid(); super.onReady(); } @@ -387,8 +387,6 @@ class MailboxDashBoardController extends ReloadableController } else if (success is DeleteMultipleEmailsPermanentlyAllSuccess || success is DeleteMultipleEmailsPermanentlyHasSomeEmailFailure) { _deleteMultipleEmailsPermanentlySuccess(success); - } else if (success is GetAppDashboardConfigurationSuccess) { - appGridDashboardController.handleShowAppDashboard(success.linagoraApplications); } else if(success is GetEmailByIdSuccess) { openEmailDetailedView(success.email); } else if (success is StoreSendingEmailSuccess) { @@ -2036,16 +2034,6 @@ class MailboxDashBoardController extends ReloadableController dispatchAction(EmptyTrashAction()); } - void showAppDashboardAction() async { - log('MailboxDashBoardController::showAppDashboardAction(): begin'); - final apps = appGridDashboardController.linagoraApplications.value; - if (apps != null) { - consumeState(Stream.value(Right(GetAppDashboardConfigurationSuccess(apps)))); - return; - } - consumeState(appGridDashboardController.showDashboardAction()); - } - bool isAbleMarkAllAsRead(){ return !searchController.isSearchEmailRunning && selectedMailbox.value != null && selectedMailbox.value!.isDrafts; } @@ -3011,6 +2999,16 @@ class MailboxDashBoardController extends ReloadableController accountId: accountId.value!); } + void _loadAppGrid() { + if (PlatformInfo.isWeb && AppConfig.appGridDashboardAvailable) { + appGridDashboardController.loadAppDashboardConfiguration(); + } else if (PlatformInfo.isMobile) { + appGridDashboardController.loadAppGridLinagraEcosystem( + dynamicUrlInterceptors.jmapUrl ?? '', + ); + } + } + @override void onClose() { if (PlatformInfo.isWeb) { 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 bef9b16a6a..379d265453 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -72,7 +72,6 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { contactSupportCapability: controller.sessionCurrent?.getContactSupportCapability(accountId), searchForm: SearchInputFormWidget(), appGridController: controller.appGridDashboardController, - onShowAppDashboardAction: controller.showAppDashboardAction, onTapApplicationLogoAction: controller.redirectToInboxAction, onTapAvatarAction: (position) => controller.handleClickAvatarAction(context, position), onTapContactSupportAction: (contactSupport) => diff --git a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_icon.dart b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_icon.dart index 885fdfbae5..11e6b1a8a3 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_icon.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_icon.dart @@ -2,60 +2,74 @@ 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/domain/linagora_ecosystem/app_linagora_ecosystem.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 { +class AppGridDashboardIcon extends StatefulWidget { final ImagePaths imagePaths; - final AppGridDashboardController appGridController; - final VoidCallback? onShowAppDashboardAction; + final List linagoraApps; const AppGridDashboardIcon({ super.key, required this.imagePaths, - required this.appGridController, - this.onShowAppDashboardAction, + required this.linagoraApps, }); + @override + State createState() => _AppGridDashboardIconState(); +} + +class _AppGridDashboardIconState extends State { + + final ValueNotifier _isExpandedNotifier = ValueNotifier(false); + + @override + void dispose() { + _isExpandedNotifier.dispose(); + super.dispose(); + } + @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 + return ValueListenableBuilder( + valueListenable: _isExpandedNotifier, + builder: (context, isExpanded, child) { + return PortalTarget( + visible: isExpanded, + portalFollower: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: _toggleAppGridDashboard, ), - 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, + child: PortalTarget( + anchor: Aligned( + follower: AppUtils.isDirectionRTL(context) + ? Alignment.topLeft + : Alignment.topRight, + target: AppUtils.isDirectionRTL(context) + ? Alignment.bottomLeft + : Alignment.bottomRight, + ), + portalFollower: AppDashboardOverlay( + listLinagoraApp: widget.linagoraApps, + imagePaths: widget.imagePaths, + ), + visible: isExpanded, + child: TMailButtonWidget.fromIcon( + icon: widget.imagePaths.icAppDashboard, + backgroundColor: Colors.transparent, + iconSize: 30, + padding: const EdgeInsets.all(6), + onTapActionCallback: _toggleAppGridDashboard, + ), ), - ) - ); - }); + ); + }, + ); + } + + void _toggleAppGridDashboard() { + _isExpandedNotifier.value = !_isExpandedNotifier.value; } } 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 ade8417d54..ef38db5acd 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 @@ -1,64 +1,47 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/image/image_loader_mixin.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/mixin/launcher_application_mixin.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/domain/linagora_ecosystem/app_linagora_ecosystem.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 { - final LinagoraApp app; +class AppGridDashboardItem extends StatelessWidget + with LauncherApplicationMixin, ImageLoaderMixin { - AppGridDashboardItem(this.app, {Key? key}) : super(key: key); + final AppLinagoraEcosystem app; + final ImagePaths imagePaths; - final ImagePaths _imagePaths = Get.find(); + const AppGridDashboardItem({ + Key? key, + required this.app, + required this.imagePaths, + }) : super(key: key); @override Widget build(BuildContext context) { return LinkBrowserWidget( - uri: app.appUri, + uri: app.appRedirectLink!, child: Material( color: Colors.transparent, child: InkWell( - onTap: () => AppUtils.launchLink(app.appUri.toString()), + onTap: () => launchApplication(uri: app.appRedirectLink), borderRadius: const BorderRadius.all(Radius.circular(16)), hoverColor: AppColor.colorBgMailboxSelected, child: Container( width: AppGridDashboardStyle.hoverIconSize, padding: const EdgeInsets.symmetric(vertical: 8), child: Column(children: [ - 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, - ); - } + if (app.getIconPath(imagePaths) != null) + buildImage( + imagePath: app.getIconPath(imagePaths)!, + imageSize: AppGridDashboardStyle.iconSize, ), Padding( padding: const EdgeInsets.only(top: 8), child: Text( - app.appName, + app.appName ?? '', maxLines: 1, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, diff --git a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_overlay.dart b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_overlay.dart index 065060d482..9b04070787 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_overlay.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_overlay.dart @@ -1,12 +1,18 @@ +import 'package:core/presentation/resources/image_paths.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/domain/linagora_ecosystem/app_linagora_ecosystem.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 { - final LinagoraApplications _linagoraApplications; + final List listLinagoraApp; + final ImagePaths imagePaths; - const AppDashboardOverlay(this._linagoraApplications, {Key? key}) : super(key: key); + const AppDashboardOverlay({ + Key? key, + required this.listLinagoraApp, + required this.imagePaths, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -20,17 +26,17 @@ class AppDashboardOverlay extends StatelessWidget { boxShadow: AppGridDashboardStyle.cardShadow ), padding: AppGridDashboardStyle.padding, - child: Wrap(children: _linagoraApplications.apps - .map((app) => AppGridDashboardItem(app)) + child: Wrap(children: listLinagoraApp + .map((app) => AppGridDashboardItem(app: app, imagePaths: imagePaths)) .toList()), ), ); } double get _widthAppGrid { - if (_linagoraApplications.apps.length >= 3) { + if (listLinagoraApp.length >= 3) { return AppGridDashboardStyle.hoverIconSize * 3 + AppGridDashboardStyle.padding.horizontal; - } else if (_linagoraApplications.apps.length == 2) { + } else if (listLinagoraApp.length == 2) { return AppGridDashboardStyle.hoverIconSize * 2 + AppGridDashboardStyle.padding.horizontal; } else { return AppGridDashboardStyle.hoverIconSize + AppGridDashboardStyle.padding.horizontal; 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 bb51f2086a..95ac7d4397 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,60 +1,43 @@ -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/platform_info.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; +import 'package:tmail_ui_user/features/base/mixin/launcher_application_mixin.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; -class AppListDashboardItem extends StatelessWidget { +class AppListDashboardItem extends StatelessWidget with LauncherApplicationMixin { - final LinagoraApp app; + final AppLinagoraEcosystem app; + final ImagePaths imagePaths; - const AppListDashboardItem(this.app, {Key? key}) : super(key: key); + const AppListDashboardItem({ + super.key, + required this.app, + required this.imagePaths, + }); @override Widget build(BuildContext context) { - final imagePaths = Get.find(); return SloganBuilder( sizeLogo: 32, paddingText: const EdgeInsetsDirectional.only(start: 12), text: app.appName, textAlign: TextAlign.center, textStyle: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500, color: AppColor.colorNameEmail), - logo: app.iconName?.isNotEmpty == true - ? imagePaths.getConfigurationImagePath(app.iconName!) - : null, - publicLogoUri: app.publicIconUri, - onTapCallback: () => _openApp(context, app), + logo: app.getIconPath(imagePaths), + onTapCallback: _handleOpenApp, padding: const EdgeInsetsDirectional.symmetric(vertical: 12, horizontal: 20), hoverColor: AppColor.colorBgMailboxSelected ); } - void _openApp(BuildContext context, LinagoraApp app) async { - if (PlatformInfo.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 { - if (await launcher.canLaunchUrl(app.appUri)) { - await launcher.launchUrl( - app.appUri, - mode: launcher.LaunchMode.externalApplication - ); - } - } + Future _handleOpenApp() async { + await launchApplication( + androidPackageId: app.androidPackageId, + iosScheme: app.iosUrlScheme, + iosStoreLink: app.iosAppStoreLink, + uri: app.appRedirectLink, + ); } } diff --git a/lib/features/mailbox_dashboard/presentation/widgets/navigation_bar/navigation_bar_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/navigation_bar/navigation_bar_widget.dart index 1061d3e8c0..feb59f75f8 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/navigation_bar/navigation_bar_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/navigation_bar/navigation_bar_widget.dart @@ -4,6 +4,7 @@ import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/image/avatar_builder.dart'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:model/support/contact_support_capability.dart'; import 'package:tmail_ui_user/features/base/mixin/contact_support_mixin.dart'; import 'package:tmail_ui_user/features/base/widget/application_logo_with_text_widget.dart'; @@ -11,7 +12,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/navigation_bar_style.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_grid_dashboard_icon.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/utils/app_config.dart'; class NavigationBarWidget extends StatelessWidget { @@ -21,7 +21,6 @@ class NavigationBarWidget extends StatelessWidget { final Widget? searchForm; final AppGridDashboardController? appGridController; final VoidCallback? onTapApplicationLogoAction; - final VoidCallback? onShowAppDashboardAction; final OnTapAvatarActionWithPositionClick? onTapAvatarAction; final OnTapContactSupportAction? onTapContactSupportAction; @@ -32,7 +31,6 @@ class NavigationBarWidget extends StatelessWidget { this.contactSupportCapability, this.searchForm, this.appGridController, - this.onShowAppDashboardAction, this.onTapApplicationLogoAction, this.onTapAvatarAction, this.onTapContactSupportAction, @@ -74,15 +72,17 @@ class NavigationBarWidget extends StatelessWidget { tooltipMessage: AppLocalizations.of(context).getHelpOrReportABug, onTapActionCallback: () => onTapContactSupportAction?.call(contactSupportCapability!), ), - if (AppConfig.appGridDashboardAvailable && appGridController != null) - Padding( - padding: const EdgeInsetsDirectional.only(end: 16), - child: AppGridDashboardIcon( - imagePaths: imagePaths, - appGridController: appGridController!, - onShowAppDashboardAction: onShowAppDashboardAction, - ), - ), + if (appGridController != null) + Obx(() { + if (appGridController!.listLinagoraApp.isNotEmpty) { + return AppGridDashboardIcon( + imagePaths: imagePaths, + linagoraApps: appGridController!.listLinagoraApp, + ); + } + return const SizedBox.shrink(); + }), + const SizedBox(width: 16), (AvatarBuilder() ..text(avatarUserName) ..backgroundColor(Colors.white) @@ -114,15 +114,17 @@ class NavigationBarWidget extends StatelessWidget { tooltipMessage: AppLocalizations.of(context).getHelpOrReportABug, onTapActionCallback: () => onTapContactSupportAction?.call(contactSupportCapability!), ), - if (AppConfig.appGridDashboardAvailable && appGridController != null) - Padding( - padding: const EdgeInsetsDirectional.only(end: 16), - child: AppGridDashboardIcon( - imagePaths: imagePaths, - appGridController: appGridController!, - onShowAppDashboardAction: onShowAppDashboardAction, - ), - ), + if (appGridController != null) + Obx(() { + if (appGridController!.listLinagoraApp.isNotEmpty) { + return AppGridDashboardIcon( + imagePaths: imagePaths, + linagoraApps: appGridController!.listLinagoraApp, + ); + } + return const SizedBox.shrink(); + }), + const SizedBox(width: 16), (AvatarBuilder() ..text(avatarUserName) ..backgroundColor(Colors.white) diff --git a/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart index ceb7b0669d..5bd47b5d42 100644 --- a/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart +++ b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_controller.dart @@ -152,8 +152,6 @@ class MailboxVisibilityController extends BaseMailboxController { mailboxCategoriesExpandMode.value.teamMailboxes = newExpandMode; mailboxCategoriesExpandMode.refresh(); break; - case MailboxCategories.appGrid: - break; } } diff --git a/lib/main/bindings/network/network_bindings.dart b/lib/main/bindings/network/network_bindings.dart index ce115afe95..dde247ecb6 100644 --- a/lib/main/bindings/network/network_bindings.dart +++ b/lib/main/bindings/network/network_bindings.dart @@ -26,6 +26,7 @@ import 'package:tmail_ui_user/features/login/data/utils/library_platform/app_aut import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/network/linagora_ecosystem_api.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/network/spam_report_api.dart'; import 'package:tmail_ui_user/features/manage_account/data/network/forwarding_api.dart'; import 'package:tmail_ui_user/features/manage_account/data/network/identity_api.dart'; @@ -126,6 +127,7 @@ class NetworkBindings extends Bindings { Get.put(SpamReportApi(Get.find())); Get.put(ServerSettingsAPI(Get.find())); Get.put(WebSocketApi(Get.find())); + Get.put(LinagoraEcosystemApi(Get.find())); } void _bindingConnection() { diff --git a/pubspec.lock b/pubspec.lock index 676dc6efa3..3e1cd0d60f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -524,11 +524,12 @@ packages: external_app_launcher: dependency: "direct main" description: - name: external_app_launcher - sha256: fb55cddd706c62ede11056750d5e018ef379820e09739e967873211dd537d833 - url: "https://pub.dev" - source: hosted - version: "3.1.0" + path: "." + ref: master + resolved-ref: cb31d10b5a7261f774b6e312d82c5cf1899979b6 + url: "https://github.com/putnokiabel/external_app_launcher.git" + source: git + version: "4.0.0" external_path: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b6bee1382d..c9a4605076 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -109,6 +109,13 @@ dependencies: url: https://github.com/linagora/linagora-design-flutter.git ref: master + # TODO: Fix bug where apps cannot be launched on iOS 18. We will change it when the PR in upstream repository will be merged + # https://github.com/GeekyAnts/external_app_launcher/pull/42 + external_app_launcher: + git: + url: https://github.com/putnokiabel/external_app_launcher.git + ref: master + ### Dependencies from pub.dev ### cupertino_icons: 1.0.6 @@ -208,8 +215,6 @@ dependencies: intl: 0.19.0 - external_app_launcher: 3.1.0 - workmanager: 0.5.1 flutter_typeahead: 5.0.2 diff --git a/test/features/linagora_ecosystem/deserialize_linagora_ecosystem_test.dart b/test/features/linagora_ecosystem/deserialize_linagora_ecosystem_test.dart new file mode 100644 index 0000000000..f0d285e965 --- /dev/null +++ b/test/features/linagora_ecosystem/deserialize_linagora_ecosystem_test.dart @@ -0,0 +1,195 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/api_key_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/api_url_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/app_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/default_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/empty_linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/linagora_ecosystem_identifier.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/linagora_ecosystem/mobile_apps_linagora_ecosystem.dart'; + +void main() { + group('Deserialize LinagoraEcosystem test', () { + test('Should parse correctly when all properties are present', () { + const linagoraEcosystemString = ''' + { + "linShareApiUrl": "https://example.com/api", + "linToApiUrl": "https://example.com/api", + "linToApiKey": "apiKey", + "twakeApiUrl": "https://example.com/api", + "Twake Drive": { + "appName": "Twake Drive", + "logoURL": "https://xyz", + "webLink": "https://abc" + }, + "mobileApps": { + "Twake Chat": { + "appName": "Twake Chat", + "logoURL": "https://xyz", + "androidPackageId": "com.example.android", + "iosUrlScheme": "app.scheme", + "iosAppStoreLink": "itms-apps://itunes.apple.com/app" + }, + "Twake Sync": { + "appName": "Twake Sync", + "logoURL": "https://xyz", + "androidPackageId": "com.example.android" + }, + "LinShare": { + "appName": "LinShare", + "logoURL": "https://xyz", + "androidPackageId": "com.example.android", + "iosUrlScheme": "app.scheme", + "iosAppStoreLink": "itms-apps://itunes.apple.com/app" + } + } + } + '''; + + final expectedLinagoraEcosystem = LinagoraEcosystem({ + LinagoraEcosystemIdentifier.linShareApiUrl: ApiUrlLinagoraEcosystem('https://example.com/api'), + LinagoraEcosystemIdentifier.linToApiUrl: ApiUrlLinagoraEcosystem('https://example.com/api'), + LinagoraEcosystemIdentifier.linToApiKey: ApiKeyLinagoraEcosystem('apiKey'), + LinagoraEcosystemIdentifier.twakeApiUrl: ApiUrlLinagoraEcosystem('https://example.com/api'), + LinagoraEcosystemIdentifier.twakeDrive: AppLinagoraEcosystem( + appName: 'Twake Drive', + logoURL: 'https://xyz', + webLink: Uri.parse('https://abc'), + ), + LinagoraEcosystemIdentifier.mobileApps: MobileAppsLinagoraEcosystem({ + LinagoraEcosystemIdentifier.twakeChat: AppLinagoraEcosystem( + appName: 'Twake Chat', + logoURL: 'https://xyz', + androidPackageId: 'com.example.android', + iosUrlScheme: 'app.scheme', + iosAppStoreLink: 'itms-apps://itunes.apple.com/app', + ), + LinagoraEcosystemIdentifier.twakeSync: AppLinagoraEcosystem( + appName: 'Twake Sync', + logoURL: 'https://xyz', + androidPackageId: 'com.example.android', + ), + LinagoraEcosystemIdentifier.linShare: AppLinagoraEcosystem( + appName: 'LinShare', + logoURL: 'https://xyz', + androidPackageId: 'com.example.android', + iosUrlScheme: 'app.scheme', + iosAppStoreLink: 'itms-apps://itunes.apple.com/app', + ), + }), + }); + + final parsedLinagoraEcosystem = LinagoraEcosystem.deserialize(json.decode(linagoraEcosystemString)); + + expect(parsedLinagoraEcosystem, equals(expectedLinagoraEcosystem)); + }); + + test('Should parse correctly when some properties are null', () { + const linagoraEcosystemString = ''' + { + "linShareApiUrl": "https://example.com/api", + "linToApiUrl": null, + "linToApiKey": "apiKey", + "twakeApiUrl": "https://example.com/api", + "Twake Drive": null, + "mobileApps": { + "Twake Chat": { + "appName": "Twake Chat", + "logoURL": "https://xyz", + "androidPackageId": "com.example.android", + "iosUrlScheme": "app.scheme", + "iosAppStoreLink": "itms-apps://itunes.apple.com/app" + }, + "Twake Sync": null, + "LinShare": { + "appName": "LinShare", + "logoURL": "https://xyz", + "androidPackageId": "com.example.android", + "iosUrlScheme": "app.scheme", + "iosAppStoreLink": "itms-apps://itunes.apple.com/app" + } + } + } + '''; + + final expectedLinagoraEcosystem = LinagoraEcosystem({ + LinagoraEcosystemIdentifier.linShareApiUrl: ApiUrlLinagoraEcosystem('https://example.com/api'), + LinagoraEcosystemIdentifier.linToApiUrl: EmptyLinagoraEcosystem(), + LinagoraEcosystemIdentifier.linToApiKey: ApiKeyLinagoraEcosystem('apiKey'), + LinagoraEcosystemIdentifier.twakeApiUrl: ApiUrlLinagoraEcosystem('https://example.com/api'), + LinagoraEcosystemIdentifier.twakeDrive: EmptyLinagoraEcosystem(), + LinagoraEcosystemIdentifier.mobileApps: MobileAppsLinagoraEcosystem({ + LinagoraEcosystemIdentifier.twakeChat: AppLinagoraEcosystem( + appName: 'Twake Chat', + logoURL: 'https://xyz', + androidPackageId: 'com.example.android', + iosUrlScheme: 'app.scheme', + iosAppStoreLink: 'itms-apps://itunes.apple.com/app', + ), + LinagoraEcosystemIdentifier.twakeSync: EmptyLinagoraEcosystem(), + LinagoraEcosystemIdentifier.linShare: AppLinagoraEcosystem( + appName: 'LinShare', + logoURL: 'https://xyz', + androidPackageId: 'com.example.android', + iosUrlScheme: 'app.scheme', + iosAppStoreLink: 'itms-apps://itunes.apple.com/app', + ), + }), + }); + + final parsedLinagoraEcosystem = LinagoraEcosystem.deserialize(json.decode(linagoraEcosystemString)); + + expect(parsedLinagoraEcosystem, equals(expectedLinagoraEcosystem)); + }); + + test('Should parse correctly when some properties are not default', () { + const linagoraEcosystemString = ''' + { + "abc": "https://example.com/api", + "custom": "apiKey", + "twakeApiUrl": "https://example.com/api", + "mobileApps": { + "Twake Chat": { + "appName": "Twake Chat", + "logoURL": "https://xyz", + "androidPackageId": "com.example.android", + "iosUrlScheme": "app.scheme", + "iosAppStoreLink": "itms-apps://itunes.apple.com/app" + }, + "xyz": { + "appName": "LinShare", + "logoURL": "https://xyz" + }, + "dab": "test" + } + } + '''; + + final expectedLinagoraEcosystem = LinagoraEcosystem({ + LinagoraEcosystemIdentifier('abc'): DefaultLinagoraEcosystem('https://example.com/api'), + LinagoraEcosystemIdentifier('custom'): DefaultLinagoraEcosystem('apiKey'), + LinagoraEcosystemIdentifier.twakeApiUrl: ApiUrlLinagoraEcosystem('https://example.com/api'), + LinagoraEcosystemIdentifier.mobileApps: MobileAppsLinagoraEcosystem({ + LinagoraEcosystemIdentifier.twakeChat: AppLinagoraEcosystem( + appName: 'Twake Chat', + logoURL: 'https://xyz', + androidPackageId: 'com.example.android', + iosUrlScheme: 'app.scheme', + iosAppStoreLink: 'itms-apps://itunes.apple.com/app', + ), + LinagoraEcosystemIdentifier('xyz'): AppLinagoraEcosystem( + appName: 'LinShare', + logoURL: 'https://xyz' + ), + LinagoraEcosystemIdentifier('dab'): DefaultLinagoraEcosystem('test'), + }), + }); + + final parsedLinagoraEcosystem = LinagoraEcosystem.deserialize(json.decode(linagoraEcosystemString)); + + expect(parsedLinagoraEcosystem, equals(expectedLinagoraEcosystem)); + }); + }); +} diff --git a/test/features/login/data/extensions/service_path_extension_test.dart b/test/features/login/data/extensions/service_path_extension_test.dart new file mode 100644 index 0000000000..5c7a25f8da --- /dev/null +++ b/test/features/login/data/extensions/service_path_extension_test.dart @@ -0,0 +1,56 @@ + +import 'package:core/data/network/config/service_path.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/service_path_extension.dart'; + +void main() { + group('ServicePathExtension::test', () { + test('Should correctly combines baseUrl and path without trailing or leading slashes', () { + final servicePath = ServicePath('/api/v1/resource'); + final result = servicePath.usingBaseUrl('https://example.com'); + expect(result.path, 'https://example.com/api/v1/resource'); + }); + + test('Should handles baseUrl with trailing slash', () { + final servicePath = ServicePath('/api/v1/resource'); + final result = servicePath.usingBaseUrl('https://example.com/'); + expect(result.path, 'https://example.com/api/v1/resource'); + }); + + test('Should handles path without leading slash', () { + final servicePath = ServicePath('api/v1/resource'); + final result = servicePath.usingBaseUrl('https://example.com'); + expect(result.path, 'https://example.com/api/v1/resource'); + }); + + test('Should handles both baseUrl with trailing slash and path without leading slash', () { + final servicePath = ServicePath('api/v1/resource'); + final result = servicePath.usingBaseUrl('https://example.com/'); + expect(result.path, 'https://example.com/api/v1/resource'); + }); + + test('Should handles both baseUrl without trailing slash and path with leading slash', () { + final servicePath = ServicePath('/api/v1/resource'); + final result = servicePath.usingBaseUrl('https://example.com'); + expect(result.path, 'https://example.com/api/v1/resource'); + }); + + test('Should handles empty path', () { + final servicePath = ServicePath(''); + final result = servicePath.usingBaseUrl('https://example.com'); + expect(result.path, 'https://example.com/'); + }); + + test('Should handles empty baseUrl', () { + final servicePath = ServicePath('/api/v1/resource'); + final result = servicePath.usingBaseUrl(''); + expect(result.path, '/api/v1/resource'); + }); + + test('Should handles both baseUrl and path being empty', () { + final servicePath = ServicePath(''); + final result = servicePath.usingBaseUrl(''); + expect(result.path, '/'); + }); + }); +} diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index b890e6126c..0fdef05aae 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -135,7 +135,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), - MockSpec(), + MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), diff --git a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart index a090c81866..3a69bf2922 100644 --- a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart +++ b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart @@ -137,7 +137,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), - MockSpec(), + MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(),