From 40568925e1d8a0c710541887ae356b80b3e8c677 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 18 Sep 2023 12:22:37 -0300 Subject: [PATCH 1/6] refactor(ui): Move service card name to its icon row - Resolves https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/350 --- lib/ui/pages/services/services.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 99bb848e..f6a0e00a 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -129,17 +129,17 @@ class _Card extends StatelessWidget { ), ), ), + const SizedBox(width: 10), + Text( + service.displayName, + style: Theme.of(context).textTheme.headlineMedium, + ), ], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 12), - Text( - service.displayName, - style: Theme.of(context).textTheme.headlineMedium, - ), - const SizedBox(height: 8), + const SizedBox(height: 10), if (service.url != '' && service.url != null) Column( children: [ From c64c6e11d7ae59bc7d4a53582bf5fa5da3d4582a Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 18 Sep 2023 12:42:40 -0300 Subject: [PATCH 2/6] refactor(ui): Change SizedBox dimension hardcode from 10 to 8 for service cards --- lib/ui/pages/services/services.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 17e947b5..dd2b3701 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -129,7 +129,7 @@ class _Card extends StatelessWidget { ), ), ), - const SizedBox(width: 10), + const SizedBox(width: 8), Text( service.displayName, style: Theme.of(context).textTheme.headlineMedium, @@ -139,14 +139,14 @@ class _Card extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 10), + const SizedBox(height: 8), if (service.url != '' && service.url != null) Column( children: [ _ServiceLink( url: service.url ?? '', ), - const SizedBox(height: 10), + const SizedBox(height: 8), ], ), if (service.id == 'mailserver') @@ -156,21 +156,21 @@ class _Card extends StatelessWidget { url: domainName, isActive: false, ), - const SizedBox(height: 10), + const SizedBox(height: 8), ], ), Text( service.description, style: Theme.of(context).textTheme.bodyMedium, ), - const SizedBox(height: 10), + const SizedBox(height: 8), Text( service.loginInfo, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.secondary, ), ), - const SizedBox(height: 10), + const SizedBox(height: 8), ], ) ], From aa4429cc79f30a55a8fedde6af35893a91a67700 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Sat, 23 Sep 2023 22:17:54 -0300 Subject: [PATCH 3/6] 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 4/6] 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 { From fca04f89ada46182cf325075ba074642a4444d58 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Tue, 26 Sep 2023 20:05:17 +0300 Subject: [PATCH 5/6] docs: Add issue templates --- .gitea/ISSUE_TEMPLATE/bug.yaml | 68 ++++++++++++++++++++++++++ .gitea/ISSUE_TEMPLATE/feature.yaml | 23 +++++++++ .gitea/ISSUE_TEMPLATE/translation.yaml | 29 +++++++++++ 3 files changed, 120 insertions(+) create mode 100644 .gitea/ISSUE_TEMPLATE/bug.yaml create mode 100644 .gitea/ISSUE_TEMPLATE/feature.yaml create mode 100644 .gitea/ISSUE_TEMPLATE/translation.yaml diff --git a/.gitea/ISSUE_TEMPLATE/bug.yaml b/.gitea/ISSUE_TEMPLATE/bug.yaml new file mode 100644 index 00000000..f9aed2ab --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug.yaml @@ -0,0 +1,68 @@ +name: Bug report +about: File a bug report +labels: + - Bug +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! Please provide a short, but a descriptive title for your issue. + - type: textarea + id: expected-behaviour + attributes: + label: Expected Behavior + description: What did you expect to happen? + validations: + required: true + - type: textarea + id: actual-behaviour + attributes: + label: Actual Behavior + description: What actually happened? + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to Reproduce + description: What steps can we follow to reproduce this issue? + placeholder: | + 1. First step + 2. Second step + 3. and so on... + validations: + required: true + - type: textarea + id: context + attributes: + label: Context and notes + description: Additional information about environment or what were you trying to do. If you have an idea how to fix this issue, please describe it here too. + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output, if you have any. This will be automatically formatted into code, so no need for backticks. + render: shell + - type: input + id: app-version + attributes: + label: App Version + description: What version of SelfPrivacy app are you running? You can find it in the "About" section of the app. + validations: + required: true + - type: input + id: api-version + attributes: + label: Server API Version + description: What version of SelfPrivacy API are you running? You can find it in the "About" section of the app. Leave it empty, if your app is not connected to the server yet. + - type: dropdown + id: os + attributes: + label: Operating System + description: What operating system are you using? + options: + - Android + - iOS + - Linux + - macOS + - Windows diff --git a/.gitea/ISSUE_TEMPLATE/feature.yaml b/.gitea/ISSUE_TEMPLATE/feature.yaml new file mode 100644 index 00000000..0d3fa085 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/feature.yaml @@ -0,0 +1,23 @@ +name: Feature request +about: Suggest an idea for this project +label: + - Feature request +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! Please provide a short, but a descriptive title for your issue. + - type: textarea + id: description + attributes: + label: Description + description: Describe the feature you'd like to see. + placeholder: | + As a user, I want to be able to... + validations: + required: true + - type: textarea + id: context + attributes: + label: Context and notes + description: Additional information about environment and what were you trying to do. If you have an idea how to implement this feature, please describe it here too. diff --git a/.gitea/ISSUE_TEMPLATE/translation.yaml b/.gitea/ISSUE_TEMPLATE/translation.yaml new file mode 100644 index 00000000..c7348b94 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/translation.yaml @@ -0,0 +1,29 @@ +name: Translation issue +about: File a translation (localization) issue +labels: + - Translations +body: + - type: markdown + attributes: + value: | + Translations can be modified and discussed on [Weblate](https://weblate.selfprivacy.org/projects/selfprivacy/). You can fix the mistranslation issue yourself there. Using the search, you can also find the string ID of the mistranslated string. If your issue is more complex, please file it here + + If you are a member of SelfPrivacy core team, you **must** fix the issue yourself on Weblate. + - type: input + id: language + attributes: + label: Language + description: What language is affected? + placeholder: | + English + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Describe the issue in detail. If you have an idea how to fix this issue, please describe it here too. Include the string ID of the mistranslated string, if possible. + placeholder: | + The string `string.id` is translated as "foo", but it should be "bar". + validations: + required: true From 5ff615bb8b0e3336835e0c4ccdb85107a67debc2 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Tue, 26 Sep 2023 20:24:04 +0300 Subject: [PATCH 6/6] docs: Fix grammar of issue templates --- .gitea/ISSUE_TEMPLATE/bug.yaml | 4 ++-- .gitea/ISSUE_TEMPLATE/feature.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/ISSUE_TEMPLATE/bug.yaml b/.gitea/ISSUE_TEMPLATE/bug.yaml index f9aed2ab..f9441722 100644 --- a/.gitea/ISSUE_TEMPLATE/bug.yaml +++ b/.gitea/ISSUE_TEMPLATE/bug.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! Please provide a short, but a descriptive title for your issue. + Thanks for taking the time to fill out this bug report! Please provide a short but a descriptive title for your issue. - type: textarea id: expected-behaviour attributes: @@ -54,7 +54,7 @@ body: id: api-version attributes: label: Server API Version - description: What version of SelfPrivacy API are you running? You can find it in the "About" section of the app. Leave it empty, if your app is not connected to the server yet. + description: What version of SelfPrivacy API are you running? You can find it in the "About" section of the app. Leave it empty if your app is not connected to the server yet. - type: dropdown id: os attributes: diff --git a/.gitea/ISSUE_TEMPLATE/feature.yaml b/.gitea/ISSUE_TEMPLATE/feature.yaml index 0d3fa085..8ff40a0f 100644 --- a/.gitea/ISSUE_TEMPLATE/feature.yaml +++ b/.gitea/ISSUE_TEMPLATE/feature.yaml @@ -6,7 +6,7 @@ body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this feature request! Please provide a short, but a descriptive title for your issue. + Thanks for taking the time to fill out this feature request! Please provide a short but a descriptive title for your issue. - type: textarea id: description attributes: