refactor(router): Move more pages to new router

pull/203/head
Inex Code 2023-02-24 19:45:32 +03:00 committed by Gitea
parent 999c8346be
commit ee9b8a4e31
17 changed files with 500 additions and 267 deletions

View File

@ -255,6 +255,7 @@
"subtitle": "Private VPN server" "subtitle": "Private VPN server"
}, },
"users": { "users": {
"details_title": "User details",
"add_new_user": "Add a first user", "add_new_user": "Add a first user",
"new_user": "New user", "new_user": "New user",
"delete_user": "Delete user", "delete_user": "Delete user",

View File

@ -25,5 +25,8 @@ class BrandHeader extends StatelessWidget {
onBackButtonPressed ?? () => Navigator.of(context).pop(), onBackButtonPressed ?? () => Navigator.of(context).pop(),
) )
: null, : null,
actions: const [
SizedBox.shrink(),
],
); );
} }

View File

@ -53,9 +53,10 @@ class RootScaffoldWithNavigation extends StatelessWidget {
hidden: !(Breakpoints.small.isActive(context) && showBottomBar), hidden: !(Breakpoints.small.isActive(context) && showBottomBar),
key: const Key('bottomBar'), key: const Key('bottomBar'),
), ),
floatingActionButton: showFab && Breakpoints.small.isActive(context) floatingActionButton:
? const BrandFab() showFab && Breakpoints.small.isActive(context) && showBottomBar
: null, ? const BrandFab()
: null,
); );
} }
} }
@ -190,8 +191,6 @@ class _BottomBar extends StatelessWidget {
.any((final route) => route.name == destination.route.routeName), .any((final route) => route.name == destination.route.routeName),
); );
print(prevActiveIndex);
return AnimatedContainer( return AnimatedContainer(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
height: hidden ? 0 : 80, height: hidden ? 0 : 80,

View File

@ -13,14 +13,14 @@ import 'package:selfprivacy/ui/helpers/modals.dart';
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class BackupDetails extends StatefulWidget { class BackupDetailsPage extends StatefulWidget {
const BackupDetails({super.key}); const BackupDetailsPage({super.key});
@override @override
State<BackupDetails> createState() => _BackupDetailsState(); State<BackupDetailsPage> createState() => _BackupDetailsPageState();
} }
class _BackupDetailsState extends State<BackupDetails> class _BackupDetailsPageState extends State<BackupDetailsPage>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) {

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.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/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
@ -10,11 +11,8 @@ import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
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/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.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/ui/pages/backup_details/backup_details.dart'; import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart';
import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart';
import 'package:selfprivacy/utils/breakpoints.dart'; import 'package:selfprivacy/utils/breakpoints.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
@ -84,8 +82,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
subtitle: diskStatus.isDiskOkay subtitle: diskStatus.isDiskOkay
? 'storage.status_ok'.tr() ? 'storage.status_ok'.tr()
: 'storage.status_error'.tr(), : 'storage.status_error'.tr(),
onTap: () => Navigator.of(context) onTap: () => context.pushRoute(const ServerDetailsRoute()),
.push(materialRoute(const ServerDetailsScreen())),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_Card( _Card(
@ -95,11 +92,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
subtitle: appConfig.isDomainSelected subtitle: appConfig.isDomainSelected
? appConfig.serverDomain!.domainName ? appConfig.serverDomain!.domainName
: '', : '',
onTap: () => Navigator.of(context).push( onTap: () => context.pushRoute(const DnsDetailsRoute()),
materialRoute(
const DnsDetailsPage(),
),
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
// TODO: When backups are fixed, show this card // TODO: When backups are fixed, show this card
@ -111,8 +104,7 @@ class _ProvidersPageState extends State<ProvidersPage> {
icon: BrandIcons.save, icon: BrandIcons.save,
title: 'backup.card_title'.tr(), title: 'backup.card_title'.tr(),
subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '', subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '',
onTap: () => Navigator.of(context) onTap: () => context.pushRoute(const BackupDetailsRoute()),
.push(materialRoute(const BackupDetails())),
), ),
], ],
), ),

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.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/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
@ -8,8 +9,6 @@ import 'package:selfprivacy/logic/models/price.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/ui/pages/root_route.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class ExtendingVolumePage extends StatefulWidget { class ExtendingVolumePage extends StatefulWidget {
const ExtendingVolumePage({ const ExtendingVolumePage({
@ -155,10 +154,7 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
DiskSize.fromGibibyte(_currentSliderGbValue), DiskSize.fromGibibyte(_currentSliderGbValue),
context.read<ApiServerVolumeCubit>().reload, context.read<ApiServerVolumeCubit>().reload,
); );
Navigator.of(context).pushAndRemoveUntil( context.router.popUntilRoot();
materialRoute(const RootPage()),
(final predicate) => false,
);
}, },
child: Text('storage.extend_volume_button.title'.tr()), child: Text('storage.extend_volume_button.title'.tr()),
), ),

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.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:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
@ -7,9 +8,8 @@ import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart'; import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/ui/pages/server_storage/extending_volume.dart';
import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart'; import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/ui/router/router.dart';
class ServerStoragePage extends StatefulWidget { class ServerStoragePage extends StatefulWidget {
const ServerStoragePage({ const ServerStoragePage({
@ -105,12 +105,10 @@ class ServerStorageSection extends StatelessWidget {
const SizedBox(height: 16), const SizedBox(height: 16),
BrandOutlinedButton( BrandOutlinedButton(
title: 'storage.extend_volume_button.title'.tr(), title: 'storage.extend_volume_button.title'.tr(),
onPressed: () => Navigator.of(context).push( onPressed: () => context.pushRoute(
materialRoute( ExtendingVolumeRoute(
ExtendingVolumePage( diskVolumeToResize: volume,
diskVolumeToResize: volume, diskStatus: diskStatus,
diskStatus: diskStatus,
),
), ),
), ),
), ),

View File

@ -1,12 +1,12 @@
import 'package:auto_route/auto_route.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/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart';
import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart';
import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/ui/pages/server_storage/server_storage.dart';
import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart'; import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:selfprivacy/ui/router/router.dart';
class StorageCard extends StatelessWidget { class StorageCard extends StatelessWidget {
const StorageCard({ const StorageCard({
@ -45,13 +45,8 @@ class StorageCard extends StatelessWidget {
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: InkResponse( child: InkResponse(
highlightShape: BoxShape.rectangle, highlightShape: BoxShape.rectangle,
onTap: () => Navigator.of(context).push( onTap: () =>
materialRoute( context.pushRoute(ServerStorageRoute(diskStatus: diskStatus)),
ServerStoragePage(
diskStatus: diskStatus,
),
),
),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.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:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
@ -8,9 +9,8 @@ import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart'; import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/launch_url.dart'; import 'package:selfprivacy/utils/launch_url.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
class ServicePage extends StatefulWidget { class ServicePage extends StatefulWidget {
const ServicePage({required this.serviceId, super.key}); const ServicePage({required this.serviceId, super.key});
@ -111,14 +111,12 @@ class _ServicePageState extends State<ServicePage> {
ListTile( ListTile(
iconColor: Theme.of(context).colorScheme.onBackground, iconColor: Theme.of(context).colorScheme.onBackground,
// Open page ServicesMigrationPage // Open page ServicesMigrationPage
onTap: () => Navigator.of(context).push( onTap: () => context.pushRoute(
materialRoute( ServicesMigrationRoute(
ServicesMigrationPage( services: [service],
services: [service], diskStatus:
diskStatus: context.read<ApiServerVolumeCubit>().state.diskStatus,
context.read<ApiServerVolumeCubit>().state.diskStatus, isMigration: false,
isMigration: false,
),
), ),
), ),
leading: const Icon(Icons.drive_file_move_outlined), leading: const Icon(Icons.drive_file_move_outlined),

View File

@ -1,3 +1,4 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
@ -5,16 +6,14 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/logic/models/state_types.dart';
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.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/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.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:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/ui/pages/services/service_page.dart'; import 'package:selfprivacy/ui/router/router.dart';
import 'package:selfprivacy/utils/breakpoints.dart'; import 'package:selfprivacy/utils/breakpoints.dart';
import 'package:selfprivacy/utils/launch_url.dart'; import 'package:selfprivacy/utils/launch_url.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
import 'package:selfprivacy/utils/ui_helpers.dart'; import 'package:selfprivacy/utils/ui_helpers.dart';
class ServicesPage extends StatefulWidget { class ServicesPage extends StatefulWidget {
@ -101,79 +100,81 @@ class _Card extends StatelessWidget {
} }
} }
return GestureDetector( return Card(
onTap: isReady clipBehavior: Clip.antiAlias,
? () => Navigator.of(context) child: InkResponse(
.push(materialRoute(ServicePage(serviceId: service.id))) highlightShape: BoxShape.rectangle,
: null, onTap: isReady
child: BrandCards.big( ? () => context.pushRoute(
child: Column( ServiceRoute(serviceId: service.id),
crossAxisAlignment: CrossAxisAlignment.start, )
children: [ : null,
Row( child: Padding(
children: [ padding: const EdgeInsets.all(16.0),
IconStatusMask( child: Column(
status: getStatus(service.status), crossAxisAlignment: CrossAxisAlignment.start,
icon: SvgPicture.string( children: [
service.svgIcon, Row(
width: 30.0,
height: 30.0,
color: Theme.of(context).colorScheme.onBackground,
),
),
],
),
ClipRect(
child: Stack(
children: [ children: [
Column( IconStatusMask(
crossAxisAlignment: CrossAxisAlignment.start, status: getStatus(service.status),
children: [ icon: SvgPicture.string(
const SizedBox(height: 10), service.svgIcon,
BrandText.h2(service.displayName), width: 30.0,
const SizedBox(height: 10), height: 30.0,
if (service.url != '' && service.url != null) colorFilter: const ColorFilter.mode(
Column( Colors.white,
children: [ BlendMode.srcIn,
GestureDetector( ),
onTap: () => launchURL( ),
service.url,
),
child: Text(
'${service.url}',
style: TextStyle(
color:
Theme.of(context).colorScheme.secondary,
decoration: TextDecoration.underline,
),
),
),
const SizedBox(height: 10),
],
),
if (service.id == 'mailserver')
Column(
children: [
Text(
domainName,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
const SizedBox(height: 10),
],
),
BrandText.body2(service.loginInfo),
const SizedBox(height: 10),
BrandText.body2(service.description),
const SizedBox(height: 10),
],
), ),
], ],
), ),
) Column(
], crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
BrandText.h2(service.displayName),
const SizedBox(height: 10),
if (service.url != '' && service.url != null)
Column(
children: [
GestureDetector(
onTap: () => launchURL(
service.url,
),
child: Text(
'${service.url}',
style: TextStyle(
color: Theme.of(context).colorScheme.secondary,
decoration: TextDecoration.underline,
),
),
),
const SizedBox(height: 10),
],
),
if (service.id == 'mailserver')
Column(
children: [
Text(
domainName,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
const SizedBox(height: 10),
],
),
BrandText.body2(service.loginInfo),
const SizedBox(height: 10),
BrandText.body2(service.description),
const SizedBox(height: 10),
],
)
],
),
), ),
), ),
); );

View File

@ -1,22 +0,0 @@
part of 'users.dart';
class AddUserFab extends StatelessWidget {
const AddUserFab({super.key});
@override
Widget build(final BuildContext context) => FloatingActionButton.small(
heroTag: 'new_user_fab',
onPressed: () {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (final BuildContext context) => Padding(
padding: MediaQuery.of(context).viewInsets,
child: const NewUser(),
),
);
},
child: const Icon(Icons.person_add_outlined),
);
}

View File

@ -1,7 +1,7 @@
part of 'users.dart'; part of 'users.dart';
class NewUser extends StatelessWidget { class NewUserPage extends StatelessWidget {
const NewUser({super.key}); const NewUserPage({super.key});
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) {
@ -10,108 +10,89 @@ class NewUser extends StatelessWidget {
final String domainName = UiHelpers.getDomainName(config); final String domainName = UiHelpers.getDomainName(config);
return BrandBottomSheet( return BlocProvider(
child: BlocProvider( create: (final BuildContext context) {
create: (final BuildContext context) { final jobCubit = context.read<JobsCubit>();
final jobCubit = context.read<JobsCubit>(); final jobState = jobCubit.state;
final jobState = jobCubit.state; final users = <User>[];
final users = <User>[]; users.addAll(context.read<UsersCubit>().state.users);
users.addAll(context.read<UsersCubit>().state.users); if (jobState is JobsStateWithJobs) {
if (jobState is JobsStateWithJobs) { final jobs = jobState.clientJobList;
final jobs = jobState.clientJobList; for (final job in jobs) {
for (final job in jobs) { if (job is CreateUserJob) {
if (job is CreateUserJob) { users.add(job.user);
users.add(job.user);
}
} }
} }
return UserFormCubit( }
jobsCubit: jobCubit, return UserFormCubit(
fieldFactory: FieldCubitFactory(context), jobsCubit: jobCubit,
); fieldFactory: FieldCubitFactory(context),
}, );
child: Builder( },
builder: (final BuildContext context) { child: Builder(
final FormCubitState formCubitState = builder: (final BuildContext context) {
context.watch<UserFormCubit>().state; final FormCubitState formCubitState =
context.watch<UserFormCubit>().state;
return BlocListener<UserFormCubit, FormCubitState>( return BlocListener<UserFormCubit, FormCubitState>(
listener: listener: (final BuildContext context, final FormCubitState state) {
(final BuildContext context, final FormCubitState state) { if (state.isSubmitted) {
if (state.isSubmitted) { context.router.pop();
Navigator.pop(context); }
} },
}, child: BrandHeroScreen(
child: Column( heroTitle: 'users.new_user'.tr(),
crossAxisAlignment: CrossAxisAlignment.start, heroIcon: Icons.person_add_outlined,
mainAxisSize: MainAxisSize.min, children: [
children: [ if (formCubitState.isErrorShown)
BrandHeader( Text(
title: 'users.new_user'.tr(), 'users.username_rule'.tr(),
), style: TextStyle(
const SizedBox(width: 14), color: Theme.of(context).colorScheme.error,
Padding(
padding: paddingH15V0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (formCubitState.isErrorShown)
Text(
'users.username_rule'.tr(),
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
const SizedBox(width: 14),
IntrinsicHeight(
child: CubitFormTextField(
formFieldCubit: context.read<UserFormCubit>().login,
decoration: InputDecoration(
labelText: 'users.login'.tr(),
suffixText: '@$domainName',
),
),
),
const SizedBox(height: 20),
CubitFormTextField(
formFieldCubit:
context.read<UserFormCubit>().password,
decoration: InputDecoration(
alignLabelWithHint: false,
labelText: 'basis.password'.tr(),
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 8),
child: IconButton(
icon: Icon(
BrandIcons.refresh,
color:
Theme.of(context).colorScheme.secondary,
),
onPressed: context
.read<UserFormCubit>()
.genNewPassword,
),
),
),
),
const SizedBox(height: 30),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<UserFormCubit>().trySubmit(),
text: 'basis.create'.tr(),
),
const SizedBox(height: 40),
Text('users.new_user_info_note'.tr()),
const SizedBox(height: 30),
],
), ),
), ),
], const SizedBox(width: 14),
), IntrinsicHeight(
); child: CubitFormTextField(
}, formFieldCubit: context.read<UserFormCubit>().login,
), decoration: InputDecoration(
labelText: 'users.login'.tr(),
suffixText: '@$domainName',
),
),
),
const SizedBox(height: 20),
CubitFormTextField(
formFieldCubit: context.read<UserFormCubit>().password,
decoration: InputDecoration(
alignLabelWithHint: false,
labelText: 'basis.password'.tr(),
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 8),
child: IconButton(
icon: Icon(
BrandIcons.refresh,
color: Theme.of(context).colorScheme.secondary,
),
onPressed: context.read<UserFormCubit>().genNewPassword,
),
),
),
),
const SizedBox(height: 30),
BrandButton.rised(
onPressed: formCubitState.isSubmitting
? null
: () => context.read<UserFormCubit>().trySubmit(),
text: 'basis.create'.tr(),
),
const SizedBox(height: 40),
Text('users.new_user_info_note'.tr()),
const SizedBox(height: 30),
],
),
);
},
), ),
); );
} }

View File

@ -11,9 +11,7 @@ class _User extends StatelessWidget {
@override @override
Widget build(final BuildContext context) => InkWell( Widget build(final BuildContext context) => InkWell(
onTap: () { onTap: () {
Navigator.of(context).push( context.pushRoute(UserDetailsRoute(login: user.login));
materialRoute(UserDetailsPage(login: user.login)),
);
}, },
child: Container( child: Container(
padding: paddingH15V0, padding: paddingH15V0,

View File

@ -87,6 +87,7 @@ class _DeleteUserTile extends StatelessWidget {
onTap: () => { onTap: () => {
showDialog( showDialog(
context: context, context: context,
// useRootNavigator: false,
builder: (final BuildContext context) => AlertDialog( builder: (final BuildContext context) => AlertDialog(
title: Text('basis.confirmation'.tr()), title: Text('basis.confirmation'.tr()),
content: SingleChildScrollView( content: SingleChildScrollView(
@ -102,7 +103,7 @@ class _DeleteUserTile extends StatelessWidget {
TextButton( TextButton(
child: Text('basis.cancel'.tr()), child: Text('basis.cancel'.tr()),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); context.router.pop();
}, },
), ),
TextButton( TextButton(
@ -114,9 +115,8 @@ class _DeleteUserTile extends StatelessWidget {
), ),
onPressed: () { onPressed: () {
context.read<JobsCubit>().addJob(DeleteUserJob(user: user)); context.read<JobsCubit>().addJob(DeleteUserJob(user: user));
Navigator.of(context) context.router.childControllers.first.pop();
..pop() context.router.pop();
..pop();
}, },
), ),
], ],

View File

@ -1,3 +1,4 @@
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';
@ -22,16 +23,14 @@ 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/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/ui/router/router.dart';
import 'package:selfprivacy/utils/breakpoints.dart'; import 'package:selfprivacy/utils/breakpoints.dart';
import 'package:selfprivacy/utils/ui_helpers.dart'; import 'package:selfprivacy/utils/ui_helpers.dart';
import 'package:selfprivacy/utils/route_transitions/basic.dart';
part 'empty.dart'; part 'empty.dart';
part 'new_user.dart'; part 'new_user.dart';
part 'user.dart'; part 'user.dart';
part 'user_details.dart'; part 'user_details.dart';
part 'add_user_fab.dart';
part 'reset_password.dart'; part 'reset_password.dart';
class UsersPage extends StatelessWidget { class UsersPage extends StatelessWidget {
@ -93,13 +92,36 @@ class UsersPage extends StatelessWidget {
onRefresh: () async { onRefresh: () async {
context.read<UsersCubit>().refresh(); context.read<UsersCubit>().refresh();
}, },
child: ListView.builder( child: Column(
itemCount: users.length, children: [
itemBuilder: (final BuildContext context, final int index) => Padding(
_User( padding: const EdgeInsets.all(8.0),
user: users[index], child: FilledButton.tonal(
isRootUser: users[index].type == UserType.primary, child: Row(
), crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.person_add_outlined),
const SizedBox(width: 8),
Text('users.new_user'.tr()),
],
),
onPressed: () {
context.pushRoute(const NewUserRoute());
},
),
),
Expanded(
child: ListView.builder(
itemCount: users.length,
itemBuilder:
(final BuildContext context, final int index) => _User(
user: users[index],
isRootUser: users[index].type == UserType.primary,
),
),
),
],
), ),
); );
}, },

View File

@ -3,7 +3,9 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/disk_status.dart';
import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/service.dart';
import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart';
import 'package:selfprivacy/ui/pages/devices/devices.dart'; import 'package:selfprivacy/ui/pages/devices/devices.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';
import 'package:selfprivacy/ui/pages/more/app_settings/developer_settings.dart'; import 'package:selfprivacy/ui/pages/more/app_settings/developer_settings.dart';
@ -13,7 +15,11 @@ import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
import 'package:selfprivacy/ui/pages/providers/providers.dart'; import 'package:selfprivacy/ui/pages/providers/providers.dart';
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart'; import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart';
import 'package:selfprivacy/ui/pages/root_route.dart'; import 'package:selfprivacy/ui/pages/root_route.dart';
import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart';
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.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/services.dart'; import 'package:selfprivacy/ui/pages/services/services.dart';
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart'; import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';
@ -75,10 +81,17 @@ Widget fadeThroughTransition(
), ),
AutoRoute(page: AppSettingsPage), AutoRoute(page: AppSettingsPage),
AutoRoute(page: UserDetailsPage), AutoRoute(page: UserDetailsPage),
AutoRoute(page: NewUserPage),
AutoRoute(page: RecoveryKeyPage), AutoRoute(page: RecoveryKeyPage),
AutoRoute(page: DevicesScreen), AutoRoute(page: DevicesScreen),
AutoRoute(page: AboutApplicationPage), AutoRoute(page: AboutApplicationPage),
AutoRoute(page: DeveloperSettingsPage), AutoRoute(page: DeveloperSettingsPage),
AutoRoute(page: ServicePage),
AutoRoute(page: ServerDetailsScreen),
AutoRoute(page: DnsDetailsPage),
AutoRoute(page: BackupDetailsPage),
AutoRoute(page: ServerStoragePage),
AutoRoute(page: ExtendingVolumePage),
], ],
), ),
AutoRoute(page: ServicesMigrationPage), AutoRoute(page: ServicesMigrationPage),
@ -97,6 +110,7 @@ String getRouteTitle(final String routeName) {
case 'ProvidersRoute': case 'ProvidersRoute':
return 'basis.providers_title'; return 'basis.providers_title';
case 'ServicesRoute': case 'ServicesRoute':
case 'ServiceRoute':
return 'basis.services'; return 'basis.services';
case 'UsersRoute': case 'UsersRoute':
return 'basis.users'; return 'basis.users';
@ -105,7 +119,9 @@ String getRouteTitle(final String routeName) {
case 'AppSettingsRoute': case 'AppSettingsRoute':
return 'application_settings.title'; return 'application_settings.title';
case 'UserDetailsRoute': case 'UserDetailsRoute':
return '[User Details]'; return 'users.details_title';
case 'NewUserRoute':
return 'users.new_user';
case 'RecoveryKeyRoute': case 'RecoveryKeyRoute':
return 'recovery_key.key_main_header'; return 'recovery_key.key_main_header';
case 'DevicesRoute': case 'DevicesRoute':
@ -113,9 +129,19 @@ String getRouteTitle(final String routeName) {
case 'AboutApplicationRoute': case 'AboutApplicationRoute':
return 'about_us_page.title'; return 'about_us_page.title';
case 'ConsoleRoute': case 'ConsoleRoute':
return '[Console]'; return 'console_page.title';
case 'DeveloperSettingsRoute': case 'DeveloperSettingsRoute':
return 'developer_settings.title'; return 'developer_settings.title';
case 'DnsDetailsRoute':
return 'domain.screen_title';
case 'ServerDetailsRoute':
return 'server.card_title';
case 'BackupDetailsRoute':
return 'backup.card_title';
case 'ServerStorageRoute':
return 'storage.card_title';
case 'ExtendingVolumeRoute':
return 'storage.extending_volume_title';
default: default:
return routeName; return routeName;
} }

View File

@ -115,6 +115,12 @@ class _$RootRouter extends RootStackRouter {
), ),
); );
}, },
NewUserRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData,
child: const NewUserPage(),
);
},
RecoveryKeyRoute.name: (routeData) { RecoveryKeyRoute.name: (routeData) {
return MaterialPageX<dynamic>( return MaterialPageX<dynamic>(
routeData: routeData, routeData: routeData,
@ -139,6 +145,55 @@ class _$RootRouter extends RootStackRouter {
child: const DeveloperSettingsPage(), child: const DeveloperSettingsPage(),
); );
}, },
ServiceRoute.name: (routeData) {
final args = routeData.argsAs<ServiceRouteArgs>();
return MaterialPageX<dynamic>(
routeData: routeData,
child: ServicePage(
serviceId: args.serviceId,
key: args.key,
),
);
},
ServerDetailsRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData,
child: const ServerDetailsScreen(),
);
},
DnsDetailsRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData,
child: const DnsDetailsPage(),
);
},
BackupDetailsRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData,
child: const BackupDetailsPage(),
);
},
ServerStorageRoute.name: (routeData) {
final args = routeData.argsAs<ServerStorageRouteArgs>();
return MaterialPageX<dynamic>(
routeData: routeData,
child: ServerStoragePage(
diskStatus: args.diskStatus,
key: args.key,
),
);
},
ExtendingVolumeRoute.name: (routeData) {
final args = routeData.argsAs<ExtendingVolumeRouteArgs>();
return MaterialPageX<dynamic>(
routeData: routeData,
child: ExtendingVolumePage(
diskVolumeToResize: args.diskVolumeToResize,
diskStatus: args.diskStatus,
key: args.key,
),
);
},
}; };
@override @override
@ -193,6 +248,11 @@ class _$RootRouter extends RootStackRouter {
path: 'user-details-page', path: 'user-details-page',
parent: RootRoute.name, parent: RootRoute.name,
), ),
RouteConfig(
NewUserRoute.name,
path: 'new-user-page',
parent: RootRoute.name,
),
RouteConfig( RouteConfig(
RecoveryKeyRoute.name, RecoveryKeyRoute.name,
path: 'recovery-key-page', path: 'recovery-key-page',
@ -213,6 +273,36 @@ class _$RootRouter extends RootStackRouter {
path: 'developer-settings-page', path: 'developer-settings-page',
parent: RootRoute.name, parent: RootRoute.name,
), ),
RouteConfig(
ServiceRoute.name,
path: 'service-page',
parent: RootRoute.name,
),
RouteConfig(
ServerDetailsRoute.name,
path: 'server-details-screen',
parent: RootRoute.name,
),
RouteConfig(
DnsDetailsRoute.name,
path: 'dns-details-page',
parent: RootRoute.name,
),
RouteConfig(
BackupDetailsRoute.name,
path: 'backup-details-page',
parent: RootRoute.name,
),
RouteConfig(
ServerStorageRoute.name,
path: 'server-storage-page',
parent: RootRoute.name,
),
RouteConfig(
ExtendingVolumeRoute.name,
path: 'extending-volume-page',
parent: RootRoute.name,
),
], ],
), ),
RouteConfig( RouteConfig(
@ -425,6 +515,18 @@ class UserDetailsRouteArgs {
} }
} }
/// generated route for
/// [NewUserPage]
class NewUserRoute extends PageRouteInfo<void> {
const NewUserRoute()
: super(
NewUserRoute.name,
path: 'new-user-page',
);
static const String name = 'NewUserRoute';
}
/// generated route for /// generated route for
/// [RecoveryKeyPage] /// [RecoveryKeyPage]
class RecoveryKeyRoute extends PageRouteInfo<void> { class RecoveryKeyRoute extends PageRouteInfo<void> {
@ -472,3 +574,146 @@ class DeveloperSettingsRoute extends PageRouteInfo<void> {
static const String name = 'DeveloperSettingsRoute'; static const String name = 'DeveloperSettingsRoute';
} }
/// generated route for
/// [ServicePage]
class ServiceRoute extends PageRouteInfo<ServiceRouteArgs> {
ServiceRoute({
required String serviceId,
Key? key,
}) : super(
ServiceRoute.name,
path: 'service-page',
args: ServiceRouteArgs(
serviceId: serviceId,
key: key,
),
);
static const String name = 'ServiceRoute';
}
class ServiceRouteArgs {
const ServiceRouteArgs({
required this.serviceId,
this.key,
});
final String serviceId;
final Key? key;
@override
String toString() {
return 'ServiceRouteArgs{serviceId: $serviceId, key: $key}';
}
}
/// generated route for
/// [ServerDetailsScreen]
class ServerDetailsRoute extends PageRouteInfo<void> {
const ServerDetailsRoute()
: super(
ServerDetailsRoute.name,
path: 'server-details-screen',
);
static const String name = 'ServerDetailsRoute';
}
/// generated route for
/// [DnsDetailsPage]
class DnsDetailsRoute extends PageRouteInfo<void> {
const DnsDetailsRoute()
: super(
DnsDetailsRoute.name,
path: 'dns-details-page',
);
static const String name = 'DnsDetailsRoute';
}
/// generated route for
/// [BackupDetailsPage]
class BackupDetailsRoute extends PageRouteInfo<void> {
const BackupDetailsRoute()
: super(
BackupDetailsRoute.name,
path: 'backup-details-page',
);
static const String name = 'BackupDetailsRoute';
}
/// generated route for
/// [ServerStoragePage]
class ServerStorageRoute extends PageRouteInfo<ServerStorageRouteArgs> {
ServerStorageRoute({
required DiskStatus diskStatus,
Key? key,
}) : super(
ServerStorageRoute.name,
path: 'server-storage-page',
args: ServerStorageRouteArgs(
diskStatus: diskStatus,
key: key,
),
);
static const String name = 'ServerStorageRoute';
}
class ServerStorageRouteArgs {
const ServerStorageRouteArgs({
required this.diskStatus,
this.key,
});
final DiskStatus diskStatus;
final Key? key;
@override
String toString() {
return 'ServerStorageRouteArgs{diskStatus: $diskStatus, key: $key}';
}
}
/// generated route for
/// [ExtendingVolumePage]
class ExtendingVolumeRoute extends PageRouteInfo<ExtendingVolumeRouteArgs> {
ExtendingVolumeRoute({
required DiskVolume diskVolumeToResize,
required DiskStatus diskStatus,
Key? key,
}) : super(
ExtendingVolumeRoute.name,
path: 'extending-volume-page',
args: ExtendingVolumeRouteArgs(
diskVolumeToResize: diskVolumeToResize,
diskStatus: diskStatus,
key: key,
),
);
static const String name = 'ExtendingVolumeRoute';
}
class ExtendingVolumeRouteArgs {
const ExtendingVolumeRouteArgs({
required this.diskVolumeToResize,
required this.diskStatus,
this.key,
});
final DiskVolume diskVolumeToResize;
final DiskStatus diskStatus;
final Key? key;
@override
String toString() {
return 'ExtendingVolumeRouteArgs{diskVolumeToResize: $diskVolumeToResize, diskStatus: $diskStatus, key: $key}';
}
}