mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-04 23:24:20 +00:00
refactor(ui): Refactor DNS screen and introduce skeletons
This commit is contained in:
parent
c86c00cdf0
commit
65ec990410
76
lib/ui/molecules/cards/dns_state_card.dart
Normal file
76
lib/ui/molecules/cards/dns_state_card.dart
Normal file
|
@ -0,0 +1,76 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
|
||||
import 'package:selfprivacy/ui/atoms/cards/filled_card.dart';
|
||||
|
||||
class DnsStateCard extends StatelessWidget {
|
||||
const DnsStateCard({
|
||||
required this.dnsState,
|
||||
required this.fixCallback,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final DnsRecordsStatus dnsState;
|
||||
final Function fixCallback;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
String description = '';
|
||||
String subtitle = '';
|
||||
Icon icon = const Icon(
|
||||
Icons.check_circle_outline,
|
||||
size: 24.0,
|
||||
);
|
||||
bool isError = false;
|
||||
switch (dnsState) {
|
||||
case DnsRecordsStatus.uninitialized:
|
||||
description = 'domain.uninitialized'.tr();
|
||||
icon = const Icon(
|
||||
Icons.refresh,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = false;
|
||||
break;
|
||||
case DnsRecordsStatus.refreshing:
|
||||
description = 'domain.refreshing'.tr();
|
||||
icon = const Icon(
|
||||
Icons.refresh,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = false;
|
||||
break;
|
||||
case DnsRecordsStatus.good:
|
||||
description = 'domain.ok'.tr();
|
||||
icon = const Icon(
|
||||
Icons.check_circle_outline,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = false;
|
||||
break;
|
||||
case DnsRecordsStatus.error:
|
||||
description = 'domain.error'.tr();
|
||||
subtitle = 'domain.error_subtitle'.tr();
|
||||
icon = const Icon(
|
||||
Icons.error_outline,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = true;
|
||||
break;
|
||||
}
|
||||
return FilledCard(
|
||||
error: isError,
|
||||
child: ListTile(
|
||||
onTap: dnsState == DnsRecordsStatus.error ? () => fixCallback() : null,
|
||||
leading: icon,
|
||||
title: Text(description),
|
||||
subtitle: subtitle != '' ? Text(subtitle) : null,
|
||||
textColor: isError
|
||||
? Theme.of(context).colorScheme.onErrorContainer
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
iconColor: isError
|
||||
? Theme.of(context).colorScheme.onErrorContainer
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
40
lib/ui/molecules/list_items/dns_record_item.dart
Normal file
40
lib/ui/molecules/list_items/dns_record_item.dart
Normal file
|
@ -0,0 +1,40 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart';
|
||||
|
||||
class DnsRecordItem extends StatelessWidget {
|
||||
const DnsRecordItem({
|
||||
required this.dnsRecord,
|
||||
required this.refreshing,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final DesiredDnsRecord dnsRecord;
|
||||
final bool refreshing;
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final Color goodColor = Theme.of(context).colorScheme.onSurface;
|
||||
final Color errorColor = Theme.of(context).colorScheme.error;
|
||||
final Color neutralColor = Theme.of(context).colorScheme.onSurface;
|
||||
|
||||
return ListTile(
|
||||
leading: Icon(
|
||||
dnsRecord.isSatisfied
|
||||
? Icons.check_circle_outline
|
||||
: refreshing
|
||||
? Icons.refresh
|
||||
: Icons.error_outline,
|
||||
color: dnsRecord.isSatisfied
|
||||
? goodColor
|
||||
: refreshing
|
||||
? neutralColor
|
||||
: errorColor,
|
||||
),
|
||||
title: Text(dnsRecord.displayName ?? dnsRecord.name),
|
||||
subtitle: Text(
|
||||
dnsRecord.content,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,19 @@
|
|||
import 'package:auto_route/auto_route.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/api_maps/rest_maps/dns_providers/desired_dns_record.dart';
|
||||
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/get_it/resources_model.dart';
|
||||
import 'package:selfprivacy/ui/atoms/cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/atoms/icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/atoms/list_tiles/section_headline.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/molecules/cards/dns_state_card.dart';
|
||||
import 'package:selfprivacy/ui/molecules/list_items/dns_record_item.dart';
|
||||
import 'package:selfprivacy/utils/fake_data.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
|
||||
@RoutePage()
|
||||
class DnsDetailsPage extends StatefulWidget {
|
||||
|
@ -19,69 +24,6 @@ class DnsDetailsPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _DnsDetailsPageState extends State<DnsDetailsPage> {
|
||||
Widget _getStateCard(
|
||||
final DnsRecordsStatus dnsState,
|
||||
final Function fixCallback,
|
||||
) {
|
||||
String description = '';
|
||||
String subtitle = '';
|
||||
Icon icon = const Icon(
|
||||
Icons.check_circle_outline,
|
||||
size: 24.0,
|
||||
);
|
||||
bool isError = false;
|
||||
switch (dnsState) {
|
||||
case DnsRecordsStatus.uninitialized:
|
||||
description = 'domain.uninitialized'.tr();
|
||||
icon = const Icon(
|
||||
Icons.refresh,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = false;
|
||||
break;
|
||||
case DnsRecordsStatus.refreshing:
|
||||
description = 'domain.refreshing'.tr();
|
||||
icon = const Icon(
|
||||
Icons.refresh,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = false;
|
||||
break;
|
||||
case DnsRecordsStatus.good:
|
||||
description = 'domain.ok'.tr();
|
||||
icon = const Icon(
|
||||
Icons.check_circle_outline,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = false;
|
||||
break;
|
||||
case DnsRecordsStatus.error:
|
||||
description = 'domain.error'.tr();
|
||||
subtitle = 'domain.error_subtitle'.tr();
|
||||
icon = const Icon(
|
||||
Icons.error_outline,
|
||||
size: 24.0,
|
||||
);
|
||||
isError = true;
|
||||
break;
|
||||
}
|
||||
return FilledCard(
|
||||
error: isError,
|
||||
child: ListTile(
|
||||
onTap: dnsState == DnsRecordsStatus.error ? () => fixCallback() : null,
|
||||
leading: icon,
|
||||
title: Text(description),
|
||||
subtitle: subtitle != '' ? Text(subtitle) : null,
|
||||
textColor: isError
|
||||
? Theme.of(context).colorScheme.error
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
iconColor: isError
|
||||
? Theme.of(context).colorScheme.error
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final bool isReady = context.watch<ServerInstallationCubit>().state
|
||||
|
@ -89,6 +31,8 @@ class _DnsDetailsPageState extends State<DnsDetailsPage> {
|
|||
final String domain =
|
||||
getIt<ResourcesModel>().serverDomain?.domainName ?? '';
|
||||
final DnsRecordsState dnsCubit = context.watch<DnsRecordsCubit>().state;
|
||||
final List<DesiredDnsRecord> dnsRecords =
|
||||
context.watch<DnsRecordsCubit>().state.dnsRecords;
|
||||
|
||||
print(dnsCubit.dnsState);
|
||||
|
||||
|
@ -102,9 +46,35 @@ class _DnsDetailsPageState extends State<DnsDetailsPage> {
|
|||
);
|
||||
}
|
||||
|
||||
final Color goodColor = Theme.of(context).colorScheme.onSurface;
|
||||
final Color errorColor = Theme.of(context).colorScheme.error;
|
||||
final Color neutralColor = Theme.of(context).colorScheme.onSurface;
|
||||
final recordsToShow =
|
||||
dnsRecords.isEmpty ? FakeSelfPrivacyData.desiredDnsRecords : dnsRecords;
|
||||
final refreshing =
|
||||
dnsCubit.dnsState == DnsRecordsStatus.refreshing || dnsRecords.isEmpty;
|
||||
|
||||
List<Widget> recordsSection(
|
||||
final String title,
|
||||
final String subtitle,
|
||||
final DnsRecordsCategory category,
|
||||
) =>
|
||||
[
|
||||
SectionHeadline(
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
),
|
||||
...recordsToShow
|
||||
.where(
|
||||
(final dnsRecord) => dnsRecord.category == category,
|
||||
)
|
||||
.map(
|
||||
(final dnsRecord) => Skeletonizer(
|
||||
enabled: refreshing,
|
||||
child: DnsRecordItem(
|
||||
dnsRecord: dnsRecord,
|
||||
refreshing: refreshing,
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
return BrandHeroScreen(
|
||||
hasBackButton: true,
|
||||
|
@ -112,132 +82,30 @@ class _DnsDetailsPageState extends State<DnsDetailsPage> {
|
|||
heroIcon: BrandIcons.globe,
|
||||
heroTitle: 'domain.screen_title'.tr(),
|
||||
children: <Widget>[
|
||||
_getStateCard(
|
||||
dnsCubit.dnsState,
|
||||
() {
|
||||
DnsStateCard(
|
||||
dnsState: dnsCubit.dnsState,
|
||||
fixCallback: () {
|
||||
context.read<DnsRecordsCubit>().fix();
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'domain.services_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
'domain.services_subtitle'.tr(),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
const Gap(8.0),
|
||||
...recordsSection(
|
||||
'domain.services_title'.tr(),
|
||||
'domain.services_subtitle'.tr(),
|
||||
DnsRecordsCategory.services,
|
||||
),
|
||||
...dnsCubit.dnsRecords
|
||||
.where(
|
||||
(final dnsRecord) =>
|
||||
dnsRecord.category == DnsRecordsCategory.services,
|
||||
)
|
||||
.map(
|
||||
(final dnsRecord) => Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
dnsRecord.isSatisfied
|
||||
? Icons.check_circle_outline
|
||||
: dnsCubit.dnsState == DnsRecordsStatus.refreshing
|
||||
? Icons.refresh
|
||||
: Icons.error_outline,
|
||||
color: dnsRecord.isSatisfied
|
||||
? goodColor
|
||||
: dnsCubit.dnsState == DnsRecordsStatus.refreshing
|
||||
? neutralColor
|
||||
: errorColor,
|
||||
),
|
||||
title: Text(dnsRecord.displayName ?? dnsRecord.name),
|
||||
subtitle: Text(dnsRecord.content),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'domain.email_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
'domain.email_subtitle'.tr(),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
const Gap(8.0),
|
||||
...recordsSection(
|
||||
'domain.email_title'.tr(),
|
||||
'domain.email_subtitle'.tr(),
|
||||
DnsRecordsCategory.email,
|
||||
),
|
||||
...dnsCubit.dnsRecords
|
||||
.where(
|
||||
(final dnsRecord) =>
|
||||
dnsRecord.category == DnsRecordsCategory.email,
|
||||
)
|
||||
.map(
|
||||
(final dnsRecord) => Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
dnsRecord.isSatisfied
|
||||
? Icons.check_circle_outline
|
||||
: dnsCubit.dnsState == DnsRecordsStatus.refreshing
|
||||
? Icons.refresh
|
||||
: Icons.error_outline,
|
||||
color: dnsRecord.isSatisfied
|
||||
? goodColor
|
||||
: dnsCubit.dnsState == DnsRecordsStatus.refreshing
|
||||
? neutralColor
|
||||
: errorColor,
|
||||
),
|
||||
title: Text(dnsRecord.displayName ?? dnsRecord.name),
|
||||
subtitle: Text(dnsRecord.name),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'domain.other_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
'domain.other_subtitle'.tr(),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
const Gap(8.0),
|
||||
...recordsSection(
|
||||
'domain.other_title'.tr(),
|
||||
'domain.other_subtitle'.tr(),
|
||||
DnsRecordsCategory.other,
|
||||
),
|
||||
...dnsCubit.dnsRecords
|
||||
.where(
|
||||
(final dnsRecord) =>
|
||||
dnsRecord.category == DnsRecordsCategory.other,
|
||||
)
|
||||
.map(
|
||||
(final dnsRecord) => Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
dnsRecord.isSatisfied
|
||||
? Icons.check_circle_outline
|
||||
: dnsCubit.dnsState == DnsRecordsStatus.refreshing
|
||||
? Icons.refresh
|
||||
: Icons.error_outline,
|
||||
color: dnsRecord.isSatisfied
|
||||
? goodColor
|
||||
: dnsCubit.dnsState == DnsRecordsStatus.refreshing
|
||||
? neutralColor
|
||||
: errorColor,
|
||||
),
|
||||
title: Text(dnsRecord.displayName ?? dnsRecord.name),
|
||||
subtitle: Text(dnsRecord.content),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
31
lib/utils/fake_data.dart
Normal file
31
lib/utils/fake_data.dart
Normal file
|
@ -0,0 +1,31 @@
|
|||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart';
|
||||
|
||||
/// Fake data collections to fill skeletons
|
||||
class FakeSelfPrivacyData {
|
||||
static final List<DesiredDnsRecord> desiredDnsRecords = <DesiredDnsRecord>[
|
||||
...List.generate(
|
||||
6,
|
||||
(final index) => const DesiredDnsRecord(
|
||||
type: 'A',
|
||||
name: 'example.com',
|
||||
description: 'Service name',
|
||||
content: '192.0.2.100',
|
||||
),
|
||||
),
|
||||
...List.generate(
|
||||
4,
|
||||
(final index) => const DesiredDnsRecord(
|
||||
type: 'TXT',
|
||||
name: 'example.com',
|
||||
description: 'Service name',
|
||||
content: 'Some text text text',
|
||||
),
|
||||
),
|
||||
const DesiredDnsRecord(
|
||||
type: 'CAA',
|
||||
name: 'example.com',
|
||||
description: 'Service name',
|
||||
content: 'Some very very long text text text',
|
||||
),
|
||||
];
|
||||
}
|
12
pubspec.lock
12
pubspec.lock
|
@ -1186,6 +1186,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
skeletonizer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: skeletonizer
|
||||
sha256: "3b202e4fa9c49b017d368fb0e570d4952bcd19972b67b2face071bdd68abbfae"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -1403,10 +1411,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.5"
|
||||
version: "14.2.4"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -50,6 +50,7 @@ dependencies:
|
|||
pretty_dio_logger: ^1.4.0
|
||||
provider: ^6.1.2
|
||||
pub_semver: ^2.1.4
|
||||
skeletonizer: ^1.4.2
|
||||
timezone: ^0.9.4
|
||||
url_launcher: ^6.3.0
|
||||
# wakelock: ^0.6.2 # TODO: Developer is not available, update later.
|
||||
|
|
Loading…
Reference in a new issue