mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-11 18:39:45 +00:00
Introduce ListTileOnSurfaceVariant and refactor TextDetails on server details screen.
This commit is contained in:
parent
3d34f0bb55
commit
5ca4ee27e3
|
@ -114,6 +114,19 @@
|
||||||
"reboot_after_upgrade_hint": "Reboot without prompt after applying changes on server",
|
"reboot_after_upgrade_hint": "Reboot without prompt after applying changes on server",
|
||||||
"server_timezone": "Server timezone",
|
"server_timezone": "Server timezone",
|
||||||
"select_timezone": "Select timezone"
|
"select_timezone": "Select timezone"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"server_id": "Server ID",
|
||||||
|
"status": "Status",
|
||||||
|
"cpu": "CPU",
|
||||||
|
"ram": "Memory",
|
||||||
|
"disk": "Disk local",
|
||||||
|
"monthly_cost": "Monthly cost",
|
||||||
|
"location": "Location",
|
||||||
|
"core_count": {
|
||||||
|
"one": "{} core",
|
||||||
|
"other": "{} cores"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
|
|
|
@ -113,6 +113,22 @@
|
||||||
"reboot_after_upgrade_hint": "Автоматически перезагружать сервер после применения обновлений",
|
"reboot_after_upgrade_hint": "Автоматически перезагружать сервер после применения обновлений",
|
||||||
"server_timezone": "Часовой пояс сервера",
|
"server_timezone": "Часовой пояс сервера",
|
||||||
"select_timezone": "Выберите часовой пояс"
|
"select_timezone": "Выберите часовой пояс"
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"server_id": "ID сервера",
|
||||||
|
"status": "Статус",
|
||||||
|
"cpu": "Процессор",
|
||||||
|
"ram": "Операивная память",
|
||||||
|
"disk": "Диск",
|
||||||
|
"monthly_cost": "Ежемесячная стоимость",
|
||||||
|
"location": "Размещение",
|
||||||
|
"core_count": {
|
||||||
|
"one": "{} ядро",
|
||||||
|
"two": "{} ядра",
|
||||||
|
"few": "{} ядра",
|
||||||
|
"many": "{} ядер",
|
||||||
|
"other": "{} ядер"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ListTileOnSurfaceVariant extends StatelessWidget {
|
||||||
|
const ListTileOnSurfaceVariant({
|
||||||
|
required this.title,
|
||||||
|
this.subtitle,
|
||||||
|
this.leadingIcon,
|
||||||
|
this.onTap,
|
||||||
|
this.disableSubtitleOverflow = false,
|
||||||
|
final super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String? subtitle;
|
||||||
|
final IconData? leadingIcon;
|
||||||
|
final Function()? onTap;
|
||||||
|
final bool disableSubtitleOverflow;
|
||||||
|
|
||||||
|
Widget? getSubtitle() {
|
||||||
|
if (subtitle == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (disableSubtitleOverflow) {
|
||||||
|
return Text(
|
||||||
|
subtitle!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Text(
|
||||||
|
subtitle!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => ListTile(
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: getSubtitle(),
|
||||||
|
onTap: onTap,
|
||||||
|
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
leading: leadingIcon != null ? Icon(leadingIcon) : null,
|
||||||
|
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
);
|
||||||
|
}
|
|
@ -15,10 +15,12 @@ import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.da
|
||||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
|
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart';
|
||||||
import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart';
|
import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart';
|
import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_details/time_zone/lang.dart';
|
import 'package:selfprivacy/ui/pages/server_details/time_zone/lang.dart';
|
||||||
import 'package:selfprivacy/utils/extensions/duration.dart';
|
import 'package:selfprivacy/utils/extensions/duration.dart';
|
||||||
|
import 'package:selfprivacy/utils/extensions/string_extensions.dart';
|
||||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
import 'package:timezone/timezone.dart';
|
import 'package:timezone/timezone.dart';
|
||||||
|
|
|
@ -11,115 +11,53 @@ class _TextDetails extends StatelessWidget {
|
||||||
return _TempMessage(message: 'basis.no_data'.tr());
|
return _TempMessage(message: 'basis.no_data'.tr());
|
||||||
} else if (details is Loaded) {
|
} else if (details is Loaded) {
|
||||||
final data = details.serverInfo;
|
final data = details.serverInfo;
|
||||||
final checkTime = details.checkTime;
|
return FilledCard(
|
||||||
return Column(
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Center(child: BrandText.h3('providers.server.bottom_sheet.2'.tr())),
|
children: [
|
||||||
const SizedBox(height: 10),
|
Padding(
|
||||||
Table(
|
padding: const EdgeInsets.all(16.0),
|
||||||
columnWidths: const {
|
child: Text(
|
||||||
0: FractionColumnWidth(.5),
|
'providers.server.bottom_sheet.2'.tr(),
|
||||||
1: FractionColumnWidth(.5),
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||||
},
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
),
|
||||||
children: [
|
|
||||||
TableRow(
|
|
||||||
children: [
|
|
||||||
getRowTitle('Last check:'),
|
|
||||||
getRowValue(formatter.format(checkTime)),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
TableRow(
|
),
|
||||||
children: [
|
ListTileOnSurfaceVariant(
|
||||||
getRowTitle('Server Id:'),
|
leadingIcon: Icons.numbers_outlined,
|
||||||
getRowValue(data.id.toString()),
|
title: data.id.toString(),
|
||||||
],
|
subtitle: 'providers.server.info.server_id'.tr(),
|
||||||
),
|
),
|
||||||
TableRow(
|
ListTileOnSurfaceVariant(
|
||||||
children: [
|
leadingIcon: Icons.mode_standby_outlined,
|
||||||
getRowTitle('Status:'),
|
title: data.status.toString().split('.')[1].capitalize(),
|
||||||
getRowValue(
|
subtitle: 'providers.server.info.status'.tr(),
|
||||||
data.status.toString().split('.')[1].toUpperCase(),
|
),
|
||||||
isBold: true,
|
ListTileOnSurfaceVariant(
|
||||||
),
|
leadingIcon: Icons.memory_outlined,
|
||||||
],
|
title: 'providers.server.info.core_count'
|
||||||
),
|
.plural(data.serverType.cores),
|
||||||
TableRow(
|
subtitle: 'providers.server.info.cpu'.tr(),
|
||||||
children: [
|
),
|
||||||
getRowTitle('CPU:'),
|
ListTileOnSurfaceVariant(
|
||||||
getRowValue(
|
leadingIcon: Icons.memory_outlined,
|
||||||
data.serverType.cores.toString(),
|
title: '${data.serverType.memory.toString()} GB',
|
||||||
),
|
subtitle: 'providers.server.info.ram'.tr(),
|
||||||
],
|
),
|
||||||
),
|
ListTileOnSurfaceVariant(
|
||||||
TableRow(
|
leadingIcon: Icons.euro_outlined,
|
||||||
children: [
|
title: data.serverType.prices[1].monthly.toStringAsFixed(2),
|
||||||
getRowTitle('Memory:'),
|
subtitle: 'providers.server.info.monthly_cost'.tr(),
|
||||||
getRowValue(
|
),
|
||||||
'${data.serverType.memory.toString()} GB',
|
// Server location
|
||||||
),
|
ListTileOnSurfaceVariant(
|
||||||
],
|
leadingIcon: Icons.location_on_outlined,
|
||||||
),
|
title: '${data.location.city}, ${data.location.country}',
|
||||||
TableRow(
|
subtitle: 'providers.server.info.location'.tr(),
|
||||||
children: [
|
),
|
||||||
getRowTitle('Disk Local:'),
|
],
|
||||||
getRowValue(
|
),
|
||||||
'${data.serverType.disk.toString()} GB',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
TableRow(
|
|
||||||
children: [
|
|
||||||
getRowTitle('Price monthly:'),
|
|
||||||
getRowValue(
|
|
||||||
data.serverType.prices[1].monthly.toString(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
TableRow(
|
|
||||||
children: [
|
|
||||||
getRowTitle('Price hourly:'),
|
|
||||||
getRowValue(
|
|
||||||
data.serverType.prices[1].hourly.toString(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Center(child: BrandText.h3('providers.server.bottom_sheet.3'.tr())),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Table(
|
|
||||||
columnWidths: const {
|
|
||||||
0: FractionColumnWidth(.5),
|
|
||||||
1: FractionColumnWidth(.5),
|
|
||||||
},
|
|
||||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
|
||||||
children: [
|
|
||||||
TableRow(
|
|
||||||
children: [
|
|
||||||
getRowTitle('Country:'),
|
|
||||||
getRowValue(
|
|
||||||
data.location.country,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
TableRow(
|
|
||||||
children: [
|
|
||||||
getRowTitle('City:'),
|
|
||||||
getRowValue(data.location.city),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
TableRow(
|
|
||||||
children: [
|
|
||||||
getRowTitle('Description:'),
|
|
||||||
getRowValue(data.location.description),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw Exception('wrong state');
|
throw Exception('wrong state');
|
||||||
|
|
|
@ -133,12 +133,10 @@ class _UserLogins extends StatelessWidget {
|
||||||
Widget build(final BuildContext context) => FilledCard(
|
Widget build(final BuildContext context) => FilledCard(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTileOnSurfaceVariant(
|
||||||
title: Text('${user.login}@$domainName'),
|
title: '${user.login}@$domainName',
|
||||||
subtitle: Text('users.email_login'.tr()),
|
subtitle: 'users.email_login'.tr(),
|
||||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
leadingIcon: Icons.alternate_email_outlined,
|
||||||
leading: const Icon(Icons.alternate_email_outlined),
|
|
||||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -156,18 +154,13 @@ class _SshKeysCard extends StatelessWidget {
|
||||||
Widget build(final BuildContext context) => FilledCard(
|
Widget build(final BuildContext context) => FilledCard(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTileOnSurfaceVariant(
|
||||||
title: Text('ssh.title'.tr()),
|
title: 'ssh.title'.tr(),
|
||||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
),
|
||||||
const Divider(height: 0),
|
const Divider(height: 0),
|
||||||
ListTile(
|
ListTileOnSurfaceVariant(
|
||||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
title: 'ssh.create'.tr(),
|
||||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
leadingIcon: Icons.add_circle_outline,
|
||||||
title: Text(
|
|
||||||
'ssh.create'.tr(),
|
|
||||||
),
|
|
||||||
leading: const Icon(Icons.add_circle_outlined),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet<void>(
|
showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -188,15 +181,11 @@ class _SshKeysCard extends StatelessWidget {
|
||||||
final keyName = key.split(' ').length > 2
|
final keyName = key.split(' ').length > 2
|
||||||
? key.split(' ')[2]
|
? key.split(' ')[2]
|
||||||
: 'ssh.no_key_name'.tr();
|
: 'ssh.no_key_name'.tr();
|
||||||
return ListTile(
|
return ListTileOnSurfaceVariant(
|
||||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
title: '$keyName ($keyType)',
|
||||||
title: Text('$keyName ($keyType)'),
|
disableSubtitleOverflow: true,
|
||||||
// do not overflow text
|
// do not overflow text
|
||||||
subtitle: Text(
|
subtitle: publicKey,
|
||||||
publicKey,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -20,6 +20,7 @@ import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.da
|
||||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
import 'package:selfprivacy/ui/components/info_box/info_box.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/components/not_ready_card/not_ready_card.dart';
|
||||||
import 'package:selfprivacy/utils/ui_helpers.dart';
|
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||||
|
|
||||||
|
|
4
lib/utils/extensions/string_extensions.dart
Normal file
4
lib/utils/extensions/string_extensions.dart
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
extension StringExtension on String {
|
||||||
|
String capitalize() =>
|
||||||
|
'${this[0].toUpperCase()}${substring(1).toLowerCase()}';
|
||||||
|
}
|
Loading…
Reference in a new issue