mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-08 17:11:14 +00:00
refactor(ui): Refactor Devices screen UI
This commit is contained in:
parent
1168b2219f
commit
c17d87c348
|
@ -249,7 +249,7 @@
|
||||||
"autobackup_period_disable": "Disable automatic backups",
|
"autobackup_period_disable": "Disable automatic backups",
|
||||||
"autobackup_set_period": "Set period",
|
"autobackup_set_period": "Set period",
|
||||||
"backups_encryption_key": "Encryption key",
|
"backups_encryption_key": "Encryption key",
|
||||||
"backups_encryption_key_subtitle": "Keep it in a safe place.",
|
"backups_encryption_key_subtitle": "Keep it in a safe place",
|
||||||
"backups_encryption_key_copy": "Copy the encryption key",
|
"backups_encryption_key_copy": "Copy the encryption key",
|
||||||
"backups_encryption_key_show": "Show the encryption key",
|
"backups_encryption_key_show": "Show the encryption key",
|
||||||
"backups_encryption_key_description": "This key is used to encrypt your backups. If you lose it, you will not be able to restore your backups. Keep it in a safe place, as it will be useful if you ever need to restore from backups manually.",
|
"backups_encryption_key_description": "This key is used to encrypt your backups. If you lose it, you will not be able to restore your backups. Keep it in a safe place, as it will be useful if you ever need to restore from backups manually.",
|
||||||
|
@ -598,7 +598,6 @@
|
||||||
"key_replace_button": "Generate new key",
|
"key_replace_button": "Generate new key",
|
||||||
"key_receiving_description": "Write down this key and put to a safe place. It is used to restore full access to your server:",
|
"key_receiving_description": "Write down this key and put to a safe place. It is used to restore full access to your server:",
|
||||||
"key_receiving_info": "The key will never ever be shown again, but you will be able to replace it with another one.",
|
"key_receiving_info": "The key will never ever be shown again, but you will be able to replace it with another one.",
|
||||||
"key_receiving_done": "Done!",
|
|
||||||
"generation_error": "Couldn't generate a recovery key. {}"
|
"generation_error": "Couldn't generate a recovery key. {}"
|
||||||
},
|
},
|
||||||
"modals": {
|
"modals": {
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SectionHeadline extends StatelessWidget {
|
class SectionHeadline extends StatelessWidget {
|
||||||
const SectionHeadline({
|
const SectionHeadline({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.subtitle,
|
this.subtitle,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final String subtitle;
|
final String? subtitle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) => ListTile(
|
Widget build(final BuildContext context) => ListTile(
|
||||||
|
@ -19,9 +18,11 @@ class SectionHeadline extends StatelessWidget {
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: subtitle != null
|
||||||
subtitle,
|
? Text(
|
||||||
style: Theme.of(context).textTheme.labelMedium,
|
subtitle!,
|
||||||
),
|
style: Theme.of(context).textTheme.labelMedium,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,10 @@ class SectionTitle extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) => Padding(
|
Widget build(final BuildContext context) => Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 8.0,
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
||||||
|
|
|
@ -19,7 +19,10 @@ class BrandHeroScreen extends StatelessWidget {
|
||||||
this.heroTitle = '',
|
this.heroTitle = '',
|
||||||
this.heroSubtitle,
|
this.heroSubtitle,
|
||||||
this.onBackButtonPressed,
|
this.onBackButtonPressed,
|
||||||
this.bodyPadding = const EdgeInsets.all(16.0),
|
this.bodyPadding = const EdgeInsets.symmetric(
|
||||||
|
vertical: 16.0,
|
||||||
|
horizontal: 8.0,
|
||||||
|
),
|
||||||
this.ignoreBreakpoints = false,
|
this.ignoreBreakpoints = false,
|
||||||
this.hasSupportDrawer = false,
|
this.hasSupportDrawer = false,
|
||||||
});
|
});
|
||||||
|
@ -80,7 +83,7 @@ class BrandHeroScreen extends StatelessWidget {
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
textAlign: hasHeroIcon ? TextAlign.center : TextAlign.start,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
|
131
lib/ui/molecules/list_items/device_item.dart
Normal file
131
lib/ui/molecules/list_items/device_item.dart
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart';
|
||||||
|
import 'package:selfprivacy/logic/bloc/tokens/tokens_bloc.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
||||||
|
|
||||||
|
class DeviceItem extends StatelessWidget {
|
||||||
|
const DeviceItem({
|
||||||
|
required this.device,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ApiToken device;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => ListTile(
|
||||||
|
title: Text(device.name),
|
||||||
|
subtitle: Text(
|
||||||
|
'devices.main_screen.access_granted_on'
|
||||||
|
.tr(args: [DateFormat.yMMMMd().format(device.date)]),
|
||||||
|
),
|
||||||
|
onTap: device.isCaller
|
||||||
|
? () => _showTokenRefreshDialog(context, device)
|
||||||
|
: () => _showConfirmationDialog(context, device),
|
||||||
|
);
|
||||||
|
|
||||||
|
Future _showConfirmationDialog(
|
||||||
|
final BuildContext context,
|
||||||
|
final ApiToken device,
|
||||||
|
) =>
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (final context) => AlertDialog(
|
||||||
|
title: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.link_off_outlined),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'devices.revoke_device_alert.header'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
'devices.revoke_device_alert.description'
|
||||||
|
.tr(args: [device.name]),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text('devices.revoke_device_alert.no'.tr()),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text(
|
||||||
|
'devices.revoke_device_alert.yes'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
context.read<DevicesBloc>().add(DeleteDevice(device));
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Future _showTokenRefreshDialog(
|
||||||
|
final BuildContext context,
|
||||||
|
final ApiToken device,
|
||||||
|
) =>
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (final context) => AlertDialog(
|
||||||
|
title: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.update_outlined),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'devices.refresh_token_alert.header'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
'devices.refresh_token_alert.description'
|
||||||
|
.tr(args: [device.name]),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: Text('devices.refresh_token_alert.no'.tr()),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text('devices.refresh_token_alert.yes'.tr()),
|
||||||
|
onPressed: () {
|
||||||
|
context
|
||||||
|
.read<TokensBloc>()
|
||||||
|
.add(const RefreshServerApiTokenEvent());
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
60
lib/ui/organisms/displays/key_display.dart
Normal file
60
lib/ui/organisms/displays/key_display.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/ui/atoms/buttons/brand_button.dart';
|
||||||
|
import 'package:selfprivacy/ui/molecules/info_box/info_box.dart';
|
||||||
|
|
||||||
|
class KeyDisplay extends StatelessWidget {
|
||||||
|
const KeyDisplay({
|
||||||
|
required this.keyToDisplay,
|
||||||
|
required this.canCopy,
|
||||||
|
required this.infoboxText,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String keyToDisplay;
|
||||||
|
final bool canCopy;
|
||||||
|
final String infoboxText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
if (canCopy)
|
||||||
|
SelectableText(
|
||||||
|
keyToDisplay,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
|
fontSize: 24,
|
||||||
|
fontFamily: 'RobotoMono',
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
if (!canCopy)
|
||||||
|
Text(
|
||||||
|
keyToDisplay,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
|
fontSize: 24,
|
||||||
|
fontFamily: 'RobotoMono',
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
InfoBox(
|
||||||
|
text: infoboxText,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
BrandButton.filled(
|
||||||
|
child: Text(
|
||||||
|
'basis.done'.tr(),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).popUntil((final route) => route.isFirst);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
|
@ -257,13 +257,8 @@ class BackupDetailsPage extends StatelessWidget {
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
SectionHeadline(
|
||||||
title: Text(
|
title: 'backup.pending_jobs'.tr(),
|
||||||
'backup.pending_jobs'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
for (final job in backupJobs)
|
for (final job in backupJobs)
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -312,7 +307,6 @@ class BackupDetailsPage extends StatelessWidget {
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
'backup.show_more'.tr(),
|
'backup.show_more'.tr(),
|
||||||
style: Theme.of(context).textTheme.labelMedium,
|
|
||||||
),
|
),
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.arrow_drop_down,
|
Icons.arrow_drop_down,
|
||||||
|
@ -345,6 +339,7 @@ class BackupDetailsPage extends StatelessWidget {
|
||||||
Icons.cached_outlined,
|
Icons.cached_outlined,
|
||||||
color: overrideColor,
|
color: overrideColor,
|
||||||
),
|
),
|
||||||
|
isThreeLine: true,
|
||||||
onTap: preventActions
|
onTap: preventActions
|
||||||
? null
|
? null
|
||||||
: () => context
|
: () => context
|
||||||
|
|
|
@ -3,23 +3,22 @@ import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/tokens/tokens_bloc.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
import 'package:selfprivacy/ui/atoms/list_tiles/section_title.dart';
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
import 'package:selfprivacy/ui/molecules/info_box/info_box.dart';
|
import 'package:selfprivacy/ui/molecules/info_box/info_box.dart';
|
||||||
import 'package:selfprivacy/ui/pages/devices/new_device.dart';
|
import 'package:selfprivacy/ui/molecules/list_items/device_item.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/ui/router/router.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class DevicesScreen extends StatefulWidget {
|
class DevicesPage extends StatefulWidget {
|
||||||
const DevicesScreen({super.key});
|
const DevicesPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DevicesScreen> createState() => _DevicesScreenState();
|
State<DevicesPage> createState() => _DevicesPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DevicesScreenState extends State<DevicesScreen> {
|
class _DevicesPageState extends State<DevicesPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final DevicesState devicesStatus = context.watch<DevicesBloc>().state;
|
final DevicesState devicesStatus = context.watch<DevicesBloc>().state;
|
||||||
|
@ -31,6 +30,7 @@ class _DevicesScreenState extends State<DevicesScreen> {
|
||||||
child: BrandHeroScreen(
|
child: BrandHeroScreen(
|
||||||
heroTitle: 'devices.main_screen.header'.tr(),
|
heroTitle: 'devices.main_screen.header'.tr(),
|
||||||
heroSubtitle: 'devices.main_screen.description'.tr(),
|
heroSubtitle: 'devices.main_screen.description'.tr(),
|
||||||
|
heroIcon: Icons.devices_outlined,
|
||||||
hasBackButton: true,
|
hasBackButton: true,
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
children: [
|
children: [
|
||||||
|
@ -46,8 +46,7 @@ class _DevicesScreenState extends State<DevicesScreen> {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onPressed: () => Navigator.of(context)
|
onPressed: () => context.pushRoute(const NewDeviceRoute()),
|
||||||
.push(materialRoute(const NewDeviceScreen())),
|
|
||||||
child: Text('devices.main_screen.authorize_new_device'.tr()),
|
child: Text('devices.main_screen.authorize_new_device'.tr()),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
@ -75,22 +74,17 @@ class _DevicesInfo extends StatelessWidget {
|
||||||
Widget build(final BuildContext context) => Column(
|
Widget build(final BuildContext context) => Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
SectionTitle(
|
||||||
'devices.main_screen.this_device'.tr(),
|
title: 'devices.main_screen.this_device'.tr(),
|
||||||
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_DeviceTile(
|
DeviceItem(
|
||||||
device: devicesStatus.thisDevice,
|
device: devicesStatus.thisDevice,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 8),
|
||||||
Text(
|
SectionTitle(
|
||||||
'devices.main_screen.other_devices'.tr(),
|
title: 'devices.main_screen.other_devices'.tr(),
|
||||||
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (devicesStatus is DevicesDeleting) ...[
|
if (devicesStatus is DevicesDeleting) ...[
|
||||||
const Center(
|
const Center(
|
||||||
|
@ -100,127 +94,10 @@ class _DevicesInfo extends StatelessWidget {
|
||||||
],
|
],
|
||||||
if (devicesStatus is! DevicesDeleting)
|
if (devicesStatus is! DevicesDeleting)
|
||||||
...devicesStatus.otherDevices.map(
|
...devicesStatus.otherDevices.map(
|
||||||
(final device) => _DeviceTile(
|
(final device) => DeviceItem(
|
||||||
device: device,
|
device: device,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DeviceTile extends StatelessWidget {
|
|
||||||
const _DeviceTile({required this.device});
|
|
||||||
|
|
||||||
final ApiToken device;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(final BuildContext context) => ListTile(
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
title: Text(device.name),
|
|
||||||
subtitle: Text(
|
|
||||||
'devices.main_screen.access_granted_on'
|
|
||||||
.tr(args: [DateFormat.yMMMMd().format(device.date)]),
|
|
||||||
),
|
|
||||||
onTap: device.isCaller
|
|
||||||
? () => _showTokenRefreshDialog(context, device)
|
|
||||||
: () => _showConfirmationDialog(context, device),
|
|
||||||
);
|
|
||||||
|
|
||||||
Future _showConfirmationDialog(
|
|
||||||
final BuildContext context,
|
|
||||||
final ApiToken device,
|
|
||||||
) =>
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (final context) => AlertDialog(
|
|
||||||
title: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.link_off_outlined),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
'devices.revoke_device_alert.header'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
'devices.revoke_device_alert.description'
|
|
||||||
.tr(args: [device.name]),
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
child: Text('devices.revoke_device_alert.no'.tr()),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: Text('devices.revoke_device_alert.yes'.tr()),
|
|
||||||
onPressed: () {
|
|
||||||
context.read<DevicesBloc>().add(DeleteDevice(device));
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Future _showTokenRefreshDialog(
|
|
||||||
final BuildContext context,
|
|
||||||
final ApiToken device,
|
|
||||||
) =>
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (final context) => AlertDialog(
|
|
||||||
title: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.update_outlined),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
'devices.refresh_token_alert.header'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
'devices.refresh_token_alert.description'
|
|
||||||
.tr(args: [device.name]),
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
child: Text('devices.refresh_token_alert.no'.tr()),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: Text('devices.refresh_token_alert.yes'.tr()),
|
|
||||||
onPressed: () {
|
|
||||||
context
|
|
||||||
.read<TokensBloc>()
|
|
||||||
.add(const RefreshServerApiTokenEvent());
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart';
|
import 'package:selfprivacy/logic/bloc/devices/devices_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/ui/atoms/buttons/brand_button.dart';
|
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
|
import 'package:selfprivacy/ui/organisms/displays/key_display.dart';
|
||||||
|
|
||||||
class NewDeviceScreen extends StatelessWidget {
|
@RoutePage()
|
||||||
const NewDeviceScreen({super.key});
|
class NewDevicePage extends StatelessWidget {
|
||||||
|
const NewDevicePage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) => BrandHeroScreen(
|
Widget build(final BuildContext context) => BrandHeroScreen(
|
||||||
|
@ -23,8 +25,10 @@ class NewDeviceScreen extends StatelessWidget {
|
||||||
final AsyncSnapshot<Object?> snapshot,
|
final AsyncSnapshot<Object?> snapshot,
|
||||||
) {
|
) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return _KeyDisplay(
|
return KeyDisplay(
|
||||||
newDeviceKey: snapshot.data.toString(),
|
keyToDisplay: snapshot.data.toString(),
|
||||||
|
canCopy: true,
|
||||||
|
infoboxText: 'devices.add_new_device_screen.tip'.tr(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const Center(
|
return const Center(
|
||||||
|
@ -36,51 +40,3 @@ class NewDeviceScreen extends StatelessWidget {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _KeyDisplay extends StatelessWidget {
|
|
||||||
const _KeyDisplay({required this.newDeviceKey});
|
|
||||||
|
|
||||||
final String newDeviceKey;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(final BuildContext context) => Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
SelectableText(
|
|
||||||
newDeviceKey,
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
|
||||||
fontSize: 24,
|
|
||||||
fontFamily: 'RobotoMono',
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.info_outline,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
'devices.add_new_device_screen.tip'.tr(),
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
BrandButton.filled(
|
|
||||||
child: Text(
|
|
||||||
'basis.done'.tr(),
|
|
||||||
),
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ class AboutApplicationPage extends StatelessWidget {
|
||||||
hasBackButton: true,
|
hasBackButton: true,
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
heroTitle: 'about_application_page.title'.tr(),
|
heroTitle: 'about_application_page.title'.tr(),
|
||||||
bodyPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
children: [
|
children: [
|
||||||
SectionTitle(title: 'about_application_page.versions'.tr()),
|
SectionTitle(title: 'about_application_page.versions'.tr()),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
|
|
|
@ -25,7 +25,6 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
|
||||||
Widget build(final BuildContext context) => BrandHeroScreen(
|
Widget build(final BuildContext context) => BrandHeroScreen(
|
||||||
hasBackButton: true,
|
hasBackButton: true,
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
bodyPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
heroTitle: 'developer_settings.title'.tr(),
|
heroTitle: 'developer_settings.title'.tr(),
|
||||||
heroSubtitle: 'developer_settings.subtitle'.tr(),
|
heroSubtitle: 'developer_settings.subtitle'.tr(),
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -250,7 +250,7 @@ class _RecoveryKeyConfigurationState extends State<RecoveryKeyConfiguration> {
|
||||||
});
|
});
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
materialRoute(
|
materialRoute(
|
||||||
RecoveryKeyReceiving(recoveryKey: token),
|
NewRecoveryKeyPage(recoveryKey: token),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} on GenerationError catch (e) {
|
} on GenerationError catch (e) {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/ui/atoms/buttons/brand_button.dart';
|
|
||||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||||
import 'package:selfprivacy/ui/molecules/info_box/info_box.dart';
|
import 'package:selfprivacy/ui/organisms/displays/key_display.dart';
|
||||||
|
|
||||||
class RecoveryKeyReceiving extends StatelessWidget {
|
class NewRecoveryKeyPage extends StatelessWidget {
|
||||||
const RecoveryKeyReceiving({required this.recoveryKey, super.key});
|
const NewRecoveryKeyPage({required this.recoveryKey, super.key});
|
||||||
|
|
||||||
final String recoveryKey;
|
final String recoveryKey;
|
||||||
|
|
||||||
|
@ -16,28 +15,10 @@ class RecoveryKeyReceiving extends StatelessWidget {
|
||||||
hasBackButton: false,
|
hasBackButton: false,
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
children: [
|
children: [
|
||||||
const Divider(),
|
KeyDisplay(
|
||||||
const SizedBox(height: 16),
|
keyToDisplay: recoveryKey,
|
||||||
Text(
|
canCopy: false,
|
||||||
recoveryKey,
|
infoboxText: 'recovery_key.key_receiving_info'.tr(),
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
|
||||||
fontSize: 24,
|
|
||||||
fontFamily: 'RobotoMono',
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Divider(),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
InfoBox(
|
|
||||||
text: 'recovery_key.key_receiving_info'.tr(),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
BrandButton.filled(
|
|
||||||
child: Text('recovery_key.key_receiving_done'.tr()),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).popUntil((final route) => route.isFirst);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:selfprivacy/logic/models/service.dart';
|
||||||
import 'package:selfprivacy/ui/pages/backups/backup_details.dart';
|
import 'package:selfprivacy/ui/pages/backups/backup_details.dart';
|
||||||
import 'package:selfprivacy/ui/pages/backups/backups_list.dart';
|
import 'package:selfprivacy/ui/pages/backups/backups_list.dart';
|
||||||
import 'package:selfprivacy/ui/pages/devices/devices.dart';
|
import 'package:selfprivacy/ui/pages/devices/devices.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/devices/new_device.dart';
|
||||||
import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart';
|
import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart';
|
||||||
import 'package:selfprivacy/ui/pages/more/about_application.dart';
|
import 'package:selfprivacy/ui/pages/more/about_application.dart';
|
||||||
import 'package:selfprivacy/ui/pages/more/app_settings/app_settings.dart';
|
import 'package:selfprivacy/ui/pages/more/app_settings/app_settings.dart';
|
||||||
|
@ -101,6 +102,7 @@ class RootRouter extends RootStackRouter {
|
||||||
AutoRoute(page: NewUserRoute.page),
|
AutoRoute(page: NewUserRoute.page),
|
||||||
AutoRoute(page: RecoveryKeyRoute.page),
|
AutoRoute(page: RecoveryKeyRoute.page),
|
||||||
AutoRoute(page: DevicesRoute.page),
|
AutoRoute(page: DevicesRoute.page),
|
||||||
|
AutoRoute(page: NewDeviceRoute.page),
|
||||||
AutoRoute(page: AboutApplicationRoute.page),
|
AutoRoute(page: AboutApplicationRoute.page),
|
||||||
AutoRoute(page: DeveloperSettingsRoute.page),
|
AutoRoute(page: DeveloperSettingsRoute.page),
|
||||||
AutoRoute(page: ServiceRoute.page),
|
AutoRoute(page: ServiceRoute.page),
|
||||||
|
|
|
@ -198,7 +198,7 @@ class DeveloperSettingsRoute extends PageRouteInfo<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [DevicesScreen]
|
/// [DevicesPage]
|
||||||
class DevicesRoute extends PageRouteInfo<void> {
|
class DevicesRoute extends PageRouteInfo<void> {
|
||||||
const DevicesRoute({List<PageRouteInfo>? children})
|
const DevicesRoute({List<PageRouteInfo>? children})
|
||||||
: super(
|
: super(
|
||||||
|
@ -211,7 +211,7 @@ class DevicesRoute extends PageRouteInfo<void> {
|
||||||
static PageInfo page = PageInfo(
|
static PageInfo page = PageInfo(
|
||||||
name,
|
name,
|
||||||
builder: (data) {
|
builder: (data) {
|
||||||
return const DevicesScreen();
|
return const DevicesPage();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -344,6 +344,25 @@ class MoreRoute extends PageRouteInfo<void> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [NewDevicePage]
|
||||||
|
class NewDeviceRoute extends PageRouteInfo<void> {
|
||||||
|
const NewDeviceRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(
|
||||||
|
NewDeviceRoute.name,
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'NewDeviceRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const NewDevicePage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [NewUserPage]
|
/// [NewUserPage]
|
||||||
class NewUserRoute extends PageRouteInfo<void> {
|
class NewUserRoute extends PageRouteInfo<void> {
|
||||||
|
|
Loading…
Reference in a new issue