mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-08 17:11:14 +00:00
Merge branch 'master' into remove_snackbar_style_notif
This commit is contained in:
commit
087deede3a
7
.github/workflows/windows.yml
vendored
7
.github/workflows/windows.yml
vendored
|
@ -1,6 +1,9 @@
|
|||
name: Windows Builder
|
||||
|
||||
on: tag
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*.*.*'
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
|
@ -14,7 +17,7 @@ jobs:
|
|||
# Install Flutter
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.3.10'
|
||||
flutter-version: '3.16.1'
|
||||
channel: 'stable'
|
||||
|
||||
# Build Windows artifact
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import 'package:graphql/client.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
final DateFormat formatter = DateFormat('hh:mm');
|
||||
|
||||
/// TODO(misterfourtytwo): add equality override
|
||||
class Message {
|
||||
Message({this.text, this.severity = MessageSeverity.normal})
|
||||
: time = DateTime.now();
|
||||
|
@ -13,7 +12,9 @@ class Message {
|
|||
final String? text;
|
||||
final DateTime time;
|
||||
final MessageSeverity severity;
|
||||
String get timeString => formatter.format(time);
|
||||
|
||||
static final DateFormat _formatter = DateFormat('hh:mm');
|
||||
String get timeString => _formatter.format(time);
|
||||
}
|
||||
|
||||
enum MessageSeverity {
|
||||
|
|
|
@ -92,13 +92,15 @@ class SelfprivacyApp extends StatelessWidget {
|
|||
? ThemeMode.dark
|
||||
: ThemeMode.light,
|
||||
builder: (final BuildContext context, final Widget? widget) {
|
||||
Widget error = const Text('...rendering error...');
|
||||
Widget error =
|
||||
const Center(child: Text('...rendering error...'));
|
||||
if (widget is Scaffold || widget is Navigator) {
|
||||
error = Scaffold(body: Center(child: error));
|
||||
error = Scaffold(body: error);
|
||||
}
|
||||
ErrorWidget.builder =
|
||||
(final FlutterErrorDetails errorDetails) => error;
|
||||
return widget!;
|
||||
|
||||
return widget ?? error;
|
||||
},
|
||||
);
|
||||
},
|
||||
|
|
|
@ -14,6 +14,7 @@ class BrandHeader extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(final BuildContext context) => AppBar(
|
||||
centerTitle: true,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: Text(title),
|
||||
|
@ -25,8 +26,5 @@ class BrandHeader extends StatelessWidget {
|
|||
onBackButtonPressed ?? () => Navigator.of(context).pop(),
|
||||
)
|
||||
: null,
|
||||
actions: const [
|
||||
SizedBox.shrink(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@ class BrandButton {
|
|||
final String? text,
|
||||
final Widget? child,
|
||||
}) {
|
||||
assert(text == null || child == null, 'required title or child');
|
||||
assert(text != null || child != null, 'required title or child');
|
||||
assert((text ?? child) != null, 'either title or child must not be empty');
|
||||
assert(text != null || child != null, 'title or child must be provided');
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 48,
|
||||
|
@ -28,8 +29,9 @@ class BrandButton {
|
|||
final String? text,
|
||||
final Widget? child,
|
||||
}) {
|
||||
assert(text == null || child == null, 'required title or child');
|
||||
assert(text != null || child != null, 'required title or child');
|
||||
assert((text ?? child) != null, 'either title or child must not be empty');
|
||||
assert(text != null || child != null, 'title or child must be provided');
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: double.infinity,
|
||||
|
|
2
lib/ui/components/buttons/buttons.dart
Normal file
2
lib/ui/components/buttons/buttons.dart
Normal file
|
@ -0,0 +1,2 @@
|
|||
export 'brand_button.dart';
|
||||
export 'sp_brand_button.dart';
|
28
lib/ui/components/buttons/sp_brand_button.dart
Normal file
28
lib/ui/components/buttons/sp_brand_button.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class SPBrandButton extends StatelessWidget {
|
||||
const SPBrandButton({
|
||||
required this.child,
|
||||
required this.onPressed,
|
||||
super.key,
|
||||
});
|
||||
|
||||
SPBrandButton.text({
|
||||
required final String title,
|
||||
required this.onPressed,
|
||||
super.key,
|
||||
}) : child = Text(title);
|
||||
|
||||
final Widget child;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => FilledButton(
|
||||
// TODO(misterfourtytwo): move button styles to theme configuration
|
||||
style: const ButtonStyle(
|
||||
minimumSize: MaterialStatePropertyAll(Size.fromHeight(48)),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: child,
|
||||
);
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
||||
|
||||
class ProgressBar extends StatefulWidget {
|
||||
const ProgressBar({
|
||||
|
@ -21,41 +20,6 @@ class _ProgressBarState extends State<ProgressBar> {
|
|||
Widget build(final BuildContext context) {
|
||||
final double progress =
|
||||
1 / widget.steps.length * (widget.activeIndex + 0.3);
|
||||
final bool isDark = context.watch<AppSettingsCubit>().state.isDarkModeOn;
|
||||
final TextStyle style =
|
||||
isDark ? progressTextStyleDark : progressTextStyleLight;
|
||||
|
||||
final Iterable<Container> allSteps = widget.steps.asMap().map(
|
||||
(final i, final step) {
|
||||
final Container value = _stepTitle(index: i, style: style, step: step);
|
||||
return MapEntry(i, value);
|
||||
},
|
||||
).values;
|
||||
|
||||
final List<Widget> odd = [];
|
||||
final List<Widget> even = [];
|
||||
|
||||
int i = 0;
|
||||
for (final Container step in allSteps) {
|
||||
if (i.isEven) {
|
||||
even.add(step);
|
||||
} else {
|
||||
odd.add(step);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
odd.insert(
|
||||
0,
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
);
|
||||
odd.add(
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
@ -91,39 +55,4 @@ class _ProgressBarState extends State<ProgressBar> {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
Container _stepTitle({
|
||||
required final int index,
|
||||
TextStyle? style,
|
||||
final String? step,
|
||||
}) {
|
||||
final bool isActive = index == widget.activeIndex;
|
||||
|
||||
style = isActive ? style!.copyWith(fontWeight: FontWeight.w700) : style;
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
height: 20,
|
||||
alignment: Alignment.center,
|
||||
child: RichText(
|
||||
textAlign: TextAlign.justify,
|
||||
text: TextSpan(
|
||||
style: progressTextStyleLight,
|
||||
children: [
|
||||
TextSpan(text: '${index + 1}.', style: style),
|
||||
TextSpan(text: step, style: style),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const TextStyle progressTextStyleLight = TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.black,
|
||||
height: 1.7,
|
||||
);
|
||||
|
||||
final TextStyle progressTextStyleDark = progressTextStyleLight.copyWith(
|
||||
color: Colors.white,
|
||||
);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import 'dart:collection';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:selfprivacy/config/get_it_config.dart';
|
||||
import 'package:selfprivacy/logic/models/message.dart';
|
||||
import 'package:selfprivacy/ui/components/list_tiles/log_list_tile.dart';
|
||||
|
@ -16,25 +15,36 @@ class ConsolePage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ConsolePageState extends State<ConsolePage> {
|
||||
bool paused = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
getIt.get<ConsoleModel>().addListener(update);
|
||||
|
||||
super.initState();
|
||||
|
||||
getIt<ConsoleModel>().addListener(update);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
getIt<ConsoleModel>().removeListener(update);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool paused = false;
|
||||
|
||||
void update() {
|
||||
if (!paused) {
|
||||
setState(() => {});
|
||||
}
|
||||
/// listener update could come at any time, like when widget is already
|
||||
/// unmounted or during frame build, adding as postframe callback ensures
|
||||
/// that element is marked for rebuild
|
||||
WidgetsBinding.instance.addPostFrameCallback((final _) {
|
||||
if (!paused && mounted) {
|
||||
setState(() => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void togglePause() {
|
||||
paused ^= true;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -51,7 +61,7 @@ class _ConsolePageState extends State<ConsolePage> {
|
|||
icon: Icon(
|
||||
paused ? Icons.play_arrow_outlined : Icons.pause_outlined,
|
||||
),
|
||||
onPressed: () => setState(() => paused = !paused),
|
||||
onPressed: togglePause,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -69,12 +79,12 @@ class _ConsolePageState extends State<ConsolePage> {
|
|||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
...UnmodifiableListView(
|
||||
messages
|
||||
.map((final message) => LogListItem(message: message))
|
||||
.toList()
|
||||
.reversed,
|
||||
const Gap(20),
|
||||
...messages.reversed.map(
|
||||
(final message) => LogListItem(
|
||||
key: ValueKey(message),
|
||||
message: message,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -82,11 +92,10 @@ class _ConsolePageState extends State<ConsolePage> {
|
|||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text('console_page.waiting'.tr()),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
const Gap(16),
|
||||
const CircularProgressIndicator(),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
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/ui/components/buttons/brand_button.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/views/views.dart';
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
|
||||
@RoutePage()
|
||||
|
@ -17,152 +16,35 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
|||
PageController pageController = PageController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
void dispose() {
|
||||
pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> scrollTo(final int targetView) => pageController.animateToPage(
|
||||
targetView,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOutCubicEmphasized,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Scaffold(
|
||||
body: PageView(
|
||||
Widget build(final BuildContext context) => Material(
|
||||
child: PageView(
|
||||
controller: pageController,
|
||||
children: [
|
||||
_withPadding(firstPage()),
|
||||
_withPadding(secondPage()),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _withPadding(final Widget child) => Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
|
||||
Widget firstPage() => ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
'onboarding.page1_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page1_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Center(
|
||||
child: Image.asset(
|
||||
_fileName(
|
||||
context: context,
|
||||
path: 'assets/images/onboarding',
|
||||
fileExtention: 'png',
|
||||
fileName: 'onboarding1',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
OnboardingFirstView(
|
||||
onProceed: () => scrollTo(1),
|
||||
),
|
||||
BrandButton.rised(
|
||||
onPressed: () {
|
||||
pageController.animateToPage(
|
||||
1,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOutCubicEmphasized,
|
||||
);
|
||||
},
|
||||
text: 'basis.next'.tr(),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget secondPage() => ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
'onboarding.page2_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page2_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page2_server_provider_title'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page2_server_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page2_dns_provider_title'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page2_dns_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page2_backup_provider_title'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'onboarding.page2_backup_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
BrandButton.rised(
|
||||
onPressed: () {
|
||||
OnboardingSecondView(
|
||||
onProceed: () {
|
||||
context.read<AppSettingsCubit>().turnOffOnboarding();
|
||||
context.router.replaceAll([
|
||||
const RootRoute(),
|
||||
const InitializingRoute(),
|
||||
]);
|
||||
},
|
||||
text: 'basis.got_it'.tr(),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _fileName({
|
||||
required final BuildContext context,
|
||||
required final String path,
|
||||
required final String fileName,
|
||||
required final String fileExtention,
|
||||
}) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final bool isDark = theme.brightness == Brightness.dark;
|
||||
return '$path/$fileName${isDark ? '-dark' : '-light'}.$fileExtention';
|
||||
}
|
||||
|
|
50
lib/ui/pages/onboarding/views/onboarding_first_view.dart
Normal file
50
lib/ui/pages/onboarding/views/onboarding_first_view.dart
Normal file
|
@ -0,0 +1,50 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/views/onboarding_view.dart';
|
||||
|
||||
class OnboardingFirstView extends StatelessWidget {
|
||||
const OnboardingFirstView({
|
||||
required this.onProceed,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final VoidCallback onProceed;
|
||||
|
||||
String assetName({
|
||||
required final BuildContext context,
|
||||
required final String path,
|
||||
required final String fileName,
|
||||
required final String fileExtension,
|
||||
}) {
|
||||
final String suffix =
|
||||
Theme.of(context).brightness == Brightness.dark ? '-dark' : '-light';
|
||||
return '$path/$fileName$suffix.$fileExtension';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => OnboardingView(
|
||||
onProceed: onProceed,
|
||||
children: [
|
||||
Text(
|
||||
'onboarding.page1_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const Gap(15),
|
||||
Text(
|
||||
'onboarding.page1_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Gap(30),
|
||||
Image.asset(
|
||||
assetName(
|
||||
context: context,
|
||||
path: 'assets/images/onboarding',
|
||||
fileName: 'onboarding1',
|
||||
fileExtension: 'png',
|
||||
),
|
||||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
60
lib/ui/pages/onboarding/views/onboarding_second_view.dart
Normal file
60
lib/ui/pages/onboarding/views/onboarding_second_view.dart
Normal file
|
@ -0,0 +1,60 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:selfprivacy/ui/pages/onboarding/views/onboarding_view.dart';
|
||||
|
||||
class OnboardingSecondView extends StatelessWidget {
|
||||
const OnboardingSecondView({
|
||||
required this.onProceed,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final VoidCallback onProceed;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => OnboardingView(
|
||||
buttonTitle: 'basis.got_it',
|
||||
onProceed: onProceed,
|
||||
children: [
|
||||
Text(
|
||||
'onboarding.page2_title'.tr(),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'onboarding.page2_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'onboarding.page2_server_provider_title'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'onboarding.page2_server_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'onboarding.page2_dns_provider_title'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'onboarding.page2_dns_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'onboarding.page2_backup_provider_title'.tr(),
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'onboarding.page2_backup_provider_text'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
53
lib/ui/pages/onboarding/views/onboarding_view.dart
Normal file
53
lib/ui/pages/onboarding/views/onboarding_view.dart
Normal file
|
@ -0,0 +1,53 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/ui/components/buttons/buttons.dart';
|
||||
|
||||
// base widget for onboarding view
|
||||
class OnboardingView extends StatelessWidget {
|
||||
const OnboardingView({
|
||||
required this.onProceed,
|
||||
required this.children,
|
||||
this.buttonTitle = 'basis.next',
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// Proceed button title
|
||||
final String buttonTitle;
|
||||
|
||||
/// Proceed button callback
|
||||
final VoidCallback onProceed;
|
||||
|
||||
/// Current view content
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Scaffold(
|
||||
body: Align(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 480),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
primary: true,
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(15) +
|
||||
const EdgeInsets.only(top: 15),
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15) +
|
||||
const EdgeInsets.only(bottom: 30),
|
||||
child: SPBrandButton.text(
|
||||
title: buttonTitle.tr(),
|
||||
onPressed: onProceed,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
2
lib/ui/pages/onboarding/views/views.dart
Normal file
2
lib/ui/pages/onboarding/views/views.dart
Normal file
|
@ -0,0 +1,2 @@
|
|||
export 'onboarding_first_view.dart';
|
||||
export 'onboarding_second_view.dart';
|
|
@ -5,7 +5,6 @@ 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/layouts/root_scaffold_with_navigation.dart';
|
||||
import 'package:selfprivacy/ui/router/root_destinations.dart';
|
||||
|
||||
import 'package:selfprivacy/ui/router/router.dart';
|
||||
|
||||
@RoutePage()
|
||||
|
@ -150,8 +149,3 @@ class MainScreenNavigationDrawer extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChangeTab {
|
||||
ChangeTab(this.onPress);
|
||||
final ValueChanged<int> onPress;
|
||||
}
|
||||
|
|
|
@ -56,5 +56,3 @@ class _TempMessage extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
final DateFormat formatter = DateFormat('HH:mm:ss');
|
||||
|
|
|
@ -170,83 +170,54 @@ class ServiceStatusCard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
late IconData icon;
|
||||
late String buttonTitle;
|
||||
|
||||
switch (status) {
|
||||
case ServiceStatus.active:
|
||||
return FilledCard(
|
||||
child: ListTile(
|
||||
leading: const Icon(
|
||||
Icons.check_circle_outline,
|
||||
size: 24,
|
||||
),
|
||||
title: Text('service_page.status.active'.tr()),
|
||||
),
|
||||
);
|
||||
icon = Icons.check_circle_outline;
|
||||
buttonTitle = 'service_page.status.active';
|
||||
break;
|
||||
|
||||
case ServiceStatus.inactive:
|
||||
return FilledCard(
|
||||
tertiary: true,
|
||||
child: ListTile(
|
||||
leading: const Icon(
|
||||
Icons.stop_circle_outlined,
|
||||
size: 24,
|
||||
),
|
||||
title: Text('service_page.status.inactive'.tr()),
|
||||
),
|
||||
);
|
||||
icon = Icons.stop_circle_outlined;
|
||||
buttonTitle = 'service_page.status.inactive';
|
||||
break;
|
||||
|
||||
case ServiceStatus.failed:
|
||||
return FilledCard(
|
||||
error: true,
|
||||
child: ListTile(
|
||||
leading: const Icon(
|
||||
Icons.error_outline,
|
||||
size: 24,
|
||||
),
|
||||
title: Text('service_page.status.failed'.tr()),
|
||||
),
|
||||
);
|
||||
icon = Icons.error_outline;
|
||||
buttonTitle = 'service_page.status.failed';
|
||||
break;
|
||||
|
||||
case ServiceStatus.off:
|
||||
return FilledCard(
|
||||
tertiary: true,
|
||||
child: ListTile(
|
||||
leading: const Icon(
|
||||
Icons.power_settings_new,
|
||||
size: 24,
|
||||
),
|
||||
title: Text('service_page.status.off'.tr()),
|
||||
),
|
||||
);
|
||||
icon = Icons.power_settings_new;
|
||||
buttonTitle = 'service_page.status.off';
|
||||
break;
|
||||
|
||||
case ServiceStatus.activating:
|
||||
return FilledCard(
|
||||
tertiary: true,
|
||||
child: ListTile(
|
||||
leading: const Icon(
|
||||
Icons.restart_alt_outlined,
|
||||
size: 24,
|
||||
),
|
||||
title: Text('service_page.status.activating'.tr()),
|
||||
),
|
||||
);
|
||||
icon = Icons.restart_alt_outlined;
|
||||
buttonTitle = 'service_page.status.activating';
|
||||
break;
|
||||
|
||||
case ServiceStatus.deactivating:
|
||||
return FilledCard(
|
||||
tertiary: true,
|
||||
child: ListTile(
|
||||
leading: const Icon(
|
||||
Icons.restart_alt_outlined,
|
||||
size: 24,
|
||||
),
|
||||
title: Text('service_page.status.deactivating'.tr()),
|
||||
),
|
||||
);
|
||||
icon = Icons.restart_alt_outlined;
|
||||
buttonTitle = 'service_page.status.deactivating';
|
||||
break;
|
||||
|
||||
case ServiceStatus.reloading:
|
||||
return FilledCard(
|
||||
tertiary: true,
|
||||
child: ListTile(
|
||||
leading: const Icon(
|
||||
Icons.restart_alt_outlined,
|
||||
size: 24,
|
||||
),
|
||||
title: Text('service_page.status.reloading'.tr()),
|
||||
),
|
||||
);
|
||||
icon = Icons.restart_alt_outlined;
|
||||
buttonTitle = 'service_page.status.reloading';
|
||||
}
|
||||
|
||||
return FilledCard(
|
||||
tertiary: true,
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
icon,
|
||||
size: 24,
|
||||
),
|
||||
title: Text(buttonTitle.tr()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ class UiHelpers {
|
|||
static String getDomainName(final ServerInstallationState config) =>
|
||||
config.isDomainSelected ? config.serverDomain!.domainName : 'example.com';
|
||||
|
||||
static final _formatter = NumberFormat()..minimumFractionDigits = 0;
|
||||
|
||||
static String formatWithPrecision(
|
||||
final double value, {
|
||||
final int fraction = 2,
|
||||
}) {
|
||||
final NumberFormat formatter = NumberFormat();
|
||||
formatter.minimumFractionDigits = 0;
|
||||
formatter.maximumFractionDigits = fraction;
|
||||
return formatter.format(value);
|
||||
_formatter.maximumFractionDigits = fraction;
|
||||
return _formatter.format(value);
|
||||
}
|
||||
}
|
||||
|
|
16
pubspec.lock
16
pubspec.lock
|
@ -525,14 +525,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
gap:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: gap
|
||||
sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
get_it:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: get_it
|
||||
sha256: f79870884de16d689cf9a7d15eedf31ed61d750e813c538a6efb92660fea83c3
|
||||
sha256: e6017ce7fdeaf218dc51a100344d8cb70134b80e28b760f8bb23c242437bafd7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.6.4"
|
||||
version: "7.6.7"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -673,10 +681,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
|
||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.2.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -27,6 +27,7 @@ dependencies:
|
|||
flutter_markdown: ^0.6.18+2
|
||||
flutter_secure_storage: ^9.0.0
|
||||
flutter_svg: ^2.0.9
|
||||
gap: ^3.0.1
|
||||
get_it: ^7.6.4
|
||||
gql: ^1.0.0
|
||||
graphql: ^5.1.3
|
||||
|
|
Loading…
Reference in a new issue