mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-23 01:06:44 +00:00
feat: Introduce new router and adaptive layouts
This commit is contained in:
parent
befdc0286e
commit
423efeeb20
|
@ -333,7 +333,20 @@
|
|||
"create_master_account": "Create master account",
|
||||
"enter_username_and_password": "Enter username and strong password",
|
||||
"finish": "Everything is initialized",
|
||||
"checks": "Checks have been completed \n{} out of {}"
|
||||
"checks": "Checks have been completed \n{} out of {}",
|
||||
"steps": {
|
||||
"hosting": "Hosting",
|
||||
"server_type": "Server type",
|
||||
"dns_provider": "DNS provider",
|
||||
"backups_provider": "Backups",
|
||||
"domain": "Domain",
|
||||
"master_account": "Master account",
|
||||
"server": "Server",
|
||||
"dns_setup": "DNS setup",
|
||||
"nixos_installation": "NixOS installation",
|
||||
"server_reboot": "Server reboot",
|
||||
"final_checks": "Final checks"
|
||||
}
|
||||
},
|
||||
"recovering": {
|
||||
"generic_error": "Operation failed, please try again.",
|
||||
|
|
|
@ -5,9 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||
import 'package:selfprivacy/config/brand_colors.dart';
|
||||
import 'package:selfprivacy/config/hive_config.dart';
|
||||
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
import 'package:timezone/data/latest.dart' as tz;
|
||||
|
||||
|
@ -20,7 +18,7 @@ import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
|||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await HiveConfig.init();
|
||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
// await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
|
||||
try {
|
||||
/// Wakelock support for Linux
|
||||
|
@ -43,21 +41,20 @@ void main() async {
|
|||
fallbackColor: BrandColors.primary,
|
||||
);
|
||||
|
||||
BlocOverrides.runZoned(
|
||||
() => runApp(
|
||||
Localization(
|
||||
child: MyApp(
|
||||
lightThemeData: lightThemeData,
|
||||
darkThemeData: darkThemeData,
|
||||
),
|
||||
Bloc.observer = SimpleBlocObserver();
|
||||
|
||||
runApp(
|
||||
Localization(
|
||||
child: SelfprivacyApp(
|
||||
lightThemeData: lightThemeData,
|
||||
darkThemeData: darkThemeData,
|
||||
),
|
||||
),
|
||||
blocObserver: SimpleBlocObserver(),
|
||||
);
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({
|
||||
class SelfprivacyApp extends StatelessWidget {
|
||||
SelfprivacyApp({
|
||||
required this.lightThemeData,
|
||||
required this.darkThemeData,
|
||||
super.key,
|
||||
|
@ -66,6 +63,8 @@ class MyApp extends StatelessWidget {
|
|||
final ThemeData lightThemeData;
|
||||
final ThemeData darkThemeData;
|
||||
|
||||
final _appRouter = RootRouter();
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Localization(
|
||||
child: AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
|
@ -76,10 +75,11 @@ class MyApp extends StatelessWidget {
|
|||
final BuildContext context,
|
||||
final AppSettingsState appSettings,
|
||||
) =>
|
||||
MaterialApp(
|
||||
MaterialApp.router(
|
||||
routeInformationParser: _appRouter.defaultRouteParser(),
|
||||
routerDelegate: _appRouter.delegate(),
|
||||
scaffoldMessengerKey:
|
||||
getIt.get<NavigationService>().scaffoldMessengerKey,
|
||||
navigatorKey: getIt.get<NavigationService>().navigatorKey,
|
||||
localizationsDelegates: context.localizationDelegates,
|
||||
supportedLocales: context.supportedLocales,
|
||||
locale: context.locale,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class NotReadyCard extends StatelessWidget {
|
||||
|
@ -13,11 +13,7 @@ class NotReadyCard extends StatelessWidget {
|
|||
child: ListTile(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
onTap: () => Navigator.of(context).push(
|
||||
materialRoute(
|
||||
const InitializingPage(),
|
||||
),
|
||||
),
|
||||
onTap: () => context.pushRoute(const InitializingRoute()),
|
||||
title: Text(
|
||||
'not_ready_card.in_menu'.tr(),
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:ionicons/ionicons.dart';
|
||||
|
@ -7,7 +8,12 @@ import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart';
|
|||
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||
|
||||
class BrandFab extends StatefulWidget {
|
||||
const BrandFab({super.key});
|
||||
const BrandFab({
|
||||
this.extended = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final bool extended;
|
||||
|
||||
@override
|
||||
State<BrandFab> createState() => _BrandFabState();
|
||||
|
@ -64,20 +70,35 @@ class _BrandFabState extends State<BrandFab>
|
|||
),
|
||||
);
|
||||
},
|
||||
child: AnimatedBuilder(
|
||||
animation: _colorTween,
|
||||
builder: (final BuildContext context, final Widget? child) {
|
||||
final double v = _animationController.value;
|
||||
final IconData icon =
|
||||
v > 0.5 ? Ionicons.flash : Ionicons.flash_outline;
|
||||
return Transform.scale(
|
||||
scale: 1 + (v < 0.5 ? v : 1 - v) * 2,
|
||||
child: Icon(
|
||||
icon,
|
||||
color: _colorTween.value,
|
||||
isExtended: widget.extended,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AnimatedBuilder(
|
||||
animation: _colorTween,
|
||||
builder: (final BuildContext context, final Widget? child) {
|
||||
final double v = _animationController.value;
|
||||
final IconData icon =
|
||||
v > 0.5 ? Ionicons.flash : Ionicons.flash_outline;
|
||||
return Transform.scale(
|
||||
scale: 1 + (v < 0.5 ? v : 1 - v) * 2,
|
||||
child: Icon(
|
||||
icon,
|
||||
color: _colorTween.value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (widget.extended)
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
);
|
||||
},
|
||||
if (widget.extended)
|
||||
Text(
|
||||
'jobs.title'.tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
|
||||
import 'package:selfprivacy/ui/helpers/widget_size.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
|
||||
class BrandHeroScreen extends StatelessWidget {
|
||||
const BrandHeroScreen({
|
||||
|
@ -13,6 +15,7 @@ class BrandHeroScreen extends StatelessWidget {
|
|||
this.heroTitle = '',
|
||||
this.heroSubtitle,
|
||||
this.onBackButtonPressed,
|
||||
this.bodyPadding = const EdgeInsets.all(16.0),
|
||||
});
|
||||
|
||||
final List<Widget> children;
|
||||
|
@ -23,6 +26,7 @@ class BrandHeroScreen extends StatelessWidget {
|
|||
final String heroTitle;
|
||||
final String? heroSubtitle;
|
||||
final VoidCallback? onBackButtonPressed;
|
||||
final EdgeInsetsGeometry bodyPadding;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
|
@ -64,7 +68,7 @@ class BrandHeroScreen extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
padding: bodyPadding,
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildListDelegate(children),
|
||||
),
|
||||
|
@ -98,50 +102,50 @@ class HeroSliverAppBar extends StatefulWidget {
|
|||
class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
|
||||
Size _size = Size.zero;
|
||||
@override
|
||||
Widget build(final BuildContext context) => SliverAppBar(
|
||||
expandedHeight:
|
||||
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,
|
||||
primary: true,
|
||||
pinned: true,
|
||||
stretch: true,
|
||||
leading: widget.hasBackButton
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: widget.onBackButtonPressed ??
|
||||
() => Navigator.of(context).pop(),
|
||||
)
|
||||
: null,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
title: LayoutBuilder(
|
||||
builder: (final context, final constraints) => SizedBox(
|
||||
width: constraints.maxWidth - 72.0,
|
||||
child: WidgetSize(
|
||||
onChange: (final Size size) => setState(() => _size = size),
|
||||
child: Text(
|
||||
widget.heroTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
overflow: TextOverflow.fade,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Widget build(final BuildContext context) {
|
||||
final isMobile = Breakpoints.small.isActive(context);
|
||||
return SliverAppBar(
|
||||
expandedHeight:
|
||||
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,
|
||||
primary: true,
|
||||
pinned: isMobile,
|
||||
stretch: true,
|
||||
surfaceTintColor: isMobile ? null : Colors.transparent,
|
||||
leading: (widget.hasBackButton && isMobile)
|
||||
? const AutoLeadingButton()
|
||||
: const SizedBox.shrink(),
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
title: LayoutBuilder(
|
||||
builder: (final context, final constraints) => SizedBox(
|
||||
width: constraints.maxWidth - 72.0,
|
||||
child: WidgetSize(
|
||||
onChange: (final Size size) => setState(() => _size = size),
|
||||
child: Text(
|
||||
widget.heroTitle,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
overflow: TextOverflow.fade,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
expandedTitleScale: 1.2,
|
||||
centerTitle: true,
|
||||
collapseMode: CollapseMode.pin,
|
||||
titlePadding: const EdgeInsets.only(
|
||||
bottom: 12.0,
|
||||
top: 16.0,
|
||||
),
|
||||
background: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 72.0),
|
||||
if (widget.hasHeroIcon) widget.heroIconWidget,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
expandedTitleScale: 1.2,
|
||||
centerTitle: true,
|
||||
collapseMode: CollapseMode.pin,
|
||||
titlePadding: const EdgeInsets.only(
|
||||
bottom: 12.0,
|
||||
top: 16.0,
|
||||
),
|
||||
background: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 72.0),
|
||||
if (widget.hasHeroIcon) widget.heroIconWidget,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
70
lib/ui/layouts/responsive_layout_with_infobox.dart
Normal file
70
lib/ui/layouts/responsive_layout_with_infobox.dart
Normal file
|
@ -0,0 +1,70 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
|
||||
class ResponsiveLayoutWithInfobox extends StatelessWidget {
|
||||
const ResponsiveLayoutWithInfobox({
|
||||
required this.primaryColumn,
|
||||
this.topChild,
|
||||
this.secondaryColumn,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Widget? topChild;
|
||||
final Widget primaryColumn;
|
||||
final Widget? secondaryColumn;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final hasSecondaryColumn = secondaryColumn != null;
|
||||
final hasTopChild = topChild != null;
|
||||
|
||||
if (Breakpoints.large.isActive(context)) {
|
||||
return LayoutBuilder(
|
||||
builder: (final context, final constraints) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (hasTopChild)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: constraints.maxWidth * 0.9,
|
||||
child: topChild,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (hasTopChild) const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: hasSecondaryColumn
|
||||
? constraints.maxWidth * 0.7
|
||||
: constraints.maxWidth * 0.9,
|
||||
child: primaryColumn,
|
||||
),
|
||||
if (hasSecondaryColumn) const SizedBox(width: 16),
|
||||
if (hasSecondaryColumn)
|
||||
SizedBox(
|
||||
width: constraints.maxWidth * 0.2,
|
||||
child: secondaryColumn,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (hasTopChild) topChild!,
|
||||
const SizedBox(height: 16),
|
||||
primaryColumn,
|
||||
const SizedBox(height: 32),
|
||||
if (hasSecondaryColumn) secondaryColumn!,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
276
lib/ui/layouts/root_scaffold_with_navigation.dart
Normal file
276
lib/ui/layouts/root_scaffold_with_navigation.dart
Normal file
|
@ -0,0 +1,276 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
|
||||
import 'package:selfprivacy/ui/components/support_drawer/support_drawer.dart';
|
||||
import 'package:selfprivacy/ui/router/root_destinations.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
|
||||
class RootScaffoldWithNavigation extends StatelessWidget {
|
||||
const RootScaffoldWithNavigation({
|
||||
required this.child,
|
||||
required this.title,
|
||||
required this.destinations,
|
||||
this.showBottomBar = true,
|
||||
this.showFab = true,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final String title;
|
||||
final bool showBottomBar;
|
||||
final List<RouteDestination> destinations;
|
||||
final bool showFab;
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(final BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: Breakpoints.mediumAndUp.isActive(context)
|
||||
? PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: _RootAppBar(title: title),
|
||||
)
|
||||
: null,
|
||||
endDrawer: const SupportDrawer(),
|
||||
endDrawerEnableOpenDragGesture: false,
|
||||
body: Row(
|
||||
children: [
|
||||
if (Breakpoints.medium.isActive(context))
|
||||
MainScreenNavigationRail(
|
||||
destinations: destinations,
|
||||
showFab: showFab,
|
||||
),
|
||||
if (Breakpoints.large.isActive(context))
|
||||
MainScreenNavigationDrawer(
|
||||
destinations: destinations,
|
||||
),
|
||||
Expanded(child: child),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: BottomBar(
|
||||
destinations: destinations,
|
||||
hidden: !(Breakpoints.small.isActive(context) && showBottomBar),
|
||||
key: const Key('bottomBar'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RootAppBar extends StatelessWidget {
|
||||
const _RootAppBar({
|
||||
required this.title,
|
||||
});
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => AppBar(
|
||||
title: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
transitionBuilder:
|
||||
(final Widget child, final Animation<double> animation) =>
|
||||
SlideTransition(
|
||||
position: animation.drive(
|
||||
Tween<Offset>(
|
||||
begin: const Offset(0.0, 0.2),
|
||||
end: Offset.zero,
|
||||
),
|
||||
),
|
||||
child: FadeTransition(
|
||||
opacity: animation,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
key: ValueKey<String>(title),
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
title,
|
||||
),
|
||||
),
|
||||
),
|
||||
leading: context.router.pageCount > 1
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () => context.router.pop(),
|
||||
)
|
||||
: null,
|
||||
actions: const [
|
||||
SizedBox.shrink(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class MainScreenNavigationRail extends StatelessWidget {
|
||||
const MainScreenNavigationRail({
|
||||
required this.destinations,
|
||||
this.showFab = true,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<RouteDestination> destinations;
|
||||
final bool showFab;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
int? activeIndex = destinations.indexWhere(
|
||||
(final destination) =>
|
||||
context.router.isRouteActive(destination.route.routeName),
|
||||
);
|
||||
|
||||
final prevActiveIndex = destinations.indexWhere(
|
||||
(final destination) => context.router.stack
|
||||
.any((final route) => route.name == destination.route.routeName),
|
||||
);
|
||||
|
||||
if (activeIndex == -1) {
|
||||
if (prevActiveIndex != -1) {
|
||||
activeIndex = prevActiveIndex;
|
||||
} else {
|
||||
activeIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
final isExtended = Breakpoints.large.isActive(context);
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (final context, final constraints) => SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
||||
child: IntrinsicHeight(
|
||||
child: NavigationRail(
|
||||
backgroundColor: Colors.transparent,
|
||||
labelType: isExtended
|
||||
? NavigationRailLabelType.none
|
||||
: NavigationRailLabelType.all,
|
||||
extended: isExtended,
|
||||
leading: showFab
|
||||
? const BrandFab(
|
||||
extended: false,
|
||||
)
|
||||
: null,
|
||||
groupAlignment: 0.0,
|
||||
destinations: destinations
|
||||
.map(
|
||||
(final destination) => NavigationRailDestination(
|
||||
icon: Icon(destination.icon),
|
||||
label: Text(destination.label),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
selectedIndex: activeIndex,
|
||||
onDestinationSelected: (final index) {
|
||||
context.router.replaceAll([destinations[index].route]);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BottomBar extends StatelessWidget {
|
||||
const BottomBar({
|
||||
required this.destinations,
|
||||
required this.hidden,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<RouteDestination> destinations;
|
||||
final bool hidden;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final prevActiveIndex = destinations.indexWhere(
|
||||
(final destination) => context.router.stack
|
||||
.any((final route) => route.name == destination.route.routeName),
|
||||
);
|
||||
|
||||
print(prevActiveIndex);
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
height: hidden ? 0 : 80,
|
||||
curve: Curves.easeInOut,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: NavigationBar(
|
||||
selectedIndex: prevActiveIndex == -1 ? 0 : prevActiveIndex,
|
||||
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
|
||||
onDestinationSelected: (final index) {
|
||||
context.router.replaceAll([destinations[index].route]);
|
||||
},
|
||||
destinations: destinations
|
||||
.map(
|
||||
(final destination) => NavigationDestination(
|
||||
icon: Icon(destination.icon),
|
||||
label: destination.label,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MainScreenNavigationDrawer extends StatelessWidget {
|
||||
const MainScreenNavigationDrawer({
|
||||
required this.destinations,
|
||||
this.showFab = true,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<RouteDestination> destinations;
|
||||
final bool showFab;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
int? activeIndex = destinations.indexWhere(
|
||||
(final destination) =>
|
||||
context.router.isRouteActive(destination.route.routeName),
|
||||
);
|
||||
|
||||
final prevActiveIndex = destinations.indexWhere(
|
||||
(final destination) => context.router.stack
|
||||
.any((final route) => route.name == destination.route.routeName),
|
||||
);
|
||||
|
||||
if (activeIndex == -1) {
|
||||
if (prevActiveIndex != -1) {
|
||||
activeIndex = prevActiveIndex;
|
||||
} else {
|
||||
activeIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: 296,
|
||||
child: NavigationDrawer(
|
||||
key: const Key('PrimaryNavigationDrawer'),
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
selectedIndex: activeIndex,
|
||||
onDestinationSelected: (final index) {
|
||||
context.router.replaceAll([destinations[index].route]);
|
||||
},
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: BrandFab(extended: true),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...destinations.map(
|
||||
(final destination) => NavigationDrawerDestination(
|
||||
icon: Icon(destination.icon),
|
||||
label: Text(destination.label),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import 'package:selfprivacy/logic/models/json/backup.dart';
|
|||
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.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/helpers/modals.dart';
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
|||
import 'package:selfprivacy/logic/cubit/devices/devices_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/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
import 'package:selfprivacy/ui/pages/devices/new_device.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
|
||||
class NewDeviceScreen extends StatelessWidget {
|
||||
const NewDeviceScreen({super.key});
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
|
|||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/utils/network_utils.dart';
|
||||
|
||||
|
|
|
@ -1,67 +1,70 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:package_info/package_info.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class AboutApplicationPage extends StatelessWidget {
|
||||
const AboutApplicationPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'about_application_page.title'.tr(),
|
||||
hasBackButton: true,
|
||||
Widget build(final BuildContext context) {
|
||||
final bool isReady = context.watch<ServerInstallationCubit>().state
|
||||
is ServerInstallationFinished;
|
||||
|
||||
return BrandHeroScreen(
|
||||
hasBackButton: true,
|
||||
hasFlashButton: false,
|
||||
heroTitle: 'about_application_page.title'.tr(),
|
||||
children: [
|
||||
FutureBuilder(
|
||||
future: _packageVersion(),
|
||||
builder: (final context, final snapshot) => BrandText.body1(
|
||||
'about_application_page.application_version_text'
|
||||
.tr(args: [snapshot.data.toString()]),
|
||||
),
|
||||
),
|
||||
if (isReady)
|
||||
FutureBuilder(
|
||||
future: _apiVersion(),
|
||||
builder: (final context, final snapshot) => BrandText.body1(
|
||||
'about_application_page.api_version_text'
|
||||
.tr(args: [snapshot.data.toString()]),
|
||||
),
|
||||
),
|
||||
body: ListView(
|
||||
padding: paddingH15V0,
|
||||
const SizedBox(height: 10),
|
||||
// Button to call showAboutDialog
|
||||
TextButton(
|
||||
onPressed: () => showAboutDialog(
|
||||
context: context,
|
||||
applicationName: 'SelfPrivacy',
|
||||
applicationLegalese: '© 2022 SelfPrivacy',
|
||||
// Link to privacy policy
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder(
|
||||
future: _packageVersion(),
|
||||
builder: (final context, final snapshot) => BrandText.body1(
|
||||
'about_application_page.application_version_text'
|
||||
.tr(args: [snapshot.data.toString()]),
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: _apiVersion(),
|
||||
builder: (final context, final snapshot) => BrandText.body1(
|
||||
'about_application_page.api_version_text'
|
||||
.tr(args: [snapshot.data.toString()]),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Button to call showAboutDialog
|
||||
TextButton(
|
||||
onPressed: () => showAboutDialog(
|
||||
context: context,
|
||||
applicationName: 'SelfPrivacy',
|
||||
applicationLegalese: '© 2022 SelfPrivacy',
|
||||
// Link to privacy policy
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => launchUrl(
|
||||
Uri.parse('https://selfprivacy.ru/privacy-policy'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
child: Text('about_application_page.privacy_policy'.tr()),
|
||||
),
|
||||
],
|
||||
onPressed: () => launchUrl(
|
||||
Uri.parse('https://selfprivacy.ru/privacy-policy'),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
child: const Text('Show about dialog'),
|
||||
child: Text('about_application_page.privacy_policy'.tr()),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Text('Show about dialog'),
|
||||
),
|
||||
);
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 0),
|
||||
const SizedBox(height: 8),
|
||||
const BrandMarkdown(
|
||||
fileName: 'about',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _packageVersion() async {
|
||||
String packageVersion = 'unknown';
|
||||
|
|
83
lib/ui/pages/more/app_settings/developer_settings.dart
Normal file
83
lib/ui/pages/more/app_settings/developer_settings.dart
Normal file
|
@ -0,0 +1,83 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/api_maps/staging_options.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class DeveloperSettingsPage extends StatefulWidget {
|
||||
const DeveloperSettingsPage({super.key});
|
||||
|
||||
@override
|
||||
State<DeveloperSettingsPage> createState() => _DeveloperSettingsPageState();
|
||||
}
|
||||
|
||||
class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
|
||||
@override
|
||||
Widget build(final BuildContext context) => BrandHeroScreen(
|
||||
hasBackButton: true,
|
||||
hasFlashButton: false,
|
||||
bodyPadding: const EdgeInsets.symmetric(vertical: 16),
|
||||
heroTitle: 'developer_settings.title'.tr(),
|
||||
heroSubtitle: 'developer_settings.subtitle'.tr(),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'developer_settings.server_setup'.tr(),
|
||||
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
SwitchListTile(
|
||||
title: Text('developer_settings.use_staging_acme'.tr()),
|
||||
subtitle:
|
||||
Text('developer_settings.use_staging_acme_description'.tr()),
|
||||
value: StagingOptions.stagingAcme,
|
||||
onChanged: null,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'developer_settings.routing'.tr(),
|
||||
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text('developer_settings.reset_onboarding'.tr()),
|
||||
subtitle:
|
||||
Text('developer_settings.reset_onboarding_description'.tr()),
|
||||
enabled:
|
||||
!context.watch<AppSettingsCubit>().state.isOnboardingShowing,
|
||||
onTap: () => context
|
||||
.read<AppSettingsCubit>()
|
||||
.turnOffOnboarding(isOnboardingShowing: true),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'developer_settings.cubit_statuses'.tr(),
|
||||
style: Theme.of(context).textTheme.labelLarge!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('ApiDevicesCubit'),
|
||||
subtitle: Text(
|
||||
context.watch<ApiDevicesCubit>().state.status.toString(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('RecoveryKeyCubit'),
|
||||
subtitle: Text(
|
||||
context.watch<RecoveryKeyCubit>().state.loadingStatus.toString(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
|
@ -7,14 +7,14 @@ import 'package:selfprivacy/config/get_it_config.dart';
|
|||
import 'package:selfprivacy/logic/models/message.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
|
||||
class Console extends StatefulWidget {
|
||||
const Console({super.key});
|
||||
class ConsolePage extends StatefulWidget {
|
||||
const ConsolePage({super.key});
|
||||
|
||||
@override
|
||||
State<Console> createState() => _ConsoleState();
|
||||
State<ConsolePage> createState() => _ConsolePageState();
|
||||
}
|
||||
|
||||
class _ConsoleState extends State<Console> {
|
||||
class _ConsolePageState extends State<ConsolePage> {
|
||||
@override
|
||||
void initState() {
|
||||
getIt.get<ConsoleModel>().addListener(update);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ionicons/ionicons.dart';
|
||||
|
@ -8,19 +9,8 @@ import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
|||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/pages/devices/devices.dart';
|
||||
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||
import 'package:selfprivacy/ui/pages/users/users.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
|
||||
import 'package:selfprivacy/ui/pages/more/about_us.dart';
|
||||
import 'package:selfprivacy/ui/pages/more/app_settings/app_setting.dart';
|
||||
import 'package:selfprivacy/ui/pages/more/console.dart';
|
||||
import 'package:selfprivacy/ui/pages/more/about_application.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
|
||||
class MorePage extends StatelessWidget {
|
||||
const MorePage({super.key});
|
||||
|
@ -34,12 +24,14 @@ class MorePage extends StatelessWidget {
|
|||
context.watch<ApiServerVolumeCubit>().state.usesBinds;
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.more'.tr(),
|
||||
),
|
||||
),
|
||||
appBar: Breakpoints.small.isActive(context)
|
||||
? PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.more'.tr(),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
body: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
|
@ -50,7 +42,7 @@ class MorePage extends StatelessWidget {
|
|||
_MoreMenuItem(
|
||||
title: 'storage.start_migration_button'.tr(),
|
||||
iconData: Icons.drive_file_move_outline,
|
||||
goTo: ServicesMigrationPage(
|
||||
goTo: () => ServicesMigrationRoute(
|
||||
diskStatus: context
|
||||
.watch<ApiServerVolumeCubit>()
|
||||
.state
|
||||
|
@ -77,7 +69,7 @@ class MorePage extends StatelessWidget {
|
|||
_MoreMenuItem(
|
||||
title: 'more_page.configuration_wizard'.tr(),
|
||||
iconData: Icons.change_history_outlined,
|
||||
goTo: const InitializingPage(),
|
||||
goTo: () => const InitializingRoute(),
|
||||
subtitle: 'not_ready_card.in_menu'.tr(),
|
||||
accent: true,
|
||||
),
|
||||
|
@ -85,47 +77,43 @@ class MorePage extends StatelessWidget {
|
|||
_MoreMenuItem(
|
||||
title: 'more_page.create_ssh_key'.tr(),
|
||||
iconData: Ionicons.key_outline,
|
||||
goTo: const UserDetails(
|
||||
goTo: () => UserDetailsRoute(
|
||||
login: 'root',
|
||||
),
|
||||
),
|
||||
if (isReady)
|
||||
_MoreMenuItem(
|
||||
iconData: Icons.password_outlined,
|
||||
goTo: const RecoveryKey(),
|
||||
goTo: () => const RecoveryKeyRoute(),
|
||||
title: 'recovery_key.key_main_header'.tr(),
|
||||
),
|
||||
if (isReady)
|
||||
_MoreMenuItem(
|
||||
iconData: Icons.devices_outlined,
|
||||
goTo: const DevicesScreen(),
|
||||
goTo: () => const DevicesRoute(),
|
||||
title: 'devices.main_screen.header'.tr(),
|
||||
),
|
||||
_MoreMenuItem(
|
||||
title: 'more_page.application_settings'.tr(),
|
||||
iconData: Icons.settings_outlined,
|
||||
goTo: const AppSettingsPage(),
|
||||
),
|
||||
_MoreMenuItem(
|
||||
title: 'more_page.about_project'.tr(),
|
||||
iconData: BrandIcons.engineer,
|
||||
goTo: const AboutUsPage(),
|
||||
goTo: () => const AppSettingsRoute(),
|
||||
),
|
||||
_MoreMenuItem(
|
||||
title: 'more_page.about_application'.tr(),
|
||||
iconData: BrandIcons.fire,
|
||||
goTo: const AboutApplicationPage(),
|
||||
goTo: () => const AboutApplicationRoute(),
|
||||
longGoTo: const DeveloperSettingsRoute(),
|
||||
),
|
||||
if (!isReady)
|
||||
_MoreMenuItem(
|
||||
title: 'more_page.onboarding'.tr(),
|
||||
iconData: BrandIcons.start,
|
||||
goTo: const OnboardingPage(nextPage: RootPage()),
|
||||
goTo: () => const OnboardingRoute(),
|
||||
),
|
||||
_MoreMenuItem(
|
||||
title: 'more_page.console'.tr(),
|
||||
iconData: BrandIcons.terminal,
|
||||
goTo: const Console(),
|
||||
goTo: () => const ConsoleRoute(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -140,14 +128,16 @@ class _MoreMenuItem extends StatelessWidget {
|
|||
const _MoreMenuItem({
|
||||
required this.iconData,
|
||||
required this.title,
|
||||
required this.goTo,
|
||||
this.subtitle,
|
||||
this.goTo,
|
||||
this.longGoTo,
|
||||
this.accent = false,
|
||||
});
|
||||
|
||||
final IconData iconData;
|
||||
final String title;
|
||||
final Widget? goTo;
|
||||
final PageRouteInfo Function() goTo;
|
||||
final PageRouteInfo? longGoTo;
|
||||
final String? subtitle;
|
||||
final bool accent;
|
||||
|
||||
|
@ -160,9 +150,9 @@ class _MoreMenuItem extends StatelessWidget {
|
|||
tertiary: accent,
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
onTap: goTo != null
|
||||
? () => Navigator.of(context).push(materialRoute(goTo!))
|
||||
: null,
|
||||
onTap: () => context.pushRoute(goTo()),
|
||||
onLongPress:
|
||||
longGoTo != null ? () => context.pushRoute(longGoTo!) : null,
|
||||
leading: Icon(
|
||||
iconData,
|
||||
size: 24,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class OnboardingPage extends StatefulWidget {
|
||||
const OnboardingPage({required this.nextPage, super.key});
|
||||
const OnboardingPage({super.key});
|
||||
|
||||
final Widget nextPage;
|
||||
@override
|
||||
State<OnboardingPage> createState() => _OnboardingPageState();
|
||||
}
|
||||
|
@ -22,14 +22,14 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
|||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Scaffold(
|
||||
body: PageView(
|
||||
controller: pageController,
|
||||
children: [
|
||||
_withPadding(firstPage()),
|
||||
_withPadding(secondPage()),
|
||||
],
|
||||
),
|
||||
);
|
||||
body: PageView(
|
||||
controller: pageController,
|
||||
children: [
|
||||
_withPadding(firstPage()),
|
||||
_withPadding(secondPage()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _withPadding(final Widget child) => Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
@ -142,10 +142,10 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
|||
BrandButton.rised(
|
||||
onPressed: () {
|
||||
context.read<AppSettingsCubit>().turnOffOnboarding();
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
materialRoute(widget.nextPage),
|
||||
(final route) => false,
|
||||
);
|
||||
context.router.replaceAll([
|
||||
const RootRoute(),
|
||||
const InitializingRoute(),
|
||||
]);
|
||||
},
|
||||
text: 'basis.got_it'.tr(),
|
||||
),
|
||||
|
|
|
@ -13,6 +13,7 @@ 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/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/route_transitions/basic.dart';
|
||||
|
||||
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
@ -61,12 +62,14 @@ class _ProvidersPageState extends State<ProvidersPage> {
|
|||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.providers_title'.tr(),
|
||||
),
|
||||
),
|
||||
appBar: Breakpoints.small.isActive(context)
|
||||
? PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.providers_title'.tr(),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
body: ListView(
|
||||
padding: paddingH15V0,
|
||||
children: [
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -8,18 +7,18 @@ import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart';
|
|||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key_receiving.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
|
||||
class RecoveryKey extends StatefulWidget {
|
||||
const RecoveryKey({super.key});
|
||||
class RecoveryKeyPage extends StatefulWidget {
|
||||
const RecoveryKeyPage({super.key});
|
||||
|
||||
@override
|
||||
State<RecoveryKey> createState() => _RecoveryKeyState();
|
||||
State<RecoveryKeyPage> createState() => _RecoveryKeyPageState();
|
||||
}
|
||||
|
||||
class _RecoveryKeyState extends State<RecoveryKey> {
|
||||
class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
@ -29,7 +28,7 @@ class _RecoveryKeyState extends State<RecoveryKey> {
|
|||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final RecoveryKeyState keyStatus = context.watch<RecoveryKeyCubit>().state;
|
||||
|
||||
|
||||
final List<Widget> widgets;
|
||||
String? subtitle =
|
||||
keyStatus.exists ? null : 'recovery_key.key_main_description'.tr();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
|
||||
class RecoveryKeyReceiving extends StatelessWidget {
|
||||
|
|
|
@ -1,89 +1,149 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart';
|
||||
import 'package:selfprivacy/ui/pages/more/more.dart';
|
||||
import 'package:selfprivacy/ui/pages/providers/providers.dart';
|
||||
import 'package:selfprivacy/ui/pages/services/services.dart';
|
||||
import 'package:selfprivacy/ui/pages/users/users.dart';
|
||||
import 'package:selfprivacy/ui/layouts/root_scaffold_with_navigation.dart';
|
||||
import 'package:selfprivacy/ui/router/root_destinations.dart';
|
||||
|
||||
import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
|
||||
class RootPage extends StatefulWidget {
|
||||
class RootPage extends StatefulWidget implements AutoRouteWrapper {
|
||||
const RootPage({super.key});
|
||||
|
||||
@override
|
||||
State<RootPage> createState() => _RootPageState();
|
||||
|
||||
@override
|
||||
Widget wrappedRoute(final BuildContext context) => this;
|
||||
}
|
||||
|
||||
class _RootPageState extends State<RootPage> with TickerProviderStateMixin {
|
||||
late TabController tabController;
|
||||
bool shouldUseSplitView() => false;
|
||||
|
||||
late final AnimationController _controller = AnimationController(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
vsync: this,
|
||||
);
|
||||
late final Animation<double> _animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
tabController = TabController(length: 4, vsync: this);
|
||||
tabController.addListener(() {
|
||||
setState(() {
|
||||
tabController.index == 2
|
||||
? _controller.forward()
|
||||
: _controller.reverse();
|
||||
});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
tabController.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
final destinations = rootDestinations;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final bool isReady = context.watch<ServerInstallationCubit>().state
|
||||
is ServerInstallationFinished;
|
||||
|
||||
return Provider<ChangeTab>(
|
||||
create: (final _) => ChangeTab(tabController.animateTo),
|
||||
child: Scaffold(
|
||||
body: TabBarView(
|
||||
controller: tabController,
|
||||
children: const [
|
||||
ProvidersPage(),
|
||||
ServicesPage(),
|
||||
UsersPage(),
|
||||
MorePage(),
|
||||
if (context.read<AppSettingsCubit>().state.isOnboardingShowing) {
|
||||
context.router.replace(const OnboardingRoute());
|
||||
}
|
||||
|
||||
return AutoRouter(
|
||||
builder: (final context, final child) {
|
||||
final currentDestinationIndex = destinations.indexWhere(
|
||||
(final destination) =>
|
||||
context.router.isRouteActive(destination.route.routeName),
|
||||
);
|
||||
final routeName = getRouteTitle(context.router.current.name).tr();
|
||||
return RootScaffoldWithNavigation(
|
||||
title: routeName,
|
||||
destinations: destinations,
|
||||
showBottomBar: !(currentDestinationIndex == -1),
|
||||
showFab: isReady,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MainScreenNavigationRail extends StatelessWidget {
|
||||
const MainScreenNavigationRail({
|
||||
required this.destinations,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<RouteDestination> destinations;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
int? activeIndex = destinations.indexWhere(
|
||||
(final destination) =>
|
||||
context.router.isRouteActive(destination.route.routeName),
|
||||
);
|
||||
if (activeIndex == -1) {
|
||||
activeIndex = null;
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: 72,
|
||||
child: LayoutBuilder(
|
||||
builder: (final context, final constraints) => SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
||||
child: IntrinsicHeight(
|
||||
child: NavigationRail(
|
||||
backgroundColor: Colors.transparent,
|
||||
labelType: NavigationRailLabelType.all,
|
||||
destinations: destinations
|
||||
.map(
|
||||
(final destination) => NavigationRailDestination(
|
||||
icon: Icon(destination.icon),
|
||||
label: Text(destination.label),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
selectedIndex: activeIndex,
|
||||
onDestinationSelected: (final index) {
|
||||
context.router.replaceAll([destinations[index].route]);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MainScreenNavigationDrawer extends StatelessWidget {
|
||||
const MainScreenNavigationDrawer({
|
||||
required this.destinations,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<RouteDestination> destinations;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
int? activeIndex = destinations.indexWhere(
|
||||
(final destination) =>
|
||||
context.router.isRouteActive(destination.route.routeName),
|
||||
);
|
||||
if (activeIndex == -1) {
|
||||
activeIndex = null;
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: 296,
|
||||
child: LayoutBuilder(
|
||||
builder: (final context, final constraints) => NavigationDrawer(
|
||||
// backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
|
||||
// surfaceTintColor: Colors.transparent,
|
||||
key: const Key('PrimaryNavigationDrawer'),
|
||||
selectedIndex: activeIndex,
|
||||
onDestinationSelected: (final index) {
|
||||
context.router.replaceAll([destinations[index].route]);
|
||||
},
|
||||
children: [
|
||||
const SizedBox(height: 18),
|
||||
...destinations.map(
|
||||
(final destination) => NavigationDrawerDestination(
|
||||
icon: Icon(destination.icon),
|
||||
label: Text(destination.label),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: BrandTabBar(
|
||||
controller: tabController,
|
||||
),
|
||||
floatingActionButton: isReady
|
||||
? SizedBox(
|
||||
height: 104 + 16,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ScaleTransition(
|
||||
scale: _animation,
|
||||
child: const AddUserFab(),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const BrandFab(),
|
||||
],
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
|||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/segmented_buttons.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.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_text/brand_text.dart';
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart';
|
||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'
|
|||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||
import 'package:selfprivacy/logic/models/price.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.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';
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
|
|||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.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';
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
|||
import 'package:selfprivacy/logic/models/job.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/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/utils/launch_url.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
|
@ -50,7 +50,11 @@ class _ServicePageState extends State<ServicePage> {
|
|||
service.svgIcon,
|
||||
width: 48.0,
|
||||
height: 48.0,
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onBackground,
|
||||
BlendMode.srcIn,
|
||||
)
|
||||
// color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
heroTitle: service.displayName,
|
||||
children: [
|
||||
|
|
|
@ -12,6 +12,7 @@ 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:easy_localization/easy_localization.dart';
|
||||
import 'package:selfprivacy/ui/pages/services/service_page.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
import 'package:selfprivacy/utils/launch_url.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||
|
@ -34,12 +35,14 @@ class _ServicesPageState extends State<ServicesPage> {
|
|||
.sort((final a, final b) => a.status.index.compareTo(b.status.index));
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.services'.tr(),
|
||||
),
|
||||
),
|
||||
appBar: Breakpoints.small.isActive(context)
|
||||
? PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.services'.tr(),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
context.read<ServicesCubit>().reload();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
||||
|
@ -9,17 +9,19 @@ import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_
|
|||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart';
|
||||
import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart';
|
||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||
import 'package:selfprivacy/ui/components/support_drawer/support_drawer.dart';
|
||||
import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/initializing/server_provider_picker.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/initializing/server_type_picker.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
import 'package:selfprivacy/utils/breakpoints.dart';
|
||||
|
||||
class InitializingPage extends StatelessWidget {
|
||||
const InitializingPage({super.key});
|
||||
|
@ -48,99 +50,123 @@ class InitializingPage extends StatelessWidget {
|
|||
][cubit.state.progress.index]();
|
||||
}
|
||||
|
||||
const steps = [
|
||||
'initializing.steps.hosting',
|
||||
'initializing.steps.server_type',
|
||||
'initializing.steps.dns_provider',
|
||||
'initializing.steps.backups_provider',
|
||||
'initializing.steps.domain',
|
||||
'initializing.steps.master_account',
|
||||
'initializing.steps.server',
|
||||
'initializing.steps.dns_setup',
|
||||
'initializing.steps.nixos_installation',
|
||||
'initializing.steps.server_reboot',
|
||||
'initializing.steps.final_checks',
|
||||
];
|
||||
|
||||
return BlocListener<ServerInstallationCubit, ServerInstallationState>(
|
||||
listener: (final context, final state) {
|
||||
if (cubit.state is ServerInstallationFinished) {
|
||||
Navigator.of(context)
|
||||
.pushReplacement(materialRoute(const RootPage()));
|
||||
context.router.popUntilRoot();
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
actions: [
|
||||
if (cubit.state is ServerInstallationFinished)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.check),
|
||||
onPressed: () {
|
||||
Navigator.of(context)
|
||||
.pushReplacement(materialRoute(const RootPage()));
|
||||
},
|
||||
)
|
||||
],
|
||||
title: Text(
|
||||
'more_page.configuration_wizard'.tr(),
|
||||
),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(28),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
child: ProgressBar(
|
||||
steps: const [
|
||||
'Hosting',
|
||||
'Server Type',
|
||||
'CloudFlare',
|
||||
'Backblaze',
|
||||
'Domain',
|
||||
'User',
|
||||
'Server',
|
||||
'Installation',
|
||||
],
|
||||
activeIndex: cubit.state.porgressBar,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0.0),
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: actualInitializingPage,
|
||||
),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: MediaQuery.of(context).size.height -
|
||||
MediaQuery.of(context).padding.top -
|
||||
MediaQuery.of(context).padding.bottom -
|
||||
566,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: BrandButton.text(
|
||||
title: cubit.state is ServerInstallationFinished
|
||||
? 'basis.close'.tr()
|
||||
: 'basis.later'.tr(),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
materialRoute(const RootPage()),
|
||||
(final predicate) => false,
|
||||
);
|
||||
},
|
||||
),
|
||||
endDrawer: const SupportDrawer(),
|
||||
endDrawerEnableOpenDragGesture: false,
|
||||
appBar: Breakpoints.large.isActive(context)
|
||||
? null
|
||||
: AppBar(
|
||||
actions: [
|
||||
if (cubit.state is ServerInstallationFinished)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.check),
|
||||
onPressed: () {
|
||||
context.router.popUntilRoot();
|
||||
},
|
||||
),
|
||||
if (cubit.state is ServerInstallationEmpty ||
|
||||
cubit.state is ServerInstallationNotFinished)
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: BrandButton.text(
|
||||
title: 'basis.connect_to_existing'.tr(),
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
materialRoute(
|
||||
const RecoveryRouting(),
|
||||
),
|
||||
);
|
||||
},
|
||||
const SizedBox.shrink(),
|
||||
],
|
||||
title: Text(
|
||||
'more_page.configuration_wizard'.tr(),
|
||||
),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(28),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
child: ProgressBar(
|
||||
steps: const [
|
||||
'Hosting',
|
||||
'Server Type',
|
||||
'CloudFlare',
|
||||
'Backblaze',
|
||||
'Domain',
|
||||
'User',
|
||||
'Server',
|
||||
'Installation',
|
||||
],
|
||||
activeIndex: cubit.state.porgressBar,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: LayoutBuilder(
|
||||
builder: (final context, final constraints) => Row(
|
||||
children: [
|
||||
if (Breakpoints.large.isActive(context))
|
||||
_ProgressDrawer(
|
||||
steps: steps,
|
||||
cubit: cubit,
|
||||
constraints: constraints,
|
||||
),
|
||||
SizedBox(
|
||||
width: constraints.maxWidth -
|
||||
(Breakpoints.large.isActive(context) ? 300 : 0),
|
||||
height: constraints.maxHeight,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: Breakpoints.large.isActive(context)
|
||||
? const EdgeInsets.all(16.0)
|
||||
: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0.0),
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: actualInitializingPage,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
if (!Breakpoints.large.isActive(context))
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: BrandButton.text(
|
||||
title:
|
||||
cubit.state is ServerInstallationFinished
|
||||
? 'basis.close'.tr()
|
||||
: 'basis.later'.tr(),
|
||||
onPressed: () {
|
||||
context.router.popUntilRoot();
|
||||
},
|
||||
),
|
||||
),
|
||||
if (cubit.state is ServerInstallationEmpty ||
|
||||
cubit.state is ServerInstallationNotFinished)
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: BrandButton.text(
|
||||
title: 'basis.connect_to_existing'.tr(),
|
||||
onPressed: () {
|
||||
context.router
|
||||
.replace(const RecoveryRoute());
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -179,57 +205,55 @@ class InitializingPage extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
|
||||
void _showModal(final BuildContext context, final Widget widget) {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (final BuildContext context) => widget,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _stepCloudflare(final ServerInstallationCubit initializingCubit) =>
|
||||
BlocProvider(
|
||||
create: (final context) => DnsProviderFormCubit(initializingCubit),
|
||||
child: Builder(
|
||||
builder: (final context) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${'initializing.connect_to_server_provider'.tr()}Cloudflare',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.manage_domain_dns'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CubitFormTextField(
|
||||
formFieldCubit: context.read<DnsProviderFormCubit>().apiKey,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'initializing.cloudflare_api_token'.tr(),
|
||||
builder: (final context) => ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${'initializing.connect_to_server_provider'.tr()}Cloudflare',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.rised(
|
||||
onPressed: () =>
|
||||
context.read<DnsProviderFormCubit>().trySubmit(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BrandButton.text(
|
||||
onPressed: () => _showModal(
|
||||
context,
|
||||
const _HowTo(
|
||||
fileName: 'how_cloudflare',
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.manage_domain_dns'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CubitFormTextField(
|
||||
formFieldCubit: context.read<DnsProviderFormCubit>().apiKey,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'initializing.cloudflare_api_token'.tr(),
|
||||
),
|
||||
),
|
||||
title: 'initializing.how'.tr(),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.filled(
|
||||
onPressed: () =>
|
||||
context.read<DnsProviderFormCubit>().trySubmit(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BrandOutlinedButton(
|
||||
onPressed: () {
|
||||
context.read<SupportSystemCubit>().showArticle(
|
||||
article: 'how_cloudflare',
|
||||
context: context,
|
||||
);
|
||||
Scaffold.of(context).openEndDrawer();
|
||||
},
|
||||
title: 'initializing.how'.tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -240,50 +264,57 @@ class InitializingPage extends StatelessWidget {
|
|||
child: Builder(
|
||||
builder: (final context) {
|
||||
final formCubitState = context.watch<BackblazeFormCubit>().state;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${'initializing.connect_to_server_provider'.tr()}Backblaze',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CubitFormTextField(
|
||||
formFieldCubit: context.read<BackblazeFormCubit>().keyId,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'KeyID',
|
||||
return ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${'initializing.connect_to_server_provider'.tr()}Backblaze',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
CubitFormTextField(
|
||||
formFieldCubit:
|
||||
context.read<BackblazeFormCubit>().applicationKey,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Master Application Key',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.rised(
|
||||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<BackblazeFormCubit>().trySubmit(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BrandButton.text(
|
||||
onPressed: () => _showModal(
|
||||
context,
|
||||
const _HowTo(
|
||||
fileName: 'how_backblaze',
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CubitFormTextField(
|
||||
formFieldCubit: context.read<BackblazeFormCubit>().keyId,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'KeyID',
|
||||
),
|
||||
),
|
||||
title: 'initializing.how'.tr(),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
CubitFormTextField(
|
||||
formFieldCubit:
|
||||
context.read<BackblazeFormCubit>().applicationKey,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Master Application Key',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.rised(
|
||||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<BackblazeFormCubit>().trySubmit(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BrandButton.text(
|
||||
onPressed: () {
|
||||
context.read<SupportSystemCubit>().showArticle(
|
||||
article: 'how_backblaze',
|
||||
context: context,
|
||||
);
|
||||
Scaffold.of(context).openEndDrawer();
|
||||
},
|
||||
title: 'initializing.how'.tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -296,9 +327,8 @@ class InitializingPage extends StatelessWidget {
|
|||
builder: (final context) {
|
||||
final DomainSetupState state =
|
||||
context.watch<DomainSetupCubit>().state;
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
return ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
|
@ -310,7 +340,11 @@ class InitializingPage extends StatelessWidget {
|
|||
'initializing.use_this_domain_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (state is Empty)
|
||||
Text(
|
||||
'initializing.no_connected_domains'.tr(),
|
||||
|
@ -350,7 +384,7 @@ class InitializingPage extends StatelessWidget {
|
|||
],
|
||||
if (state is Empty) ...[
|
||||
const SizedBox(height: 30),
|
||||
BrandButton.rised(
|
||||
BrandButton.filled(
|
||||
onPressed: () => context.read<DomainSetupCubit>().load(),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
@ -367,7 +401,7 @@ class InitializingPage extends StatelessWidget {
|
|||
],
|
||||
if (state is Loaded) ...[
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.rised(
|
||||
BrandButton.filled(
|
||||
onPressed: () =>
|
||||
context.read<DomainSetupCubit>().saveDomain(),
|
||||
text: 'initializing.save_domain'.tr(),
|
||||
|
@ -388,74 +422,83 @@ class InitializingPage extends StatelessWidget {
|
|||
builder: (final context) {
|
||||
final formCubitState = context.watch<RootUserFormCubit>().state;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.create_master_account'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.enter_username_and_password'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
if (formCubitState.isErrorShown) const SizedBox(height: 16),
|
||||
if (formCubitState.isErrorShown)
|
||||
return ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'users.username_rule'.tr(),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
'initializing.create_master_account'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.enter_username_and_password'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (formCubitState.isErrorShown) const SizedBox(height: 16),
|
||||
if (formCubitState.isErrorShown)
|
||||
Text(
|
||||
'users.username_rule'.tr(),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CubitFormTextField(
|
||||
formFieldCubit: context.read<RootUserFormCubit>().userName,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'basis.username'.tr(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CubitFormTextField(
|
||||
formFieldCubit: context.read<RootUserFormCubit>().userName,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'basis.username'.tr(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
|
||||
bloc: context.read<RootUserFormCubit>().isVisible,
|
||||
builder: (final context, final state) {
|
||||
final bool isVisible = state.value;
|
||||
return CubitFormTextField(
|
||||
obscureText: !isVisible,
|
||||
formFieldCubit:
|
||||
context.read<RootUserFormCubit>().password,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'basis.password'.tr(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
isVisible ? Icons.visibility : Icons.visibility_off,
|
||||
const SizedBox(height: 16),
|
||||
BlocBuilder<FieldCubit<bool>, FieldCubitState<bool>>(
|
||||
bloc: context.read<RootUserFormCubit>().isVisible,
|
||||
builder: (final context, final state) {
|
||||
final bool isVisible = state.value;
|
||||
return CubitFormTextField(
|
||||
obscureText: !isVisible,
|
||||
formFieldCubit:
|
||||
context.read<RootUserFormCubit>().password,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'basis.password'.tr(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
isVisible
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
),
|
||||
onPressed: () => context
|
||||
.read<RootUserFormCubit>()
|
||||
.isVisible
|
||||
.setValue(!isVisible),
|
||||
),
|
||||
onPressed: () => context
|
||||
.read<RootUserFormCubit>()
|
||||
.isVisible
|
||||
.setValue(!isVisible),
|
||||
suffixIconConstraints:
|
||||
const BoxConstraints(minWidth: 60),
|
||||
prefixIconConstraints:
|
||||
const BoxConstraints(maxWidth: 60),
|
||||
prefixIcon: Container(),
|
||||
),
|
||||
suffixIconConstraints:
|
||||
const BoxConstraints(minWidth: 60),
|
||||
prefixIconConstraints:
|
||||
const BoxConstraints(maxWidth: 60),
|
||||
prefixIcon: Container(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.rised(
|
||||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<RootUserFormCubit>().trySubmit(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.filled(
|
||||
onPressed: formCubitState.isSubmitting
|
||||
? null
|
||||
: () => context.read<RootUserFormCubit>().trySubmit(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -465,27 +508,28 @@ class InitializingPage extends StatelessWidget {
|
|||
final bool isLoading =
|
||||
(appConfigCubit.state as ServerInstallationNotFinished).isLoading;
|
||||
return Builder(
|
||||
builder: (final context) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.final'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.create_server'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 128),
|
||||
BrandButton.rised(
|
||||
onPressed:
|
||||
isLoading ? null : appConfigCubit.createServerAndSetDnsRecords,
|
||||
text: isLoading
|
||||
? 'basis.loading'.tr()
|
||||
: 'initializing.create_server'.tr(),
|
||||
),
|
||||
],
|
||||
builder: (final context) => ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.final'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.create_server'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: BrandButton.filled(
|
||||
onPressed:
|
||||
isLoading ? null : appConfigCubit.createServerAndSetDnsRecords,
|
||||
text: isLoading
|
||||
? 'basis.loading'.tr()
|
||||
: 'initializing.create_server'.tr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -514,84 +558,200 @@ class InitializingPage extends StatelessWidget {
|
|||
return Builder(
|
||||
builder: (final context) => SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.checks'.tr(args: [doneCount.toString(), '4']),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (text != null)
|
||||
child: ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
'initializing.checks'.tr(args: [doneCount.toString(), '4']),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 128),
|
||||
const SizedBox(height: 10),
|
||||
if (doneCount == 0 && state.dnsMatches != null)
|
||||
Column(
|
||||
children: state.dnsMatches!.entries.map((final entry) {
|
||||
final String domain = entry.key;
|
||||
final bool isCorrect = entry.value;
|
||||
return Row(
|
||||
children: [
|
||||
if (isCorrect)
|
||||
const Icon(Icons.check, color: Colors.green),
|
||||
if (!isCorrect)
|
||||
const Icon(Icons.schedule, color: Colors.amber),
|
||||
const SizedBox(width: 10),
|
||||
Text(domain),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (!state.isLoading)
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'initializing.until_the_next_check'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
BrandTimer(
|
||||
startDateTime: state.timerStart!,
|
||||
duration: state.duration!,
|
||||
)
|
||||
],
|
||||
),
|
||||
if (state.isLoading)
|
||||
Text(
|
||||
'initializing.check'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
if (text != null)
|
||||
Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 128),
|
||||
const SizedBox(height: 10),
|
||||
if (doneCount == 0 && state.dnsMatches != null)
|
||||
Column(
|
||||
children: state.dnsMatches!.entries.map((final entry) {
|
||||
final String domain = entry.key;
|
||||
final bool isCorrect = entry.value;
|
||||
return Row(
|
||||
children: [
|
||||
if (isCorrect)
|
||||
const Icon(Icons.check, color: Colors.green),
|
||||
if (!isCorrect)
|
||||
const Icon(Icons.schedule, color: Colors.amber),
|
||||
const SizedBox(width: 10),
|
||||
Text(domain),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (!state.isLoading)
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'initializing.until_the_next_check'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
BrandTimer(
|
||||
startDateTime: state.timerStart!,
|
||||
duration: state.duration!,
|
||||
)
|
||||
],
|
||||
),
|
||||
if (state.isLoading)
|
||||
Text(
|
||||
'initializing.check'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HowTo extends StatelessWidget {
|
||||
const _HowTo({
|
||||
required this.fileName,
|
||||
class _ProgressDrawer extends StatelessWidget {
|
||||
const _ProgressDrawer({
|
||||
required this.steps,
|
||||
required this.cubit,
|
||||
required this.constraints,
|
||||
});
|
||||
|
||||
final String fileName;
|
||||
final List<String> steps;
|
||||
final ServerInstallationCubit cubit;
|
||||
final BoxConstraints constraints;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => BrandBottomSheet(
|
||||
isExpended: true,
|
||||
child: Padding(
|
||||
padding: paddingH15V0,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
Widget build(final BuildContext context) => SizedBox(
|
||||
width: 300,
|
||||
height: constraints.maxHeight,
|
||||
child: Drawer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
BrandMarkdown(
|
||||
fileName: fileName,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'more_page.configuration_wizard'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
fit: FlexFit.tight,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
...steps.map((final step) {
|
||||
final index = steps.indexOf(step);
|
||||
return _StepIndicator(
|
||||
title: step.tr(),
|
||||
isCurrent: index == cubit.state.progress.index,
|
||||
isCompleted: index < cubit.state.progress.index,
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// const Spacer(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (cubit.state is ServerInstallationEmpty ||
|
||||
cubit.state is ServerInstallationNotFinished)
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: BrandButton.filled(
|
||||
text: 'basis.connect_to_existing'.tr(),
|
||||
onPressed: () {
|
||||
context.router.replace(const RecoveryRoute());
|
||||
},
|
||||
),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: double.infinity,
|
||||
),
|
||||
child: OutlinedButton(
|
||||
child: Text(
|
||||
cubit.state is ServerInstallationFinished
|
||||
? 'basis.close'.tr()
|
||||
: 'basis.later'.tr(),
|
||||
),
|
||||
onPressed: () {
|
||||
context.router.popUntilRoot();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _StepIndicator extends StatelessWidget {
|
||||
const _StepIndicator({
|
||||
required this.title,
|
||||
required this.isCompleted,
|
||||
required this.isCurrent,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final bool isCompleted;
|
||||
final bool isCurrent;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => ListTile(
|
||||
selected: isCurrent,
|
||||
leading: isCurrent
|
||||
? const _StepCurrentIcon()
|
||||
: isCompleted
|
||||
? const _StepCompletedIcon()
|
||||
: const _StepPendingIcon(),
|
||||
title: Text(
|
||||
title,
|
||||
),
|
||||
textColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
);
|
||||
}
|
||||
|
||||
class _StepCompletedIcon extends StatelessWidget {
|
||||
const _StepCompletedIcon();
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => const Icon(Icons.check_circle);
|
||||
}
|
||||
|
||||
class _StepPendingIcon extends StatelessWidget {
|
||||
const _StepPendingIcon();
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => const Icon(Icons.circle_outlined);
|
||||
}
|
||||
|
||||
class _StepCurrentIcon extends StatelessWidget {
|
||||
const _StepCurrentIcon();
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) =>
|
||||
const Icon(Icons.build_circle_outlined);
|
||||
}
|
||||
|
|
|
@ -2,16 +2,15 @@ import 'package:cubit_form/cubit_form.dart';
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:selfprivacy/config/brand_theme.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart';
|
||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart';
|
||||
import 'package:selfprivacy/utils/launch_url.dart';
|
||||
|
||||
class ServerProviderPicker extends StatefulWidget {
|
||||
|
@ -98,56 +97,49 @@ class ProviderInputDataPage extends StatelessWidget {
|
|||
final ProviderFormCubit providerCubit;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${'initializing.connect_to_server_provider'.tr()}${providerInfo.providerType.displayName}",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.connect_to_server_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
CubitFormTextField(
|
||||
formFieldCubit: providerCubit.apiKey,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Provider API Token',
|
||||
Widget build(final BuildContext context) => ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${'initializing.connect_to_server_provider'.tr()}${providerInfo.providerType.displayName}",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.filled(
|
||||
child: Text('basis.connect'.tr()),
|
||||
onPressed: () => providerCubit.trySubmit(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BrandOutlinedButton(
|
||||
child: Text('initializing.how'.tr()),
|
||||
onPressed: () => showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (final BuildContext context) => BrandBottomSheet(
|
||||
isExpended: true,
|
||||
child: Padding(
|
||||
padding: paddingH15V0,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
children: [
|
||||
BrandMarkdown(
|
||||
fileName: providerInfo.pathToHow,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.connect_to_server_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CubitFormTextField(
|
||||
formFieldCubit: providerCubit.apiKey,
|
||||
textAlign: TextAlign.center,
|
||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Provider API Token',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 32),
|
||||
BrandButton.filled(
|
||||
child: Text('basis.connect'.tr()),
|
||||
onPressed: () => providerCubit.trySubmit(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BrandOutlinedButton(
|
||||
child: Text('initializing.how'.tr()),
|
||||
onPressed: () {
|
||||
context.read<SupportSystemCubit>().showArticle(
|
||||
article: providerInfo.pathToHow,
|
||||
context: context,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -164,175 +156,182 @@ class ProviderSelectionPage extends StatelessWidget {
|
|||
@override
|
||||
Widget build(final BuildContext context) => SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.connect_to_server'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'initializing.select_provider'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
OutlinedCard(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
color: const Color(0xFFD50C2D),
|
||||
child: ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.connect_to_server'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'initializing.select_provider'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
children: [
|
||||
OutlinedCard(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
color: const Color(0xFFD50C2D),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/logos/hetzner.svg',
|
||||
),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/logos/hetzner.svg',
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Hetzner Cloud',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Hetzner Cloud',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_countries_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_countries_text_hetzner'
|
||||
.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_price_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_price_text_hetzner'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_payment_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_payment_text_hetzner'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_email_notice'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
BrandButton.filled(
|
||||
child: Text('basis.select'.tr()),
|
||||
onPressed: () {
|
||||
serverInstallationCubit
|
||||
.setServerProviderType(ServerProvider.hetzner);
|
||||
callback(ServerProvider.hetzner);
|
||||
},
|
||||
),
|
||||
// Outlined button that will open website
|
||||
BrandOutlinedButton(
|
||||
onPressed: () =>
|
||||
launchURL('https://www.hetzner.com/cloud'),
|
||||
title: 'initializing.select_provider_site_button'.tr(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_countries_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_countries_text_hetzner'
|
||||
.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_price_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_price_text_hetzner'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_payment_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_payment_text_hetzner'
|
||||
.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_email_notice'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
BrandButton.filled(
|
||||
child: Text('basis.select'.tr()),
|
||||
onPressed: () {
|
||||
serverInstallationCubit
|
||||
.setServerProviderType(ServerProvider.hetzner);
|
||||
callback(ServerProvider.hetzner);
|
||||
},
|
||||
),
|
||||
// Outlined button that will open website
|
||||
BrandOutlinedButton(
|
||||
onPressed: () =>
|
||||
launchURL('https://www.hetzner.com/cloud'),
|
||||
title: 'initializing.select_provider_site_button'.tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
OutlinedCard(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
color: const Color(0xFF0080FF),
|
||||
const SizedBox(height: 16),
|
||||
OutlinedCard(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
color: const Color(0xFF0080FF),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/logos/digital_ocean.svg',
|
||||
),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/logos/digital_ocean.svg',
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Digital Ocean',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Digital Ocean',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_countries_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_countries_text_do'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_price_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_price_text_do'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_payment_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_payment_text_do'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
BrandButton.filled(
|
||||
child: Text('basis.select'.tr()),
|
||||
onPressed: () {
|
||||
serverInstallationCubit
|
||||
.setServerProviderType(ServerProvider.digitalOcean);
|
||||
callback(ServerProvider.digitalOcean);
|
||||
},
|
||||
),
|
||||
// Outlined button that will open website
|
||||
BrandOutlinedButton(
|
||||
onPressed: () =>
|
||||
launchURL('https://www.digitalocean.com'),
|
||||
title: 'initializing.select_provider_site_button'.tr(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_countries_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_countries_text_do'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_price_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_price_text_do'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.select_provider_payment_title'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text(
|
||||
'initializing.select_provider_payment_text_do'.tr(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
BrandButton.filled(
|
||||
child: Text('basis.select'.tr()),
|
||||
onPressed: () {
|
||||
serverInstallationCubit.setServerProviderType(
|
||||
ServerProvider.digitalOcean,
|
||||
);
|
||||
callback(ServerProvider.digitalOcean);
|
||||
},
|
||||
),
|
||||
// Outlined button that will open website
|
||||
BrandOutlinedButton(
|
||||
onPressed: () =>
|
||||
launchURL('https://www.digitalocean.com'),
|
||||
title: 'initializing.select_provider_site_button'.tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InfoBox(text: 'initializing.select_provider_notice'.tr()),
|
||||
],
|
||||
],
|
||||
),
|
||||
secondaryColumn:
|
||||
InfoBox(text: 'initializing.select_provider_notice'.tr()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
|||
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/info_box/info_box.dart';
|
||||
import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart';
|
||||
|
||||
class ServerTypePicker extends StatefulWidget {
|
||||
const ServerTypePicker({
|
||||
|
@ -70,50 +71,67 @@ class SelectLocationPage extends StatelessWidget {
|
|||
if ((snapshot.data as List<ServerProviderLocation>).isEmpty) {
|
||||
return Text('initializing.no_locations_found'.tr());
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
'initializing.choose_location_type'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.choose_location_type_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...(snapshot.data! as List<ServerProviderLocation>).map(
|
||||
(final location) => SizedBox(
|
||||
width: double.infinity,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
callback(location);
|
||||
},
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${location.flag ?? ''} ${location.title}',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (location.description != null)
|
||||
Text(
|
||||
location.description!,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
return ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.choose_location_type'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.choose_location_type_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...(snapshot.data! as List<ServerProviderLocation>).map(
|
||||
(final location) => Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Card(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkResponse(
|
||||
highlightShape: BoxShape.rectangle,
|
||||
onTap: () {
|
||||
callback(location);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${location.flag ?? ''} ${location.title}',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (location.description != null)
|
||||
Text(
|
||||
location.description!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
|
@ -180,121 +198,145 @@ class SelectTypePage extends StatelessWidget {
|
|||
],
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.choose_server_type'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.choose_server_type_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...(snapshot.data! as List<ServerType>).map(
|
||||
(final type) => SizedBox(
|
||||
width: double.infinity,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
serverInstallationCubit.setServerType(type);
|
||||
},
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
type.title,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.memory_outlined,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'server.core_count'.plural(type.cores),
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.memory_outlined,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'initializing.choose_server_type_ram'
|
||||
.tr(args: [type.ram.toString()]),
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.sd_card_outlined,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'initializing.choose_server_type_storage'
|
||||
.tr(
|
||||
args: [type.disk.gibibyte.toString()],
|
||||
return ResponsiveLayoutWithInfobox(
|
||||
topChild: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'initializing.choose_server_type'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'initializing.choose_server_type_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
primaryColumn: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...(snapshot.data! as List<ServerType>).map(
|
||||
(final type) => Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
serverInstallationCubit.setServerType(type);
|
||||
},
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
type.title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium,
|
||||
),
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 8),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.payments_outlined,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'initializing.choose_server_type_payment_per_month'
|
||||
.tr(
|
||||
args: [
|
||||
'${type.price.value.toString()} ${type.price.currency}'
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.memory_outlined,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'server.core_count'
|
||||
.plural(type.cores),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.memory_outlined,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'initializing.choose_server_type_ram'
|
||||
.tr(args: [type.ram.toString()]),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.sd_card_outlined,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'initializing.choose_server_type_storage'
|
||||
.tr(
|
||||
args: [
|
||||
type.disk.gibibyte.toString()
|
||||
],
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 8),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.payments_outlined,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'initializing.choose_server_type_payment_per_month'
|
||||
.tr(
|
||||
args: [
|
||||
'${type.price.value.toString()} ${type.price.currency}'
|
||||
],
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
InfoBox(text: 'initializing.choose_server_type_notice'.tr()),
|
||||
],
|
||||
],
|
||||
),
|
||||
secondaryColumn:
|
||||
InfoBox(text: 'initializing.choose_server_type_notice'.tr()),
|
||||
);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart
|
|||
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
|
||||
class RecoverByRecoveryKey extends StatelessWidget {
|
||||
const RecoverByRecoveryKey({super.key});
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_
|
|||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||
|
||||
class RecoveryConfirmBackblaze extends StatelessWidget {
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_fo
|
|||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||
|
||||
class RecoveryConfirmCloudflare extends StatelessWidget {
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_depe
|
|||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
|
||||
class RecoveryConfirmServer extends StatefulWidget {
|
||||
const RecoveryConfirmServer({super.key});
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart';
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
|
|||
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
||||
import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart';
|
||||
import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_recovery_key.dart';
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:selfprivacy/config/brand_theme.dart';
|
|||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
|
||||
import 'package:cubit_form/cubit_form.dart';
|
||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||
|
|
|
@ -12,7 +12,7 @@ class _User extends StatelessWidget {
|
|||
Widget build(final BuildContext context) => InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
materialRoute(UserDetails(login: user.login)),
|
||||
materialRoute(UserDetailsPage(login: user.login)),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
part of 'users.dart';
|
||||
|
||||
class UserDetails extends StatelessWidget {
|
||||
const UserDetails({
|
||||
class UserDetailsPage extends StatelessWidget {
|
||||
const UserDetailsPage({
|
||||
required this.login,
|
||||
super.key,
|
||||
});
|
||||
|
|
|
@ -16,12 +16,13 @@ import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
|||
import 'package:selfprivacy/ui/components/brand_button/outlined_button.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||
import 'package:selfprivacy/ui/layouts/brand_hero_screen.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/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/utils/breakpoints.dart';
|
||||
import 'package:selfprivacy/utils/ui_helpers.dart';
|
||||
|
||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||
|
@ -106,12 +107,14 @@ class UsersPage extends StatelessWidget {
|
|||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.users'.tr(),
|
||||
),
|
||||
),
|
||||
appBar: Breakpoints.small.isActive(context)
|
||||
? PreferredSize(
|
||||
preferredSize: const Size.fromHeight(52),
|
||||
child: BrandHeader(
|
||||
title: 'basis.users'.tr(),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
body: child,
|
||||
);
|
||||
}
|
||||
|
|
46
lib/ui/router/root_destinations.dart
Normal file
46
lib/ui/router/root_destinations.dart
Normal file
|
@ -0,0 +1,46 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
|
||||
class RouteDestination {
|
||||
const RouteDestination({
|
||||
required this.route,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
final PageRouteInfo route;
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final String title;
|
||||
}
|
||||
|
||||
final rootDestinations = [
|
||||
RouteDestination(
|
||||
route: const ProvidersRoute(),
|
||||
icon: BrandIcons.server,
|
||||
label: 'basis.providers'.tr(),
|
||||
title: 'basis.providers_title'.tr(),
|
||||
),
|
||||
RouteDestination(
|
||||
route: const ServicesRoute(),
|
||||
icon: BrandIcons.box,
|
||||
label: 'basis.services'.tr(),
|
||||
title: 'basis.services'.tr(),
|
||||
),
|
||||
RouteDestination(
|
||||
route: const UsersRoute(),
|
||||
icon: BrandIcons.users,
|
||||
label: 'basis.users'.tr(),
|
||||
title: 'basis.users'.tr(),
|
||||
),
|
||||
RouteDestination(
|
||||
route: const MoreRoute(),
|
||||
icon: Icons.menu_rounded,
|
||||
label: 'basis.more'.tr(),
|
||||
title: 'basis.more'.tr(),
|
||||
),
|
||||
];
|
122
lib/ui/router/router.dart
Normal file
122
lib/ui/router/router.dart
Normal file
|
@ -0,0 +1,122 @@
|
|||
import 'package:animations/animations.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/models/disk_status.dart';
|
||||
import 'package:selfprivacy/logic/models/service.dart';
|
||||
import 'package:selfprivacy/ui/pages/devices/devices.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/developer_settings.dart';
|
||||
import 'package:selfprivacy/ui/pages/more/console.dart';
|
||||
import 'package:selfprivacy/ui/pages/more/more.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||
import 'package:selfprivacy/ui/pages/providers/providers.dart';
|
||||
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart';
|
||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart';
|
||||
import 'package:selfprivacy/ui/pages/services/services.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/users/users.dart';
|
||||
|
||||
part 'router.gr.dart';
|
||||
|
||||
Widget fadeThroughTransition(
|
||||
final BuildContext context,
|
||||
final Animation<double> animation,
|
||||
final Animation<double> secondaryAnimation,
|
||||
final Widget child,
|
||||
) =>
|
||||
SharedAxisTransition(
|
||||
key: UniqueKey(),
|
||||
animation: animation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
transitionType: SharedAxisTransitionType.vertical,
|
||||
child: child,
|
||||
);
|
||||
|
||||
@MaterialAutoRouter(
|
||||
// transitionsBuilder: fadeThroughTransition,
|
||||
replaceInRouteName: 'Page|Screen|Routing,Route',
|
||||
routes: <AutoRoute>[
|
||||
AutoRoute(
|
||||
page: OnboardingPage,
|
||||
),
|
||||
AutoRoute(page: InitializingPage),
|
||||
AutoRoute(page: RecoveryRouting),
|
||||
AutoRoute(
|
||||
page: RootPage,
|
||||
initial: true,
|
||||
children: [
|
||||
CustomRoute(
|
||||
page: ProvidersPage,
|
||||
usesPathAsKey: true,
|
||||
initial: true,
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
),
|
||||
CustomRoute(
|
||||
page: ServicesPage,
|
||||
usesPathAsKey: true,
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
),
|
||||
CustomRoute(
|
||||
page: UsersPage,
|
||||
usesPathAsKey: true,
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
),
|
||||
CustomRoute(
|
||||
page: MorePage,
|
||||
usesPathAsKey: true,
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
),
|
||||
AutoRoute(page: AppSettingsPage),
|
||||
AutoRoute(page: UserDetailsPage),
|
||||
AutoRoute(page: RecoveryKeyPage),
|
||||
AutoRoute(page: DevicesScreen),
|
||||
AutoRoute(page: AboutApplicationPage),
|
||||
AutoRoute(page: DeveloperSettingsPage),
|
||||
],
|
||||
),
|
||||
AutoRoute(page: ServicesMigrationPage),
|
||||
AutoRoute(page: ConsolePage),
|
||||
],
|
||||
)
|
||||
class RootRouter extends _$RootRouter {
|
||||
RootRouter();
|
||||
}
|
||||
|
||||
// Function to map route names to route titles
|
||||
String getRouteTitle(final String routeName) {
|
||||
switch (routeName) {
|
||||
case 'RootRoute':
|
||||
return 'basis.app_name';
|
||||
case 'ProvidersRoute':
|
||||
return 'basis.providers_title';
|
||||
case 'ServicesRoute':
|
||||
return 'basis.services';
|
||||
case 'UsersRoute':
|
||||
return 'basis.users';
|
||||
case 'MoreRoute':
|
||||
return 'basis.more';
|
||||
case 'AppSettingsRoute':
|
||||
return 'application_settings.title';
|
||||
case 'UserDetailsRoute':
|
||||
return '[User Details]';
|
||||
case 'RecoveryKeyRoute':
|
||||
return 'recovery_key.key_main_header';
|
||||
case 'DevicesRoute':
|
||||
return 'devices.main_screen.header';
|
||||
case 'AboutApplicationRoute':
|
||||
return 'about_us_page.title';
|
||||
case 'ConsoleRoute':
|
||||
return '[Console]';
|
||||
case 'DeveloperSettingsRoute':
|
||||
return 'developer_settings.title';
|
||||
default:
|
||||
return routeName;
|
||||
}
|
||||
}
|
474
lib/ui/router/router.gr.dart
Normal file
474
lib/ui/router/router.gr.dart
Normal file
|
@ -0,0 +1,474 @@
|
|||
// **************************************************************************
|
||||
// AutoRouteGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
// **************************************************************************
|
||||
// AutoRouteGenerator
|
||||
// **************************************************************************
|
||||
//
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
part of 'router.dart';
|
||||
|
||||
class _$RootRouter extends RootStackRouter {
|
||||
_$RootRouter([GlobalKey<NavigatorState>? navigatorKey]) : super(navigatorKey);
|
||||
|
||||
@override
|
||||
final Map<String, PageFactory> pagesMap = {
|
||||
OnboardingRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const OnboardingPage(),
|
||||
);
|
||||
},
|
||||
InitializingRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const InitializingPage(),
|
||||
);
|
||||
},
|
||||
RecoveryRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const RecoveryRouting(),
|
||||
);
|
||||
},
|
||||
RootRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: WrappedRoute(child: const RootPage()),
|
||||
);
|
||||
},
|
||||
ServicesMigrationRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<ServicesMigrationRouteArgs>();
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: ServicesMigrationPage(
|
||||
services: args.services,
|
||||
diskStatus: args.diskStatus,
|
||||
isMigration: args.isMigration,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
ConsoleRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ConsolePage(),
|
||||
);
|
||||
},
|
||||
ProvidersRoute.name: (routeData) {
|
||||
return CustomPage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ProvidersPage(),
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
opaque: true,
|
||||
barrierDismissible: false,
|
||||
);
|
||||
},
|
||||
ServicesRoute.name: (routeData) {
|
||||
return CustomPage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const ServicesPage(),
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
opaque: true,
|
||||
barrierDismissible: false,
|
||||
);
|
||||
},
|
||||
UsersRoute.name: (routeData) {
|
||||
return CustomPage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const UsersPage(),
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
opaque: true,
|
||||
barrierDismissible: false,
|
||||
);
|
||||
},
|
||||
MoreRoute.name: (routeData) {
|
||||
return CustomPage<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const MorePage(),
|
||||
transitionsBuilder: fadeThroughTransition,
|
||||
durationInMilliseconds: 200,
|
||||
opaque: true,
|
||||
barrierDismissible: false,
|
||||
);
|
||||
},
|
||||
AppSettingsRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const AppSettingsPage(),
|
||||
);
|
||||
},
|
||||
UserDetailsRoute.name: (routeData) {
|
||||
final args = routeData.argsAs<UserDetailsRouteArgs>();
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: UserDetailsPage(
|
||||
login: args.login,
|
||||
key: args.key,
|
||||
),
|
||||
);
|
||||
},
|
||||
RecoveryKeyRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const RecoveryKeyPage(),
|
||||
);
|
||||
},
|
||||
DevicesRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const DevicesScreen(),
|
||||
);
|
||||
},
|
||||
AboutApplicationRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const AboutApplicationPage(),
|
||||
);
|
||||
},
|
||||
DeveloperSettingsRoute.name: (routeData) {
|
||||
return MaterialPageX<dynamic>(
|
||||
routeData: routeData,
|
||||
child: const DeveloperSettingsPage(),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@override
|
||||
List<RouteConfig> get routes => [
|
||||
RouteConfig(
|
||||
OnboardingRoute.name,
|
||||
path: '/onboarding-page',
|
||||
),
|
||||
RouteConfig(
|
||||
InitializingRoute.name,
|
||||
path: '/initializing-page',
|
||||
),
|
||||
RouteConfig(
|
||||
RecoveryRoute.name,
|
||||
path: '/recovery-routing',
|
||||
),
|
||||
RouteConfig(
|
||||
RootRoute.name,
|
||||
path: '/',
|
||||
children: [
|
||||
RouteConfig(
|
||||
ProvidersRoute.name,
|
||||
path: '',
|
||||
parent: RootRoute.name,
|
||||
usesPathAsKey: true,
|
||||
),
|
||||
RouteConfig(
|
||||
ServicesRoute.name,
|
||||
path: 'services-page',
|
||||
parent: RootRoute.name,
|
||||
usesPathAsKey: true,
|
||||
),
|
||||
RouteConfig(
|
||||
UsersRoute.name,
|
||||
path: 'users-page',
|
||||
parent: RootRoute.name,
|
||||
usesPathAsKey: true,
|
||||
),
|
||||
RouteConfig(
|
||||
MoreRoute.name,
|
||||
path: 'more-page',
|
||||
parent: RootRoute.name,
|
||||
usesPathAsKey: true,
|
||||
),
|
||||
RouteConfig(
|
||||
AppSettingsRoute.name,
|
||||
path: 'app-settings-page',
|
||||
parent: RootRoute.name,
|
||||
),
|
||||
RouteConfig(
|
||||
UserDetailsRoute.name,
|
||||
path: 'user-details-page',
|
||||
parent: RootRoute.name,
|
||||
),
|
||||
RouteConfig(
|
||||
RecoveryKeyRoute.name,
|
||||
path: 'recovery-key-page',
|
||||
parent: RootRoute.name,
|
||||
),
|
||||
RouteConfig(
|
||||
DevicesRoute.name,
|
||||
path: 'devices-screen',
|
||||
parent: RootRoute.name,
|
||||
),
|
||||
RouteConfig(
|
||||
AboutApplicationRoute.name,
|
||||
path: 'about-application-page',
|
||||
parent: RootRoute.name,
|
||||
),
|
||||
RouteConfig(
|
||||
DeveloperSettingsRoute.name,
|
||||
path: 'developer-settings-page',
|
||||
parent: RootRoute.name,
|
||||
),
|
||||
],
|
||||
),
|
||||
RouteConfig(
|
||||
ServicesMigrationRoute.name,
|
||||
path: '/services-migration-page',
|
||||
),
|
||||
RouteConfig(
|
||||
ConsoleRoute.name,
|
||||
path: '/console-page',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [OnboardingPage]
|
||||
class OnboardingRoute extends PageRouteInfo<void> {
|
||||
const OnboardingRoute()
|
||||
: super(
|
||||
OnboardingRoute.name,
|
||||
path: '/onboarding-page',
|
||||
);
|
||||
|
||||
static const String name = 'OnboardingRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [InitializingPage]
|
||||
class InitializingRoute extends PageRouteInfo<void> {
|
||||
const InitializingRoute()
|
||||
: super(
|
||||
InitializingRoute.name,
|
||||
path: '/initializing-page',
|
||||
);
|
||||
|
||||
static const String name = 'InitializingRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [RecoveryRouting]
|
||||
class RecoveryRoute extends PageRouteInfo<void> {
|
||||
const RecoveryRoute()
|
||||
: super(
|
||||
RecoveryRoute.name,
|
||||
path: '/recovery-routing',
|
||||
);
|
||||
|
||||
static const String name = 'RecoveryRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [RootPage]
|
||||
class RootRoute extends PageRouteInfo<void> {
|
||||
const RootRoute({List<PageRouteInfo>? children})
|
||||
: super(
|
||||
RootRoute.name,
|
||||
path: '/',
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'RootRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServicesMigrationPage]
|
||||
class ServicesMigrationRoute extends PageRouteInfo<ServicesMigrationRouteArgs> {
|
||||
ServicesMigrationRoute({
|
||||
required List<Service> services,
|
||||
required DiskStatus diskStatus,
|
||||
required bool isMigration,
|
||||
Key? key,
|
||||
}) : super(
|
||||
ServicesMigrationRoute.name,
|
||||
path: '/services-migration-page',
|
||||
args: ServicesMigrationRouteArgs(
|
||||
services: services,
|
||||
diskStatus: diskStatus,
|
||||
isMigration: isMigration,
|
||||
key: key,
|
||||
),
|
||||
);
|
||||
|
||||
static const String name = 'ServicesMigrationRoute';
|
||||
}
|
||||
|
||||
class ServicesMigrationRouteArgs {
|
||||
const ServicesMigrationRouteArgs({
|
||||
required this.services,
|
||||
required this.diskStatus,
|
||||
required this.isMigration,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final List<Service> services;
|
||||
|
||||
final DiskStatus diskStatus;
|
||||
|
||||
final bool isMigration;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ServicesMigrationRouteArgs{services: $services, diskStatus: $diskStatus, isMigration: $isMigration, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ConsolePage]
|
||||
class ConsoleRoute extends PageRouteInfo<void> {
|
||||
const ConsoleRoute()
|
||||
: super(
|
||||
ConsoleRoute.name,
|
||||
path: '/console-page',
|
||||
);
|
||||
|
||||
static const String name = 'ConsoleRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ProvidersPage]
|
||||
class ProvidersRoute extends PageRouteInfo<void> {
|
||||
const ProvidersRoute()
|
||||
: super(
|
||||
ProvidersRoute.name,
|
||||
path: '',
|
||||
);
|
||||
|
||||
static const String name = 'ProvidersRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [ServicesPage]
|
||||
class ServicesRoute extends PageRouteInfo<void> {
|
||||
const ServicesRoute()
|
||||
: super(
|
||||
ServicesRoute.name,
|
||||
path: 'services-page',
|
||||
);
|
||||
|
||||
static const String name = 'ServicesRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [UsersPage]
|
||||
class UsersRoute extends PageRouteInfo<void> {
|
||||
const UsersRoute()
|
||||
: super(
|
||||
UsersRoute.name,
|
||||
path: 'users-page',
|
||||
);
|
||||
|
||||
static const String name = 'UsersRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [MorePage]
|
||||
class MoreRoute extends PageRouteInfo<void> {
|
||||
const MoreRoute()
|
||||
: super(
|
||||
MoreRoute.name,
|
||||
path: 'more-page',
|
||||
);
|
||||
|
||||
static const String name = 'MoreRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [AppSettingsPage]
|
||||
class AppSettingsRoute extends PageRouteInfo<void> {
|
||||
const AppSettingsRoute()
|
||||
: super(
|
||||
AppSettingsRoute.name,
|
||||
path: 'app-settings-page',
|
||||
);
|
||||
|
||||
static const String name = 'AppSettingsRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [UserDetailsPage]
|
||||
class UserDetailsRoute extends PageRouteInfo<UserDetailsRouteArgs> {
|
||||
UserDetailsRoute({
|
||||
required String login,
|
||||
Key? key,
|
||||
}) : super(
|
||||
UserDetailsRoute.name,
|
||||
path: 'user-details-page',
|
||||
args: UserDetailsRouteArgs(
|
||||
login: login,
|
||||
key: key,
|
||||
),
|
||||
);
|
||||
|
||||
static const String name = 'UserDetailsRoute';
|
||||
}
|
||||
|
||||
class UserDetailsRouteArgs {
|
||||
const UserDetailsRouteArgs({
|
||||
required this.login,
|
||||
this.key,
|
||||
});
|
||||
|
||||
final String login;
|
||||
|
||||
final Key? key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UserDetailsRouteArgs{login: $login, key: $key}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [RecoveryKeyPage]
|
||||
class RecoveryKeyRoute extends PageRouteInfo<void> {
|
||||
const RecoveryKeyRoute()
|
||||
: super(
|
||||
RecoveryKeyRoute.name,
|
||||
path: 'recovery-key-page',
|
||||
);
|
||||
|
||||
static const String name = 'RecoveryKeyRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [DevicesScreen]
|
||||
class DevicesRoute extends PageRouteInfo<void> {
|
||||
const DevicesRoute()
|
||||
: super(
|
||||
DevicesRoute.name,
|
||||
path: 'devices-screen',
|
||||
);
|
||||
|
||||
static const String name = 'DevicesRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [AboutApplicationPage]
|
||||
class AboutApplicationRoute extends PageRouteInfo<void> {
|
||||
const AboutApplicationRoute()
|
||||
: super(
|
||||
AboutApplicationRoute.name,
|
||||
path: 'about-application-page',
|
||||
);
|
||||
|
||||
static const String name = 'AboutApplicationRoute';
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [DeveloperSettingsPage]
|
||||
class DeveloperSettingsRoute extends PageRouteInfo<void> {
|
||||
const DeveloperSettingsRoute()
|
||||
: super(
|
||||
DeveloperSettingsRoute.name,
|
||||
path: 'developer-settings-page',
|
||||
);
|
||||
|
||||
static const String name = 'DeveloperSettingsRoute';
|
||||
}
|
Loading…
Reference in a new issue