From aa4429cc79f30a55a8fedde6af35893a91a67700 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Sat, 23 Sep 2023 22:17:54 -0300 Subject: [PATCH 1/2] refactor(ui): Reorganize placeholders for empty pages - Resolve https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/348 - Make 'Data Center' cards unclickable when uninitialized --- assets/translations/en.json | 5 +- .../cubit/dns_records/dns_records_cubit.dart | 2 +- lib/ui/helpers/empty_page_placeholder.dart | 67 +++++++++++++++++ lib/ui/pages/providers/providers.dart | 14 +++- lib/ui/pages/services/services.dart | 55 ++++++++------ lib/ui/pages/users/empty.dart | 73 ------------------- lib/ui/pages/users/users.dart | 36 +++------ 7 files changed, 124 insertions(+), 128 deletions(-) create mode 100644 lib/ui/helpers/empty_page_placeholder.dart delete mode 100644 lib/ui/pages/users/empty.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 07ffe375..82f682ba 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -35,7 +35,8 @@ "done": "Done", "continue": "Continue", "alert": "Alert", - "copied_to_clipboard": "Copied to clipboard!" + "copied_to_clipboard": "Copied to clipboard!", + "please_connect": "Please connect your server, domain and DNS provider to dive in!" }, "more_page": { "configuration_wizard": "Setup wizard", @@ -315,6 +316,7 @@ "in_menu": "Server is not set up yet. Please finish setup using setup wizard for further work." }, "service_page": { + "nothing_here": "Nothing here", "open_in_browser": "Open in browser", "restart": "Restart service", "disable": "Disable service", @@ -371,7 +373,6 @@ "add_new_user": "Add a first user", "new_user": "New user", "delete_user": "Delete user", - "not_ready": "Please connect server, domain and DNS in the Providers tab, to be able to add a first user", "nobody_here": "Nobody here", "login": "Login", "new_user_info_note": "New user will automatically be granted an access to all of the services", diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 3fc2d199..6f295274 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -75,7 +75,7 @@ class DnsRecordsCubit @override Future clear() async { - emit(const DnsRecordsState(dnsState: DnsRecordsStatus.error)); + emit(const DnsRecordsState(dnsState: DnsRecordsStatus.uninitialized)); } Future refresh() async { diff --git a/lib/ui/helpers/empty_page_placeholder.dart b/lib/ui/helpers/empty_page_placeholder.dart new file mode 100644 index 00000000..e1bb0d7b --- /dev/null +++ b/lib/ui/helpers/empty_page_placeholder.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; + +class EmptyPagePlaceholder extends StatelessWidget { + const EmptyPagePlaceholder({ + required this.title, + required this.iconData, + required this.description, + this.showReadyCard = false, + super.key, + }); + + final String title; + final String description; + final IconData iconData; + final bool showReadyCard; + + @override + Widget build(final BuildContext context) => !showReadyCard + ? _expandedContent(context) + : Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 15), + child: NotReadyCard(), + ), + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Center( + child: _expandedContent(context), + ), + ), + ) + ], + ); + + Widget _expandedContent(final BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + iconData, + size: 50, + color: Theme.of(context).colorScheme.onBackground, + ), + const SizedBox(height: 16), + Text( + title, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + const SizedBox(height: 8), + Text( + description, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + ], + ), + ); +} diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index 64b87d34..5b2285f3 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -49,6 +49,8 @@ class _ProvidersPageState extends State { return StateType.stable; } + bool isClickable() => getServerStatus() != StateType.uninitialized; + StateType getDnsStatus() { if (dnsStatus == DnsRecordsStatus.uninitialized || dnsStatus == DnsRecordsStatus.refreshing) { @@ -83,7 +85,9 @@ class _ProvidersPageState extends State { subtitle: diskStatus.isDiskOkay ? 'storage.status_ok'.tr() : 'storage.status_error'.tr(), - onTap: () => context.pushRoute(const ServerDetailsRoute()), + onTap: isClickable() + ? () => context.pushRoute(const ServerDetailsRoute()) + : null, ), const SizedBox(height: 16), _Card( @@ -93,7 +97,9 @@ class _ProvidersPageState extends State { subtitle: appConfig.isDomainSelected ? appConfig.serverDomain!.domainName : '', - onTap: () => context.pushRoute(const DnsDetailsRoute()), + onTap: isClickable() + ? () => context.pushRoute(const DnsDetailsRoute()) + : null, ), const SizedBox(height: 16), _Card( @@ -103,7 +109,9 @@ class _ProvidersPageState extends State { icon: BrandIcons.save, title: 'backup.card_title'.tr(), subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '', - onTap: () => context.pushRoute(const BackupDetailsRoute()), + onTap: isClickable() + ? () => context.pushRoute(const BackupDetailsRoute()) + : null, ), ], ), diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 08446e78..62bb1321 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -7,9 +7,10 @@ import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; +import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; -import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart'; import 'package:selfprivacy/ui/router/router.dart'; import 'package:selfprivacy/utils/breakpoints.dart'; import 'package:selfprivacy/utils/launch_url.dart'; @@ -42,30 +43,36 @@ class _ServicesPageState extends State { ), ) : null, - body: RefreshIndicator( - onRefresh: () async { - await context.read().reload(); - }, - child: ListView( - padding: paddingH15V0, - children: [ - Text( - 'basis.services_title'.tr(), - style: Theme.of(context).textTheme.bodyLarge, - ), - const SizedBox(height: 24), - if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)], - ...services.map( - (final service) => Padding( - padding: const EdgeInsets.only( - bottom: 30, - ), - child: _Card(service: service), - ), + body: !isReady + ? EmptyPagePlaceholder( + showReadyCard: true, + title: 'service_page.nothing_here'.tr(), + description: 'basis.please_connect'.tr(), + iconData: BrandIcons.social, ) - ], - ), - ), + : RefreshIndicator( + onRefresh: () async { + await context.read().reload(); + }, + child: ListView( + padding: paddingH15V0, + children: [ + Text( + 'basis.services_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + const SizedBox(height: 24), + ...services.map( + (final service) => Padding( + padding: const EdgeInsets.only( + bottom: 30, + ), + child: _Card(service: service), + ), + ) + ], + ), + ), ); } } diff --git a/lib/ui/pages/users/empty.dart b/lib/ui/pages/users/empty.dart deleted file mode 100644 index f58dc740..00000000 --- a/lib/ui/pages/users/empty.dart +++ /dev/null @@ -1,73 +0,0 @@ -part of 'users.dart'; - -class _NoUsers extends StatelessWidget { - const _NoUsers({required this.text}); - - final String text; - - @override - Widget build(final BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - BrandIcons.users, - size: 50, - color: Theme.of(context).colorScheme.onBackground, - ), - const SizedBox(height: 20), - Text( - 'users.nobody_here'.tr(), - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Theme.of(context).colorScheme.onBackground, - ), - ), - const SizedBox(height: 10), - Text( - text, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleSmall?.copyWith( - color: Theme.of(context).colorScheme.onBackground, - ), - ), - ], - ), - ); -} - -class _CouldNotLoadUsers extends StatelessWidget { - const _CouldNotLoadUsers({required this.text}); - - final String text; - - @override - Widget build(final BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - BrandIcons.users, - size: 50, - color: Theme.of(context).colorScheme.onBackground, - ), - const SizedBox(height: 20), - Text( - 'users.could_not_fetch_users'.tr(), - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Theme.of(context).colorScheme.onBackground, - ), - ), - const SizedBox(height: 10), - Text( - text, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleSmall?.copyWith( - color: Theme.of(context).colorScheme.onBackground, - ), - ), - ], - ), - ); -} diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index 1ea9d76c..a3c0d319 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -16,17 +16,16 @@ import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; import 'package:selfprivacy/ui/components/cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; +import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/info_box/info_box.dart'; import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart'; -import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:selfprivacy/ui/router/router.dart'; import 'package:selfprivacy/utils/breakpoints.dart'; import 'package:selfprivacy/utils/platform_adapter.dart'; import 'package:selfprivacy/utils/ui_helpers.dart'; -part 'empty.dart'; part 'new_user.dart'; part 'user.dart'; part 'user_details.dart'; @@ -43,7 +42,12 @@ class UsersPage extends StatelessWidget { Widget child; if (!isReady) { - child = isNotReady(); + child = EmptyPagePlaceholder( + showReadyCard: true, + title: 'users.nobody_here'.tr(), + description: 'basis.please_connect'.tr(), + iconData: BrandIcons.users, + ); } else { child = BlocBuilder( builder: (final BuildContext context, final UsersState state) { @@ -64,8 +68,10 @@ class UsersPage extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - _CouldNotLoadUsers( - text: 'users.could_not_fetch_description'.tr(), + EmptyPagePlaceholder( + title: 'users.could_not_fetch_users'.tr(), + description: 'users.could_not_fetch_description'.tr(), + iconData: BrandIcons.users, ), const SizedBox(height: 18), BrandOutlinedButton( @@ -132,24 +138,4 @@ class UsersPage extends StatelessWidget { body: child, ); } - - Widget isNotReady() => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Padding( - padding: EdgeInsets.symmetric(horizontal: 15), - child: NotReadyCard(), - ), - Expanded( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Center( - child: _NoUsers( - text: 'users.not_ready'.tr(), - ), - ), - ), - ) - ], - ); } From 914775ac48545b7ea1e61ae5423a1a01fc07a741 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Tue, 26 Sep 2023 20:20:13 +0300 Subject: [PATCH 2/2] chore(ui): Change icon of the Services placeholder --- lib/ui/pages/services/services.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index af5123c3..36af585f 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -48,7 +48,7 @@ class _ServicesPageState extends State { showReadyCard: true, title: 'service_page.nothing_here'.tr(), description: 'basis.please_connect'.tr(), - iconData: BrandIcons.social, + iconData: BrandIcons.box, ) : RefreshIndicator( onRefresh: () async {