From 75495e7d1fcaba1c6b2327d2100d140b45411fad Mon Sep 17 00:00:00 2001 From: ManhNTX Date: Wed, 9 Nov 2022 16:49:01 +0700 Subject: [PATCH] TF-1014: [Dev] Show quota at the bottom of mailbox --- assets/images/ic_quotas.svg | 3 + assets/images/ic_quotas_warning.svg | 4 + .../extensions/color_extension.dart | 3 + .../presentation/resources/image_paths.dart | 2 + .../email/presentation/email_view.dart | 2 + .../mailbox/presentation/mailbox_view.dart | 4 +- .../presentation/mailbox_view_web.dart | 2 + .../bindings/mailbox_dashboard_bindings.dart | 2 + .../mailbox_dashboard_view_web.dart | 2 + .../manage_account_dashboard_view.dart | 2 + .../data/datasource/quotas_data_source.dart | 6 ++ .../quotas_data_source_impl.dart | 21 +++++ .../quotas/data/network/quotas_api.dart | 39 +++++++++ .../repository/quotas_repository_impl.dart | 15 ++++ .../model/exceptions/quotas_exception.dart | 3 + .../quotas/domain/model/quotas_response.dart | 30 +++++++ .../domain/repository/quotas_repository.dart | 6 ++ .../quotas/domain/state/get_quotas_state.dart | 25 ++++++ .../use_case/get_quotas_interactor.dart | 23 ++++++ .../quotas/presentation/quotas_bindings.dart | 43 ++++++++++ .../presentation/quotas_controller.dart | 82 +++++++++++++++++++ .../widget/quotas_footer_widget.dart | 62 ++++++++++++++ .../widget/quotas_warning_banner_widget.dart | 57 +++++++++++++ .../thread/presentation/thread_view.dart | 2 + lib/l10n/intl_messages.arb | 32 ++++++++ .../bindings/network/network_bindings.dart | 2 + lib/main/localizations/app_localizations.dart | 30 +++++++ pubspec.lock | 10 +-- 28 files changed, 508 insertions(+), 6 deletions(-) create mode 100644 assets/images/ic_quotas.svg create mode 100644 assets/images/ic_quotas_warning.svg create mode 100644 lib/features/quotas/data/datasource/quotas_data_source.dart create mode 100644 lib/features/quotas/data/datasource_impl/quotas_data_source_impl.dart create mode 100644 lib/features/quotas/data/network/quotas_api.dart create mode 100644 lib/features/quotas/data/repository/quotas_repository_impl.dart create mode 100644 lib/features/quotas/domain/model/exceptions/quotas_exception.dart create mode 100644 lib/features/quotas/domain/model/quotas_response.dart create mode 100644 lib/features/quotas/domain/repository/quotas_repository.dart create mode 100644 lib/features/quotas/domain/state/get_quotas_state.dart create mode 100644 lib/features/quotas/domain/use_case/get_quotas_interactor.dart create mode 100644 lib/features/quotas/presentation/quotas_bindings.dart create mode 100644 lib/features/quotas/presentation/quotas_controller.dart create mode 100644 lib/features/quotas/presentation/widget/quotas_footer_widget.dart create mode 100644 lib/features/quotas/presentation/widget/quotas_warning_banner_widget.dart diff --git a/assets/images/ic_quotas.svg b/assets/images/ic_quotas.svg new file mode 100644 index 000000000..a8f961589 --- /dev/null +++ b/assets/images/ic_quotas.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/ic_quotas_warning.svg b/assets/images/ic_quotas_warning.svg new file mode 100644 index 000000000..6333c56e7 --- /dev/null +++ b/assets/images/ic_quotas_warning.svg @@ -0,0 +1,4 @@ + + + + diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 9c3c51c01..c405daca6 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -163,6 +163,9 @@ extension AppColor on Color { static const colorBackgroundContactTagItem = Color(0xFFDCE0E5); static const colorDeleteContactIcon = Color(0xFFAEB7C2); static const colorItemRecipientSelected = Color(0xFFDFEEFF); + static const colorBackgroundQuotasWarning = Color(0xFFFFC107); + static const colorTitleQuotasWarning = Color(0xFFF05C44); + static const colorProgressQuotasWarning = Color(0xFFFFA000); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index b1b7c91b4..2768f52b3 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -172,6 +172,8 @@ class ImagePaths { String get icCancelSelection => _getImagePath('ic_cancel_selection.svg'); String get icSelectedRecipient => _getImagePath('ic_selected_recipient.svg'); String get icDeleteRecipient => _getImagePath('ic_delete_recipient.svg'); + String get icQuotas => _getImagePath('ic_quotas.svg'); + String get icQuotasWarning => _getImagePath('ic_quotas_warning.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index e82409d54..1f1232504 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -15,6 +15,7 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mai import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_warning_banner_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class EmailView extends GetWidget { @@ -88,6 +89,7 @@ class EmailView extends GetWidget { Widget _buildEmailView(BuildContext context, PresentationEmail email) { return Column(children: [ _buildAppBar(context, email), + const QuotasWarningBannerWidget(), _buildVacationNotificationMessage(context), const Divider(color: AppColor.colorDividerHorizontal, height: 1), Expanded( diff --git a/lib/features/mailbox/presentation/mailbox_view.dart b/lib/features/mailbox/presentation/mailbox_view.dart index a3a52c0e2..d2c55f4ce 100644 --- a/lib/features/mailbox/presentation/mailbox_view.dart +++ b/lib/features/mailbox/presentation/mailbox_view.dart @@ -13,6 +13,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/bottom_bar_s import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_search_tile_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget_builder.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_footer_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -84,7 +85,7 @@ class MailboxView extends GetWidget { } else { return const SizedBox.shrink(); } - }) + }), ]), ) ) @@ -228,6 +229,7 @@ class MailboxView extends GetWidget { Obx(() => controller.folderMailboxTree.value.root.childrenItems?.isNotEmpty ?? false ? _buildMailboxCategory(context, MailboxCategories.folders, controller.folderMailboxTree.value.root) : const SizedBox.shrink()), + const QuotasFooterWidget(), ]) ); } diff --git a/lib/features/mailbox/presentation/mailbox_view_web.dart b/lib/features/mailbox/presentation/mailbox_view_web.dart index 19989bea3..2afad7c4c 100644 --- a/lib/features/mailbox/presentation/mailbox_view_web.dart +++ b/lib/features/mailbox/presentation/mailbox_view_web.dart @@ -17,6 +17,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_fold import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_search_tile_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget_builder.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_dashboard/app_list_dashboard_item.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_footer_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; @@ -72,6 +73,7 @@ class MailboxView extends GetWidget with AppLoaderMixin, Popu })), ]), )), + const QuotasFooterWidget(), ]), ) ); 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 c3ec2e288..63d449966 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -71,6 +71,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/quotas_bindings.dart'; import 'package:tmail_ui_user/features/search/domain/usecases/refresh_changes_search_email_interactor.dart'; import 'package:tmail_ui_user/features/search/presentation/search_email_bindings.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; @@ -100,6 +101,7 @@ class MailboxDashBoardBindings extends BaseBindings { ThreadBindings().dependencies(); EmailBindings().dependencies(); SearchEmailBindings().dependencies(); + QuotasBindings().dependencies(); } @override 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 4af66a4cb..5e9731ef0 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -22,6 +22,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/se import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_warning_banner_widget.dart'; import 'package:tmail_ui_user/features/search/presentation/search_email_view.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart'; @@ -111,6 +112,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { SizedBox(child: MailboxView(), width: responsiveUtils.defaultSizeMenu), Expanded(child: Column(children: [ _buildEmptyTrashButton(context), + const QuotasWarningBannerWidget(), _buildVacationNotificationMessage(context), _buildListButtonQuickSearchFilter(context), _buildMarkAsMailboxReadLoading(context), diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart index 4901426d4..b6146b55e 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart @@ -20,6 +20,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_view.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_warning_banner_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; @@ -85,6 +86,7 @@ class ManageAccountDashBoardView extends GetWidget getQuotas(AccountId accountId); +} diff --git a/lib/features/quotas/data/datasource_impl/quotas_data_source_impl.dart b/lib/features/quotas/data/datasource_impl/quotas_data_source_impl.dart new file mode 100644 index 000000000..438709ce2 --- /dev/null +++ b/lib/features/quotas/data/datasource_impl/quotas_data_source_impl.dart @@ -0,0 +1,21 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:tmail_ui_user/features/quotas/data/datasource/quotas_data_source.dart'; +import 'package:tmail_ui_user/features/quotas/data/network/quotas_api.dart'; +import 'package:tmail_ui_user/features/quotas/domain/model/quotas_response.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; + +class QuotasDataSourceImpl extends QuotasDataSource{ + final QuotasAPI _quotasAPI; + final ExceptionThrower _exceptionThrower; + + QuotasDataSourceImpl(this._quotasAPI, this._exceptionThrower); + + @override + Future getQuotas(AccountId accountId) { + return Future.sync(() async { + return await _quotasAPI.getQuotas(accountId); + }).catchError((error) { + _exceptionThrower.throwException(error); + }); + } +} \ No newline at end of file diff --git a/lib/features/quotas/data/network/quotas_api.dart b/lib/features/quotas/data/network/quotas_api.dart new file mode 100644 index 000000000..d3b0262c3 --- /dev/null +++ b/lib/features/quotas/data/network/quotas_api.dart @@ -0,0 +1,39 @@ +import 'package:jmap_dart_client/http/http_client.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/jmap_request.dart'; +import 'package:jmap_dart_client/jmap/quotas/get/get_quota_method.dart'; +import 'package:jmap_dart_client/jmap/quotas/get/get_quota_response.dart'; +import 'package:tmail_ui_user/features/quotas/domain/model/exceptions/quotas_exception.dart'; +import 'package:tmail_ui_user/features/quotas/domain/model/quotas_response.dart'; + +class QuotasAPI { + final HttpClient _httpClient; + + QuotasAPI(this._httpClient); + + Future getQuotas(AccountId accountId) async { + final requestBuilder = + JmapRequestBuilder(_httpClient, ProcessingInvocation()); + final getQuotaMethod = GetQuotaMethod(accountId); + final getQuotaInvocation = requestBuilder.invocation(getQuotaMethod); + final response = await (requestBuilder + ..usings(getQuotaMethod.requiredCapabilities)) + .build() + .execute(); + + final getQuotaResponse = response.parse( + getQuotaInvocation.methodCallId, GetQuotaResponse.deserialize); + + if(getQuotaResponse != null) { + return QuotasResponse( + accountId: getQuotaResponse.accountId, + notFound: getQuotaResponse.notFound, + quotas: getQuotaResponse.list, + state: getQuotaResponse.state, + ); + } else { + throw NotFoundQuotasException(); + } + + } +} diff --git a/lib/features/quotas/data/repository/quotas_repository_impl.dart b/lib/features/quotas/data/repository/quotas_repository_impl.dart new file mode 100644 index 000000000..9a2636661 --- /dev/null +++ b/lib/features/quotas/data/repository/quotas_repository_impl.dart @@ -0,0 +1,15 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:tmail_ui_user/features/quotas/data/datasource/quotas_data_source.dart'; +import 'package:tmail_ui_user/features/quotas/domain/model/quotas_response.dart'; +import 'package:tmail_ui_user/features/quotas/domain/repository/quotas_repository.dart'; + +class QuotasRepositoryImpl extends QuotasRepository { + final QuotasDataSource _dataSource; + + QuotasRepositoryImpl(this._dataSource); + + @override + Future getQuotas(AccountId accountId) { + return _dataSource.getQuotas(accountId); + } +} diff --git a/lib/features/quotas/domain/model/exceptions/quotas_exception.dart b/lib/features/quotas/domain/model/exceptions/quotas_exception.dart new file mode 100644 index 000000000..914236be9 --- /dev/null +++ b/lib/features/quotas/domain/model/exceptions/quotas_exception.dart @@ -0,0 +1,3 @@ +class NotFoundQuotasException implements Exception { + NotFoundQuotasException(); +} diff --git a/lib/features/quotas/domain/model/quotas_response.dart b/lib/features/quotas/domain/model/quotas_response.dart new file mode 100644 index 000000000..7aa01e239 --- /dev/null +++ b/lib/features/quotas/domain/model/quotas_response.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/quotas/quota.dart'; + +class QuotasResponse with EquatableMixin { + final AccountId accountId; + final State state; + final List quotas; + final List? notFound; + + QuotasResponse({ + required this.accountId, + required this.state, + required this.quotas, + this.notFound, + }); + + bool hasData() { + return quotas.isNotEmpty; + } + + @override + List get props => [ + quotas, + state, + notFound, + ]; +} diff --git a/lib/features/quotas/domain/repository/quotas_repository.dart b/lib/features/quotas/domain/repository/quotas_repository.dart new file mode 100644 index 000000000..9f60543f6 --- /dev/null +++ b/lib/features/quotas/domain/repository/quotas_repository.dart @@ -0,0 +1,6 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:tmail_ui_user/features/quotas/domain/model/quotas_response.dart'; + +abstract class QuotasRepository { + Future getQuotas(AccountId accountId); +} diff --git a/lib/features/quotas/domain/state/get_quotas_state.dart b/lib/features/quotas/domain/state/get_quotas_state.dart new file mode 100644 index 000000000..bce2b9923 --- /dev/null +++ b/lib/features/quotas/domain/state/get_quotas_state.dart @@ -0,0 +1,25 @@ +import 'package:core/core.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart'; +import 'package:jmap_dart_client/jmap/quotas/quota.dart'; + +class GetQuotasSuccess extends UIState { + final List quotas; + final State? state; + + GetQuotasSuccess(this.quotas, this.state); + + @override + List get props => [ + quotas, + state, + ]; +} + +class GetQuotasFailure extends FeatureFailure { + final dynamic exception; + + GetQuotasFailure(this.exception); + + @override + List get props => [exception]; +} diff --git a/lib/features/quotas/domain/use_case/get_quotas_interactor.dart b/lib/features/quotas/domain/use_case/get_quotas_interactor.dart new file mode 100644 index 000000000..cc325bcfd --- /dev/null +++ b/lib/features/quotas/domain/use_case/get_quotas_interactor.dart @@ -0,0 +1,23 @@ +import 'dart:core'; + +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:tmail_ui_user/features/quotas/domain/repository/quotas_repository.dart'; +import 'package:tmail_ui_user/features/quotas/domain/state/get_quotas_state.dart'; + +class GetQuotasInteractor { + final QuotasRepository quotasRepository; + + GetQuotasInteractor(this.quotasRepository); + + Stream> execute(AccountId accountId) async* { + try { + yield Right(LoadingState()); + final response = await quotasRepository.getQuotas(accountId); + yield Right(GetQuotasSuccess(response.quotas, response.state)); + } catch (exception) { + yield Left(GetQuotasFailure(exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/quotas/presentation/quotas_bindings.dart b/lib/features/quotas/presentation/quotas_bindings.dart new file mode 100644 index 000000000..e70029a54 --- /dev/null +++ b/lib/features/quotas/presentation/quotas_bindings.dart @@ -0,0 +1,43 @@ +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/base_bindings.dart'; +import 'package:tmail_ui_user/features/quotas/data/datasource/quotas_data_source.dart'; +import 'package:tmail_ui_user/features/quotas/data/datasource_impl/quotas_data_source_impl.dart'; +import 'package:tmail_ui_user/features/quotas/data/network/quotas_api.dart'; +import 'package:tmail_ui_user/features/quotas/data/repository/quotas_repository_impl.dart'; +import 'package:tmail_ui_user/features/quotas/domain/repository/quotas_repository.dart'; +import 'package:tmail_ui_user/features/quotas/domain/use_case/get_quotas_interactor.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/quotas_controller.dart'; +import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; + +class QuotasBindings extends BaseBindings { + @override + void bindingsController() { + Get.put(QuotasController(Get.find())); + } + + @override + void bindingsDataSource() { + Get.lazyPut(() => Get.find()); + } + + @override + void bindingsDataSourceImpl() { + Get.lazyPut(() => QuotasDataSourceImpl(Get.find(), Get.find())); + } + + @override + void bindingsInteractor() { + Get.lazyPut(() => GetQuotasInteractor(Get.find())); + } + + @override + void bindingsRepository() { + Get.lazyPut(() => Get.find()); + } + + @override + void bindingsRepositoryImpl() { + Get.lazyPut(() => QuotasRepositoryImpl(Get.find())); + } + +} \ No newline at end of file diff --git a/lib/features/quotas/presentation/quotas_controller.dart b/lib/features/quotas/presentation/quotas_controller.dart new file mode 100644 index 000000000..e33421b54 --- /dev/null +++ b/lib/features/quotas/presentation/quotas_controller.dart @@ -0,0 +1,82 @@ +import 'package:core/core.dart'; +import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/quotas/data_types.dart'; +import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/quotas/domain/state/get_quotas_state.dart'; +import 'package:tmail_ui_user/features/quotas/domain/use_case/get_quotas_interactor.dart'; + +class QuotasController extends BaseController { + final MailboxDashBoardController mailboxDashBoardController = Get.find(); + final GetQuotasInteractor _getQuotasInteractor; + final usedCapacity = Rx(0); + final softLimitCapacity = Rx(0); + final warningLimitCapacity = Rx(0); + final enableShowQuotas = false.obs; + late Worker accountIdWorker; + + double get progressUsedCapacity => softLimitCapacity.value != 0 + ? (usedCapacity.value / softLimitCapacity.value) + : 0; + + bool get enableShowWarningQuotas => + enableShowQuotas.value && usedCapacity.value >= warningLimitCapacity.value; + + final ImagePaths imagePaths = Get.find(); + final ResponsiveUtils responsiveUtils = Get.find(); + + QuotasController(this._getQuotasInteractor); + + void _initWorker() { + accountIdWorker = ever(mailboxDashBoardController.accountId, (accountId) { + if (accountId is AccountId) { + enableShowQuotas.value = mailboxDashBoardController.sessionCurrent != null && + mailboxDashBoardController.sessionCurrent!.capabilities.containsValue(CapabilityIdentifier.jmapQuota); + consumeState(_getQuotasInteractor.execute(mailboxDashBoardController.accountId.value!)); + } + }); + + } + + @override + void onDone() { + viewState.value.fold( + (failure) { + if (failure is GetQuotasFailure) { + logError('QuotasController::onDone():[GetQuotasFailure]: ${failure.exception}'); + } + }, + (success) { + if (success is GetQuotasSuccess) { + _handleGetQuotasSuccess(success); + } + } + ); + } + + void _handleGetQuotasSuccess(GetQuotasSuccess success) { + try { + final quotas = success.quotas.firstWhere((e) => e.resourceType == ResourceType.octets); + usedCapacity.value = quotas.used.value; + warningLimitCapacity.value = quotas.warnLimit?.value ?? 0; + softLimitCapacity.value = quotas.softLimit?.value ?? 0; + } catch (e) { + logError('QuotasController::_handleGetQuotasSuccess():[NotFoundException]: $e'); + } + + } + + @override + void onInit() { + _initWorker(); + super.onInit(); + } + + @override + void onClose() { + accountIdWorker.call(); + super.onClose(); + } +} diff --git a/lib/features/quotas/presentation/widget/quotas_footer_widget.dart b/lib/features/quotas/presentation/widget/quotas_footer_widget.dart new file mode 100644 index 000000000..bd51fc813 --- /dev/null +++ b/lib/features/quotas/presentation/widget/quotas_footer_widget.dart @@ -0,0 +1,62 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/quotas_controller.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class QuotasFooterWidget extends GetWidget { + const QuotasFooterWidget({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Obx( + () => controller.enableShowQuotas.value + ? Container( + color: AppColor.colorBgDesktop, + padding: const EdgeInsets.all(24), + alignment: Alignment.centerLeft, + child: IntrinsicWidth( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SvgPicture.asset(controller.imagePaths.icQuotas), + const SizedBox(width: 12), + Text( + AppLocalizations.of(context).storageQuotas, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w400, + color: AppColor.loginTextFieldHintColor), + ) + ], + ), + const SizedBox(height: 8), + LinearProgressIndicator( + color: controller.enableShowWarningQuotas ? AppColor.colorProgressQuotasWarning: AppColor.primaryColor, + minHeight: 3, + backgroundColor: AppColor.colorDivider, + value: controller.progressUsedCapacity, + ), + const SizedBox(height: 8), + Text( + AppLocalizations.of(context).textQuotasUsed( + controller.usedCapacity.value, + controller.softLimitCapacity.value, + ), + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w400, + color: AppColor.loginTextFieldHintColor), + ) + ], + ), + ), + ) + : const SizedBox.shrink(), + ); + } +} diff --git a/lib/features/quotas/presentation/widget/quotas_warning_banner_widget.dart b/lib/features/quotas/presentation/widget/quotas_warning_banner_widget.dart new file mode 100644 index 000000000..a4d398348 --- /dev/null +++ b/lib/features/quotas/presentation/widget/quotas_warning_banner_widget.dart @@ -0,0 +1,57 @@ +import 'package:core/core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/quotas_controller.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class QuotasWarningBannerWidget extends GetWidget { + const QuotasWarningBannerWidget({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Obx( + () => controller.enableShowWarningQuotas + ? Container( + padding: const EdgeInsets.all(16), + margin: const EdgeInsets.only(left: 12, right: 12, top: 8), + decoration: BoxDecoration( + color: AppColor.colorBackgroundQuotasWarning.withOpacity(0.12), + borderRadius: const BorderRadius.all(Radius.circular(10)), + ), + child: Row( + children: [ + SvgPicture.asset(controller.imagePaths.icQuotasWarning), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context) + .textQuotasWarningTitle(controller.progressUsedCapacity *100), + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColor.colorTitleQuotasWarning, + ), + ), + const SizedBox(height: 8), + Text( + AppLocalizations.of(context) + .textQuotasWarningContent, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w400, + color: AppColor.loginTextFieldHintColor), + ), + ], + ), + ), + ], + ), + ) + : const SizedBox.shrink(), + ); + } +} diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index b858171e5..b2720f98b 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -12,6 +12,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; +import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_warning_banner_widget.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart'; @@ -56,6 +57,7 @@ class ThreadView extends GetWidget with AppLoaderMixin, ... [ _buildAppBarNormal(context), _buildSearchBarView(context), + const QuotasWarningBannerWidget(), _buildVacationNotificationMessage(context), ], _buildEmptyTrashButton(context), diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 9f6c82628..f9ba8ae0d 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,9 @@ { +<<<<<<< HEAD "@@last_modified": "2022-11-08T16:55:51.988372", +======= + "@@last_modified": "2022-11-09T17:10:23.172672", +>>>>>>> 24967b2c (TF-1014: [Dev] Show quota at the bottom of mailbox) "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2418,12 +2422,18 @@ "placeholders_order": [], "placeholders": {} }, +<<<<<<< HEAD "forwardingSettingExplanation": "Allows a new recipient to see the email sent if they were not originally included in the email chain.", "@forwardingSettingExplanation": { +======= + "storageQuotas": "Storage", + "@storageQuotas": { +>>>>>>> 24967b2c (TF-1014: [Dev] Show quota at the bottom of mailbox) "type": "text", "placeholders_order": [], "placeholders": {} }, +<<<<<<< HEAD "addRecipientButton": "Add recipient", "@addRecipientButton": { "type": "text", @@ -2450,6 +2460,28 @@ ], "placeholders": { "count": {} +======= + "textQuotasUsed": "{used} GB of {softLimit} GB Used", + "@textQuotasUsed": { + "type": "text", + "placeholders_order": [ + "used", + "softLimit" + ], + "placeholders": { + "used": {}, + "softLimit": {} + } + }, + "textQuotasWarningTitle": "You are running out of storage ({progress}%).", + "@textQuotasWarningTitle": { + "type": "text", + "placeholders_order": [ + "progress" + ], + "placeholders": { + "progress": {} +>>>>>>> 24967b2c (TF-1014: [Dev] Show quota at the bottom of mailbox) } } } \ No newline at end of file diff --git a/lib/main/bindings/network/network_bindings.dart b/lib/main/bindings/network/network_bindings.dart index 5ded2e74b..7ededc3c8 100644 --- a/lib/main/bindings/network/network_bindings.dart +++ b/lib/main/bindings/network/network_bindings.dart @@ -21,6 +21,7 @@ import 'package:tmail_ui_user/features/manage_account/data/network/forwarding_ap import 'package:tmail_ui_user/features/manage_account/data/network/identity_api.dart'; import 'package:tmail_ui_user/features/manage_account/data/network/manage_account_api.dart'; import 'package:tmail_ui_user/features/manage_account/data/network/vacation_api.dart'; +import 'package:tmail_ui_user/features/quotas/data/network/quotas_api.dart'; import 'package:tmail_ui_user/features/session/data/network/session_api.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; @@ -86,6 +87,7 @@ class NetworkBindings extends Bindings { Get.put(IdentityAPI(Get.find())); Get.put(MdnAPI(Get.find())); Get.put(ForwardingAPI(Get.find())); + Get.put(QuotasAPI(Get.find())); } void _bindingConnection() { diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 2a16abee7..a3e2f2f5b 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -2519,4 +2519,34 @@ class AppLocalizations { args: [count] ); } + + String get storageQuotas { + return Intl.message( + 'Storage', + name: 'storageQuotas', + ); + } + + String textQuotasUsed(num used, num softLimit) { + return Intl.message( + '$used GB of $softLimit GB Used', + name: 'textQuotasUsed', + args: [used, softLimit], + ); + } + + String textQuotasWarningTitle(num progress) { + return Intl.message( + 'You are running out of storage ($progress%).', + name: 'textQuotasWarningTitle', + args: [progress], + ); + } + + String get textQuotasWarningContent { + return Intl.message( + "Soon you won't be able to email in Tmail. Please clean your storage or upgrade your storage to get full features in Tmail.", + name: 'textQuotasWarningTitle', + ); + } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index bb332d3db..6cec177c6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -757,7 +757,7 @@ packages: description: path: "." ref: master - resolved-ref: "45ea109f70be0d868b005aadf11a39e2ac816c38" + resolved-ref: ea636193b15a49e9d59dab143e957f529e1bd836 url: "https://github.com/linagora/jmap-dart-client.git" source: git version: "0.0.1" @@ -1184,7 +1184,7 @@ packages: name: shelf_web_socket url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" simple_observable: dependency: transitive description: @@ -1252,7 +1252,7 @@ packages: name: stream_transform url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.1.0" string_scanner: dependency: transitive description: @@ -1273,7 +1273,7 @@ packages: name: syncfusion_flutter_core url: "https://pub.dartlang.org" source: hosted - version: "20.3.52" + version: "20.3.56" syncfusion_flutter_datepicker: dependency: transitive description: @@ -1364,7 +1364,7 @@ packages: name: url_launcher_android url: "https://pub.dartlang.org" source: hosted - version: "6.0.20" + version: "6.0.21" url_launcher_ios: dependency: transitive description: