import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; 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'; @RoutePage() class ServiceSettingsPage extends StatefulWidget { const ServiceSettingsPage({required this.serviceId, super.key}); final String serviceId; @override State createState() => _ServiceSettingsPageState(); } class _ServiceSettingsPageState extends State { Map settings = {}; bool isFormValid = true; bool isJobAlreadyExists = false; Widget configurationItemToWidget( final BuildContext context, final ServiceConfigItem configItem, final Map settings, ) { switch (configItem) { case StringServiceConfigItem(): void onChanged(final String value, final bool isFieldValid) { if (isFieldValid) { setState(() { if (value == configItem.value) { settings.remove(configItem.id); } else { settings[configItem.id] = value; } isFormValid = true; }); } else { setState(() { isFormValid = false; }); } } if (configItem.widget == 'subdomain') { return DomainStringConfigItem( configItem: configItem, newValue: settings[configItem.id], onChanged: onChanged, ); } return BasicStringConfigItem( configItem: configItem, newValue: settings[configItem.id], onChanged: onChanged, ); case BoolServiceConfigItem(): void onChanged(final bool value) { setState(() { if (value == configItem.value) { settings.remove(configItem.id); } else { settings[configItem.id] = value; } }); } return BasicBoolConfigItem( configItem: configItem, newValue: settings[configItem.id], onChanged: onChanged, ); case EnumServiceConfigItem(): void onChanged(final String value) { setState(() { if (value == configItem.value) { settings.remove(configItem.id); } else { settings[configItem.id] = value; } }); } return BasicEnumConfigItem( configItem: configItem, newValue: settings[configItem.id], onChanged: onChanged, ); case FallbackServiceConfigItem(): return ListTile( title: Text(configItem.description), subtitle: Text(configItem.id), trailing: Text(configItem.type), leading: const Icon(Icons.error), ); case IntServiceConfigItem(): return ListTile( title: Text(configItem.description), subtitle: Text(configItem.id), trailing: Text(configItem.value.toString()), leading: const Icon(Icons.error), ); } } @override void initState() { super.initState(); final JobsState state = context.read().state; if (state is JobsStateWithJobs) { final ChangeServiceConfiguration? existingJob = state.clientJobList.firstWhereOrNull( (final ClientJob job) => job is ChangeServiceConfiguration && job.serviceId == widget.serviceId, ) as ChangeServiceConfiguration?; if (existingJob != null) { setState(() { settings = existingJob.settings; isJobAlreadyExists = true; }); } } } @override Widget build(final BuildContext context) { final Service? service = context.watch().state.getServiceById(widget.serviceId); if (service == null) { return const BrandHeroScreen( hasBackButton: true, children: [ Center( child: CircularProgressIndicator.adaptive(), ), ], ); } final JobsState state = context.watch().state; if (state is JobsStateLoading) { return BrandHeroScreen( hasBackButton: true, hasFlashButton: true, heroIconWidget: SvgPicture.string( service.svgIcon, width: 48.0, height: 48.0, colorFilter: ColorFilter.mode( Theme.of(context).colorScheme.onSurface, BlendMode.srcIn, ), ), heroTitle: service.displayName, heroSubtitle: 'service_page.settings'.tr(), children: [ Center( child: Column( children: [ Text( 'service_page.wait_for_jobs'.tr(), textAlign: TextAlign.center, ), const Gap(16.0), const CircularProgressIndicator.adaptive(), ], ), ), ], ); } final bool isModified = settings.isNotEmpty; return BrandHeroScreen( hasBackButton: true, hasFlashButton: true, heroIconWidget: SvgPicture.string( service.svgIcon, width: 48.0, height: 48.0, colorFilter: ColorFilter.mode( Theme.of(context).colorScheme.onSurface, BlendMode.srcIn, ), ), heroTitle: service.displayName, heroSubtitle: 'service_page.settings'.tr(), children: [ ...service.configuration.map( (final ServiceConfigItem configItem) => Padding( padding: const EdgeInsets.only(bottom: 16.0), child: configurationItemToWidget(context, configItem, settings), ), ), Padding( padding: const EdgeInsets.only(top: 16.0), child: FilledButton( onPressed: (isModified && isFormValid) ? () { context.read().addJob( ChangeServiceConfiguration( serviceId: service.id, serviceDisplayName: service.displayName, settings: settings, ), ); context.router.maybePop(); } : null, child: Text( isJobAlreadyExists ? 'service_page.update_job'.tr() : 'service_page.create_job'.tr(), ), ), ), ], ); } }