diff --git a/lib/ui/molecules/cards/services_page_card.dart b/lib/ui/molecules/cards/services_page_card.dart new file mode 100644 index 00000000..0bb55703 --- /dev/null +++ b/lib/ui/molecules/cards/services_page_card.dart @@ -0,0 +1,152 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:gap/gap.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/logic/models/state_types.dart'; +import 'package:selfprivacy/ui/atoms/masks/icon_status_mask.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; +import 'package:selfprivacy/utils/ui_helpers.dart'; + +class ServicesPageCard extends StatelessWidget { + const ServicesPageCard({required this.service, super.key}); + + final Service service; + @override + Widget build(final BuildContext context) { + final isReady = context.watch().state + is ServerInstallationFinished; + + final config = context.watch().state; + final domainName = UiHelpers.getDomainName(config); + + StateType getStatus(final ServiceStatus status) { + switch (status) { + case ServiceStatus.active: + return StateType.stable; + case ServiceStatus.activating: + return StateType.stable; + case ServiceStatus.deactivating: + return StateType.uninitialized; + case ServiceStatus.inactive: + return StateType.uninitialized; + case ServiceStatus.failed: + return StateType.error; + case ServiceStatus.off: + return StateType.uninitialized; + case ServiceStatus.reloading: + return StateType.stable; + } + } + + return Card( + clipBehavior: Clip.antiAlias, + child: InkResponse( + highlightShape: BoxShape.rectangle, + onTap: isReady + ? () => context.pushRoute( + ServiceRoute(serviceId: service.id), + ) + : null, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + IconStatusMask( + status: getStatus(service.status), + icon: SvgPicture.string( + service.svgIcon, + width: 32.0, + height: 32.0, + colorFilter: const ColorFilter.mode( + Colors.white, + BlendMode.srcIn, + ), + ), + ), + const Gap(8), + Expanded( + child: Text( + service.displayName, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + if (service.url != '' && + service.url != null && + service.isEnabled) + Column( + children: [ + _ServiceLink( + url: service.url ?? '', + ), + const SizedBox(height: 8), + ], + ), + if (service.id == 'simple-nixos-mailserver') + Column( + children: [ + _ServiceLink( + url: domainName, + isActive: false, + ), + const SizedBox(height: 8), + ], + ), + Text( + service.description, + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 8), + Text( + service.loginInfo, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + const SizedBox(height: 8), + ], + ), + ], + ), + ), + ), + ); + } +} + +class _ServiceLink extends StatelessWidget { + const _ServiceLink({ + required this.url, + this.isActive = true, + }); + + final String url; + final bool isActive; + + @override + Widget build(final BuildContext context) => GestureDetector( + onTap: isActive + ? () => launchURL( + url, + ) + : null, + child: Text( + url, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.primary, + decoration: TextDecoration.underline, + ), + ), + ); +} diff --git a/lib/ui/pages/services/config_item_fields/basic_bool_config_item.dart b/lib/ui/molecules/config_item_fields/basic_bool_config_item.dart similarity index 86% rename from lib/ui/pages/services/config_item_fields/basic_bool_config_item.dart rename to lib/ui/molecules/config_item_fields/basic_bool_config_item.dart index e3515fd2..35848f1c 100644 --- a/lib/ui/pages/services/config_item_fields/basic_bool_config_item.dart +++ b/lib/ui/molecules/config_item_fields/basic_bool_config_item.dart @@ -1,4 +1,6 @@ -part of '../service_settings_page.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/models/service.dart'; class BasicBoolConfigItem extends StatefulWidget { const BasicBoolConfigItem({ diff --git a/lib/ui/pages/services/config_item_fields/basic_enum_config_item.dart b/lib/ui/molecules/config_item_fields/basic_enum_config_item.dart similarity index 92% rename from lib/ui/pages/services/config_item_fields/basic_enum_config_item.dart rename to lib/ui/molecules/config_item_fields/basic_enum_config_item.dart index 50ff0bc6..825e2119 100644 --- a/lib/ui/pages/services/config_item_fields/basic_enum_config_item.dart +++ b/lib/ui/molecules/config_item_fields/basic_enum_config_item.dart @@ -1,4 +1,6 @@ -part of '../service_settings_page.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/models/service.dart'; class BasicEnumConfigItem extends StatefulWidget { const BasicEnumConfigItem({ diff --git a/lib/ui/pages/services/config_item_fields/basic_string_config_item.dart b/lib/ui/molecules/config_item_fields/basic_string_config_item.dart similarity index 94% rename from lib/ui/pages/services/config_item_fields/basic_string_config_item.dart rename to lib/ui/molecules/config_item_fields/basic_string_config_item.dart index 79c53ea6..062b0bca 100644 --- a/lib/ui/pages/services/config_item_fields/basic_string_config_item.dart +++ b/lib/ui/molecules/config_item_fields/basic_string_config_item.dart @@ -1,4 +1,7 @@ -part of '../service_settings_page.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:selfprivacy/logic/models/service.dart'; class BasicStringConfigItem extends StatefulWidget { const BasicStringConfigItem({ diff --git a/lib/ui/pages/services/config_item_fields/domain_string_config_item.dart b/lib/ui/molecules/config_item_fields/domain_string_config_item.dart similarity index 91% rename from lib/ui/pages/services/config_item_fields/domain_string_config_item.dart rename to lib/ui/molecules/config_item_fields/domain_string_config_item.dart index b1cfc241..6b6e8205 100644 --- a/lib/ui/pages/services/config_item_fields/domain_string_config_item.dart +++ b/lib/ui/molecules/config_item_fields/domain_string_config_item.dart @@ -1,4 +1,9 @@ -part of '../service_settings_page.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; +import 'package:selfprivacy/logic/models/service.dart'; class DomainStringConfigItem extends StatefulWidget { const DomainStringConfigItem({ diff --git a/lib/ui/pages/services/service_page.dart b/lib/ui/pages/services/service.dart similarity index 100% rename from lib/ui/pages/services/service_page.dart rename to lib/ui/pages/services/service.dart diff --git a/lib/ui/pages/services/service_settings_page.dart b/lib/ui/pages/services/service_settings.dart similarity index 95% rename from lib/ui/pages/services/service_settings_page.dart rename to lib/ui/pages/services/service_settings.dart index d64add5e..4c2d16d2 100644 --- a/lib/ui/pages/services/service_settings_page.dart +++ b/lib/ui/pages/services/service_settings.dart @@ -4,18 +4,15 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:gap/gap.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/bloc/services/services_bloc.dart'; import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; -import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; - -part 'config_item_fields/basic_string_config_item.dart'; -part 'config_item_fields/basic_bool_config_item.dart'; -part 'config_item_fields/basic_enum_config_item.dart'; -part 'config_item_fields/domain_string_config_item.dart'; +import 'package:selfprivacy/ui/molecules/config_item_fields/basic_bool_config_item.dart'; +import 'package:selfprivacy/ui/molecules/config_item_fields/basic_enum_config_item.dart'; +import 'package:selfprivacy/ui/molecules/config_item_fields/basic_string_config_item.dart'; +import 'package:selfprivacy/ui/molecules/config_item_fields/domain_string_config_item.dart'; @RoutePage() class ServiceSettingsPage extends StatefulWidget { diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 6f948463..7380aa6b 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -1,23 +1,16 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:gap/gap.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/bloc/outdated_server_checker/outdated_server_checker_bloc.dart'; import 'package:selfprivacy/logic/bloc/services/services_bloc.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/models/service.dart'; -import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/ui/atoms/icons/brand_icons.dart'; -import 'package:selfprivacy/ui/atoms/masks/icon_status_mask.dart'; import 'package:selfprivacy/ui/molecules/cards/server_outdated_card.dart'; +import 'package:selfprivacy/ui/molecules/cards/services_page_card.dart'; import 'package:selfprivacy/ui/molecules/placeholders/empty_page_placeholder.dart'; import 'package:selfprivacy/ui/organisms/headers/brand_header.dart'; -import 'package:selfprivacy/ui/router/router.dart'; import 'package:selfprivacy/utils/breakpoints.dart'; -import 'package:selfprivacy/utils/launch_url.dart'; -import 'package:selfprivacy/utils/ui_helpers.dart'; @RoutePage() class ServicesPage extends StatefulWidget { @@ -72,13 +65,13 @@ class _ServicesPageState extends State { 'basis.services_title'.tr(), style: Theme.of(context).textTheme.bodyLarge, ), - const SizedBox(height: 24), + const SizedBox(height: 16), ...services.map( (final service) => Padding( padding: const EdgeInsets.only( - bottom: 30, + bottom: 16, ), - child: _Card(service: service), + child: ServicesPageCard(service: service), ), ), ], @@ -87,144 +80,3 @@ class _ServicesPageState extends State { ); } } - -class _Card extends StatelessWidget { - const _Card({required this.service}); - - final Service service; - @override - Widget build(final BuildContext context) { - final isReady = context.watch().state - is ServerInstallationFinished; - - final config = context.watch().state; - final domainName = UiHelpers.getDomainName(config); - - StateType getStatus(final ServiceStatus status) { - switch (status) { - case ServiceStatus.active: - return StateType.stable; - case ServiceStatus.activating: - return StateType.stable; - case ServiceStatus.deactivating: - return StateType.uninitialized; - case ServiceStatus.inactive: - return StateType.uninitialized; - case ServiceStatus.failed: - return StateType.error; - case ServiceStatus.off: - return StateType.uninitialized; - case ServiceStatus.reloading: - return StateType.stable; - } - } - - return Card( - clipBehavior: Clip.antiAlias, - child: InkResponse( - highlightShape: BoxShape.rectangle, - onTap: isReady - ? () => context.pushRoute( - ServiceRoute(serviceId: service.id), - ) - : null, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - IconStatusMask( - status: getStatus(service.status), - icon: SvgPicture.string( - service.svgIcon, - width: 30.0, - height: 30.0, - colorFilter: const ColorFilter.mode( - Colors.white, - BlendMode.srcIn, - ), - ), - ), - const Gap(8), - Expanded( - child: Text( - service.displayName, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - ], - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 8), - if (service.url != '' && - service.url != null && - service.isEnabled) - Column( - children: [ - _ServiceLink( - url: service.url ?? '', - ), - const SizedBox(height: 8), - ], - ), - if (service.id == 'mailserver') - Column( - children: [ - _ServiceLink( - url: domainName, - isActive: false, - ), - const SizedBox(height: 8), - ], - ), - Text( - service.description, - style: Theme.of(context).textTheme.bodyMedium, - ), - const SizedBox(height: 8), - Text( - service.loginInfo, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), - ), - const SizedBox(height: 8), - ], - ), - ], - ), - ), - ), - ); - } -} - -class _ServiceLink extends StatelessWidget { - const _ServiceLink({ - required this.url, - this.isActive = true, - }); - - final String url; - final bool isActive; - - @override - Widget build(final BuildContext context) => GestureDetector( - onTap: isActive - ? () => launchURL( - url, - ) - : null, - child: Text( - url, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.primary, - decoration: TextDecoration.underline, - ), - ), - ); -} diff --git a/lib/ui/router/router.dart b/lib/ui/router/router.dart index 31d4c097..053566f3 100644 --- a/lib/ui/router/router.dart +++ b/lib/ui/router/router.dart @@ -26,8 +26,8 @@ import 'package:selfprivacy/ui/pages/server_details/server_settings_screen.dart' import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart'; import 'package:selfprivacy/ui/pages/server_storage/extending_volume.dart'; import 'package:selfprivacy/ui/pages/server_storage/server_storage.dart'; -import 'package:selfprivacy/ui/pages/services/service_page.dart'; -import 'package:selfprivacy/ui/pages/services/service_settings_page.dart'; +import 'package:selfprivacy/ui/pages/services/service.dart'; +import 'package:selfprivacy/ui/pages/services/service_settings.dart'; import 'package:selfprivacy/ui/pages/services/services.dart'; import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';