diff --git a/frontend/app/lib/main.dart b/frontend/app/lib/main.dart index a8e16d2..c421f10 100644 --- a/frontend/app/lib/main.dart +++ b/frontend/app/lib/main.dart @@ -1,9 +1,6 @@ import 'package:app/model/services/auth_service.dart'; import 'package:app/model/services/storage_service.dart'; -import 'package:app/pages/notifications_page.dart'; import 'package:app/pages/start_page.dart'; -import 'package:app/pages/verify_email_page.dart'; -import 'package:app/pages/registration_page.dart'; import 'package:app/util/colors.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart' @@ -77,53 +74,39 @@ class _DigitalerFriedenState extends State { void _init() async { accountLevel = await _storageService.accountLevel; - print(accountLevel!); if (accountLevel! > 0) { authenticated = await AuthService.authenticateWithBiometrics(); } - _loading = false; - setState(() {}); + setState(() { + _loading = false; + }); } @override Widget build(BuildContext context) { if (_loading) { - return Center( - child: Column( - children: [ - Image.asset( - 'assets/JPEG.jpg', - height: 180, - ), - CircularProgressIndicator( - color: CustomColors.primary, - ), - ], + return SafeArea( + child: Center( + child: Column( + children: [ + const SizedBox( + height: 150, + ), + Hero( + tag: 'logo', + child: Image.asset( + 'assets/JPEG.jpg', + height: 180, + ), + ), + CircularProgressIndicator( + color: CustomColors.primary, + ), + ], + ), ), ); } - if (accountLevel == null || accountLevel == 0) { - return const StartPage(); - } - // else if (authenticated == null) { - // AuthService.authenticateWithBiometrics().then((value) { - // setState(() { - // authenticated = value; - // }); - // }); - // } - - switch (accountLevel) { - case null: - return const StartPage(); - case < 1: - return NotificationsPage(); - case 1: - return const RegistrationPage(); - case 2: - return VerifyEmailPage(); - default: - return const StartPage(); - } + return const StartPage(); } } diff --git a/frontend/app/lib/model/services/storage_service.dart b/frontend/app/lib/model/services/storage_service.dart index dd3bc88..64ee2e2 100644 --- a/frontend/app/lib/model/services/storage_service.dart +++ b/frontend/app/lib/model/services/storage_service.dart @@ -57,9 +57,9 @@ class StorageService { Future get accountLevel async { int? level; - final l = await readData('account_level'); - if (l != null) { - level = int.tryParse(l); + final lev = await readData('account_level'); + if (lev != null) { + level = int.tryParse(lev); } return level ?? 0; } diff --git a/frontend/app/lib/pages/notifications_page.dart b/frontend/app/lib/pages/notifications_page.dart index 582b3ee..a2f4690 100644 --- a/frontend/app/lib/pages/notifications_page.dart +++ b/frontend/app/lib/pages/notifications_page.dart @@ -3,119 +3,189 @@ import 'package:app/pages/registration_page.dart'; import 'package:app/util/colors.dart'; import 'package:flutter/material.dart'; -class NotificationsPage extends StatelessWidget { - NotificationsPage({super.key}); +class NotificationsPage extends StatefulWidget { + const NotificationsPage({super.key}); + @override + State createState() => _NotificationsPageState(); +} + +class _NotificationsPageState extends State { final StorageService _storageService = StorageService(); + bool _loading = true; - void _setNotificationSetting(bool enabled) { - _storageService.addAccountLevel(); - _storageService.setNotificationSetting(enabled).then( - (x) => _storageService.notificationSetting.then( - (value) => print('notifications: $value'), - ), - ); + Future _setNotificationSetting(bool enabled) async { + await _storageService.setNotificationSetting(enabled); + } + + @override + void initState() { + _init(); + super.initState(); + } + + void _init() async { + final accountLevel = await _storageService.accountLevel; + if (accountLevel > 2 && mounted) { + await Navigator.push(context, + MaterialPageRoute(builder: (builder) => const RegistrationPage())); + setState(() { + _loading = false; + }); + } else { + setState(() { + _loading = false; + }); + } } @override Widget build(BuildContext context) { return SafeArea( - child: Scaffold( - appBar: AppBar( - iconTheme: IconThemeData( - color: CustomColors.primary, - ), - ), - body: Padding( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), - child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - height: 80, - ), - Image.asset('assets/chat_bubbles.png'), - const SizedBox( - height: 60, - ), - const Text( - 'Erhalte Mitteilungen', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontWeight: FontWeight.bold, - letterSpacing: 2.0, - fontSize: 25, - ), - ), - const SizedBox( - height: 20, - ), - const Text( - 'Du erhältst z. B. eine Mitteilung sobald wir eine Digitale Spur gefunden haben.', - textAlign: TextAlign.center, - ), - const SizedBox( - height: 20, - ), - const Text( - 'Du kannst die Mitteilungen jederzeit wieder deaktivieren.', - textAlign: TextAlign.center, - ), - const Spacer( - flex: 2, - ), - Hero( - tag: 'flow-button', - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: CustomColors.primary, + child: _loading + ? Center( + child: Column( + children: [ + const SizedBox( + height: 150, ), - onPressed: () { - _setNotificationSetting(true); - Navigator.push( - context, - MaterialPageRoute( - builder: (builder) => const RegistrationPage(), - // builder: (builder) => SecurityPage(), - ), - ); - }, - child: const SizedBox( - height: 60, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Mitteilungen erhalten', - style: TextStyle( - fontSize: 20, - ), - ), - ], + Hero( + tag: 'logo', + child: Image.asset( + 'assets/JPEG.jpg', + height: 180, ), ), + CircularProgressIndicator( + color: CustomColors.primary, + ), + ], + ), + ) + : Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + leading: BackButton( + color: CustomColors.primary, + onPressed: () async { + await _storageService.setAccountLevel(1); + if (mounted) { + Navigator.pop(context); + } + }, + ), + iconTheme: IconThemeData( + color: CustomColors.primary, ), ), - const SizedBox( - height: 10, - ), - TextButton( - onPressed: () { - _setNotificationSetting(false); - }, - child: Text( - 'Später', - style: TextStyle(color: CustomColors.primary), + body: Padding( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), + child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox( + height: 80, + ), + Image.asset('assets/chat_bubbles.png'), + const SizedBox( + height: 60, + ), + const Text( + 'Erhalte Mitteilungen', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontWeight: FontWeight.bold, + letterSpacing: 2.0, + fontSize: 25, + ), + ), + const SizedBox( + height: 20, + ), + const Text( + 'Du erhältst z. B. eine Mitteilung sobald wir eine Digitale Spur gefunden haben.', + textAlign: TextAlign.center, + ), + const SizedBox( + height: 20, + ), + const Text( + 'Du kannst die Mitteilungen jederzeit wieder deaktivieren.', + textAlign: TextAlign.center, + ), + const Spacer( + flex: 2, + ), + Hero( + tag: 'flow-button', + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: CustomColors.primary, + ), + onPressed: () async { + await _setNotificationSetting(true); + await _storageService.setAccountLevel(3); + if (mounted) { + Navigator.push( + context, + MaterialPageRoute( + builder: (builder) => const RegistrationPage(), + // builder: (builder) => SecurityPage(), + ), + ); + } + }, + child: const SizedBox( + height: 60, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 1, + child: Text( + 'Mitteilungen erhalten', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + ), + ), + ), + ], + ), + ), + ), + ), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () async { + await _setNotificationSetting(false); + await _storageService.setAccountLevel(3); + if (mounted) { + Navigator.push( + context, + MaterialPageRoute( + builder: (builder) => const RegistrationPage(), + // builder: (builder) => SecurityPage(), + ), + ); + } + }, + child: Text( + 'Später', + style: TextStyle(color: CustomColors.primary), + ), + ), + const Spacer( + flex: 1, + ), + ], ), ), - const Spacer( - flex: 1, - ), - ], - ), - ), - ), + ), ); } } diff --git a/frontend/app/lib/pages/password_page.dart b/frontend/app/lib/pages/password_page.dart index 39f7225..9767dc4 100644 --- a/frontend/app/lib/pages/password_page.dart +++ b/frontend/app/lib/pages/password_page.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'package:app/util/validation.dart'; class PasswordPage extends StatefulWidget { - PasswordPage({super.key, required this.email, required this.register}); + const PasswordPage({super.key, required this.email, required this.register}); final String email; final bool register; @@ -47,170 +47,195 @@ class _PasswordPageState extends State { ), body: Padding( padding: const EdgeInsets.all(20.0), - child: Form( - key: _formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Sichere dein Konto mit einem Passwort', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontWeight: FontWeight.bold, - letterSpacing: 2.0, - fontSize: 25, + child: SingleChildScrollView( + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.register + ? 'Sichere dein Konto mit einem Passwort' + : 'Login', + textAlign: TextAlign.center, + style: const TextStyle( + fontFamily: 'sans-serif', + fontWeight: FontWeight.bold, + letterSpacing: 2.0, + fontSize: 25, + ), ), - ), - const SizedBox( - height: 60, - ), - TextFormField( - controller: _passwordController1, - autocorrect: false, - autofocus: true, - keyboardType: TextInputType.visiblePassword, - obscureText: !_showPassword1, - autovalidateMode: AutovalidateMode.always, - decoration: InputDecoration( - suffixIcon: IconButton( - onPressed: () { - setState(() { - _showPassword1 = !_showPassword1; - }); - }, - icon: Icon(_showPassword1 - ? Icons.remove_red_eye - : Icons.remove_red_eye_outlined)), - label: const Text('Passwort'), - filled: true, + const SizedBox( + height: 60, ), - validator: (value) { - if (value == null || !value.isValidPassword) { - _validPassword = false; - return 'Mindestens 12 Zeichen, Zahlen, Sonderzeichen (-_?!=.,*+), Groß- & Kleinbuchstaben'; - } else { - _validPassword = true; - return null; - } - }, - onChanged: (value) { - _formKey.currentState?.validate(); - if (!value.isValidPassword) { - setState(() { + TextFormField( + controller: _passwordController1, + autocorrect: false, + autofocus: true, + keyboardType: TextInputType.visiblePassword, + obscureText: !_showPassword1, + autovalidateMode: AutovalidateMode.always, + decoration: InputDecoration( + suffixIcon: IconButton( + onPressed: () { + setState(() { + _showPassword1 = !_showPassword1; + }); + }, + icon: Icon(_showPassword1 + ? Icons.remove_red_eye + : Icons.remove_red_eye_outlined)), + label: const Text('Passwort'), + filled: true, + ), + validator: (value) { + if (value == null || !value.isValidPassword) { _validPassword = false; - }); - } else { - setState(() { + return 'Mindestens 12 Zeichen, Zahlen, Sonderzeichen (-_?!=.,*+), Groß- & Kleinbuchstaben'; + } else { + if (!widget.register) { + _passwordsFilled = true; + } _validPassword = true; - }); - } - }, - ), - const SizedBox( - height: 20, - ), - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: !_validPassword - ? null - : TextFormField( - controller: _passwordController2, - keyboardType: TextInputType.visiblePassword, - obscureText: !_showPassword2, - decoration: InputDecoration( - suffixIcon: IconButton( - onPressed: () { - setState(() { - _showPassword2 = !_showPassword2; - }); - }, - icon: Icon(_showPassword2 - ? Icons.remove_red_eye - : Icons.remove_red_eye_outlined)), - label: const Text('Passwort bestätigen'), - filled: true, + return null; + } + }, + onChanged: (value) { + _formKey.currentState?.validate(); + if (!value.isValidPassword) { + setState(() { + _validPassword = false; + }); + } else { + setState(() { + if (!widget.register) { + _passwordsFilled = true; + } + _validPassword = true; + }); + } + }, + ), + const SizedBox( + height: 20, + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: !widget.register || !_validPassword + ? null + : TextFormField( + controller: _passwordController2, + keyboardType: TextInputType.visiblePassword, + obscureText: !_showPassword2, + decoration: InputDecoration( + suffixIcon: IconButton( + onPressed: () { + setState(() { + _showPassword2 = !_showPassword2; + }); + }, + icon: Icon(_showPassword2 + ? Icons.remove_red_eye + : Icons.remove_red_eye_outlined)), + label: const Text('Passwort bestätigen'), + filled: true, + ), + validator: (value) { + if (_passwordController1.text != + _passwordController2.text) { + setState(() { + _passwordsFilled = false; + }); + return 'Passwörter stimmen nicht überein'; + } else { + setState(() { + _passwordsFilled = true; + }); + return null; + } + }, + onTap: () => _formKey.currentState?.validate(), + onChanged: (value) { + _formKey.currentState?.validate(); + if (_passwordController1.text != + _passwordController2.text) { + setState(() { + _passwordsFilled = false; + }); + } else { + setState(() { + _passwordsFilled = true; + }); + } + }, ), - validator: (value) { - if (_passwordController1.text != - _passwordController2.text) { - setState(() { - _passwordsFilled = false; - }); - return 'Passwörter stimmen nicht überein'; - } else { - setState(() { - _passwordsFilled = true; - }); - return null; - } - }, - onTap: () => _formKey.currentState?.validate(), - onChanged: (value) { - _formKey.currentState?.validate(); - if (_passwordController1.text != - _passwordController2.text) { - setState(() { - _passwordsFilled = false; - }); - } else { - setState(() { - _passwordsFilled = true; - }); - } - }, - ), - ), - const SizedBox( - height: 20, - ), - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: !_passwordsFilled || - _passwordController1.text != _passwordController2.text - ? null - : ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: CustomColors.primary, - ), - onPressed: _validPassword && _passwordsFilled - ? () async { - if (_formKey.currentState!.validate()) { - final navigator = Navigator.of(context); - final loggedin = await _vm.createAccount( - context, - email: widget.email, - password: _passwordController1.text, - ); - if (loggedin && mounted) { - _storageService.addAccountLevel(); - navigator.push( - MaterialPageRoute( - builder: (builder) => VerifyEmailPage(), - ), - ); + ), + const SizedBox( + height: 20, + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: !_passwordsFilled || + (widget.register && + _passwordController1.text != + _passwordController2.text) + ? null + : ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: CustomColors.primary, + ), + onPressed: _validPassword && _passwordsFilled + ? () async { + if (_formKey.currentState!.validate()) { + FocusScope.of(context).unfocus(); + final navigator = Navigator.of(context); + bool loggedin = false; + if (widget.register) { + loggedin = await _vm.createAccount( + context, + email: widget.email, + password: _passwordController1.text, + ); + } else { + loggedin = await _vm.login( + context, + email: widget.email, + password: _passwordController1.text, + ); + } + if (loggedin && mounted) { + await _storageService.setAccountLevel(4); + navigator.push( + MaterialPageRoute( + builder: (builder) => + const VerifyEmailPage(), + ), + ); + } } } - } - : null, - child: const SizedBox( - height: 50, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Registrierung abschließen', - style: TextStyle( - fontSize: 20, + : null, + child: SizedBox( + height: 50, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.register + ? 'Registrierung abschließen' + : 'Einloggen', + style: const TextStyle( + fontSize: 20, + ), ), - ), - ], + ], + ), ), ), - ), - ), - ], + ), + ], + ), ), ), ), diff --git a/frontend/app/lib/pages/registration_page.dart b/frontend/app/lib/pages/registration_page.dart index 1477d6a..dd66e8c 100644 --- a/frontend/app/lib/pages/registration_page.dart +++ b/frontend/app/lib/pages/registration_page.dart @@ -1,4 +1,6 @@ +import 'package:app/model/services/storage_service.dart'; import 'package:app/pages/password_page.dart'; +import 'package:app/pages/verify_email_page.dart'; import 'package:app/util/colors.dart'; import 'package:app/util/validation.dart'; import 'package:flutter/material.dart'; @@ -13,6 +15,30 @@ class RegistrationPage extends StatefulWidget { class _RegistrationPageState extends State { final formKey = GlobalKey(); final mailController = TextEditingController(); + bool _loading = true; + + final StorageService _storageService = StorageService(); + + @override + void initState() { + _init(); + super.initState(); + } + + void _init() async { + final accountLevel = await _storageService.accountLevel; + if (accountLevel > 3 && mounted) { + await Navigator.push(context, + MaterialPageRoute(builder: (builder) => const VerifyEmailPage())); + setState(() { + _loading = false; + }); + } else { + setState(() { + _loading = false; + }); + } + } @override void dispose() { @@ -23,138 +49,167 @@ class _RegistrationPageState extends State { @override Widget build(BuildContext context) { return SafeArea( - child: Scaffold( - appBar: AppBar( - iconTheme: IconThemeData( - color: CustomColors.primary, - ), - ), - body: Padding( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), - child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - height: 20, + child: _loading + ? Center( + child: Column( + children: [ + const SizedBox( + height: 150, + ), + Hero( + tag: 'logo', + child: Image.asset( + 'assets/JPEG.jpg', + height: 180, + ), + ), + CircularProgressIndicator( + color: CustomColors.primary, + ), + ], ), - const Text( - 'Jetzt Registrieren', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontWeight: FontWeight.bold, - letterSpacing: 2.0, - fontSize: 25, + ) + : Scaffold( + appBar: AppBar( + leading: BackButton( + color: CustomColors.primary, + onPressed: () async { + await _storageService.setAccountLevel(2); + if (mounted) { + Navigator.pop(context); + } + }, + ), + iconTheme: IconThemeData( + color: CustomColors.primary, ), ), - const SizedBox( - height: 20, - ), - const Text( - 'Gib deine E-Mail Adresse ein.', - // textAlign: TextAlign.center, - ), - const SizedBox( - height: 20, - ), - Form( - key: formKey, + body: Padding( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), child: Column( + // mainAxisAlignment: MainAxisAlignment.center, children: [ - TextFormField( - autocorrect: false, - autofocus: true, - controller: mailController, - keyboardType: TextInputType.emailAddress, - decoration: const InputDecoration( - helperText: 'test', - label: Text('E-Mail Adresse'), - filled: true, + const SizedBox( + height: 20, + ), + const Text( + 'Jetzt Registrieren', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontWeight: FontWeight.bold, + letterSpacing: 2.0, + fontSize: 25, ), - validator: (value) { - if (value == null || !value.isValidEmail) { - return 'Bitte eine valide E-Mail Adresse angeben'; - } else { - return null; - } - }, + ), + const SizedBox( + height: 20, + ), + const Text( + 'Gib deine E-Mail Adresse ein.', + // textAlign: TextAlign.center, + ), + const SizedBox( + height: 20, + ), + Form( + key: formKey, + child: Column( + children: [ + TextFormField( + autocorrect: false, + autofocus: true, + controller: mailController, + keyboardType: TextInputType.emailAddress, + decoration: const InputDecoration( + helperText: 'test', + label: Text('E-Mail Adresse'), + filled: true, + ), + validator: (value) { + if (value == null || !value.isValidEmail) { + return 'Bitte eine valide E-Mail Adresse angeben'; + } else { + return null; + } + }, + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + onPressed: () { + if (formKey.currentState!.validate()) { + Navigator.push( + context, + MaterialPageRoute( + builder: (builder) => PasswordPage( + email: mailController.text, + register: false, + ), + ), + ); + } + }, + child: Text( + 'Stattdessen anmelden', + // textAlign: TextAlign.center, + style: TextStyle( + color: CustomColors.primary, + ), + ), + ), + Hero( + tag: 'flow-button', + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: CustomColors.primary, + ), + onPressed: () { + if (formKey.currentState!.validate()) { + Navigator.push( + context, + MaterialPageRoute( + builder: (builder) => PasswordPage( + email: mailController.text, + register: true, + ), + ), + ); + } + }, + child: const SizedBox( + height: 50, + width: 100, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Weiter', + style: TextStyle( + fontSize: 20, + ), + ), + ], + ), + ), + ), + ), + ], + ), + const Spacer( + flex: 2, ), ], ), ), - const SizedBox( - height: 20, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - TextButton( - onPressed: () { - if (formKey.currentState!.validate()) { - Navigator.push( - context, - MaterialPageRoute( - builder: (builder) => PasswordPage( - email: mailController.text, - register: false, - ), - ), - ); - } - }, - child: Text( - 'Stattdessen anmelden', - // textAlign: TextAlign.center, - style: TextStyle( - color: CustomColors.primary, - ), - ), - ), - Hero( - tag: 'flow-button', - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: CustomColors.primary, - ), - onPressed: () { - if (formKey.currentState!.validate()) { - Navigator.push( - context, - MaterialPageRoute( - builder: (builder) => PasswordPage( - email: mailController.text, - register: true, - ), - ), - ); - } - }, - child: const SizedBox( - height: 50, - width: 100, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Weiter', - style: TextStyle( - fontSize: 20, - ), - ), - ], - ), - ), - ), - ), - ], - ), - const Spacer( - flex: 2, - ), - ], - ), - ), - ), + ), ); } } diff --git a/frontend/app/lib/pages/security_page.dart b/frontend/app/lib/pages/security_page.dart index 236a1f9..fd10999 100644 --- a/frontend/app/lib/pages/security_page.dart +++ b/frontend/app/lib/pages/security_page.dart @@ -1,126 +1,187 @@ import 'package:app/model/services/auth_service.dart'; +import 'package:app/model/services/storage_service.dart'; import 'package:app/pages/notifications_page.dart'; import 'package:app/util/colors.dart'; import 'package:flutter/material.dart'; -class SecurityPage extends StatelessWidget { +class SecurityPage extends StatefulWidget { const SecurityPage({super.key}); + @override + State createState() => _SecurityPageState(); +} + +class _SecurityPageState extends State { + final StorageService _storageService = StorageService(); + bool _loading = true; + + @override + void initState() { + _init(); + super.initState(); + } + + void _init() async { + final accountLevel = await _storageService.accountLevel; + if (accountLevel > 1 && mounted) { + await Navigator.push(context, + MaterialPageRoute(builder: (builder) => const NotificationsPage())); + setState(() { + _loading = false; + }); + } else { + setState(() { + _loading = false; + }); + } + } + @override Widget build(BuildContext context) { return SafeArea( - child: Scaffold( - appBar: AppBar( - iconTheme: IconThemeData(color: CustomColors.primary), - ), - body: Padding( - padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), - child: Center( - child: Column( - children: [ - const Spacer(), - const Hero( - tag: 'flow-icon', - child: Icon( - Icons.fingerprint, - color: Colors.white, - size: 200, + child: _loading + ? Center( + child: Column( + children: [ + const SizedBox( + height: 150, ), - ), - const Spacer(), - const Text( - 'Deine Sicherheit kommt an erster Stelle', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontSize: 25, - fontWeight: FontWeight.bold), - ), - const SizedBox( - height: 30, - ), - const Text( - 'Schütze dein Konto mit der biometrischen Erkennung deines Geräts oder lege einen Code fest.', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontSize: 18, - fontWeight: FontWeight.bold), - ), - const Spacer(), - Hero( - tag: 'flow-button', - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: CustomColors.primary, + Hero( + tag: 'logo', + child: Image.asset( + 'assets/JPEG.jpg', + height: 180, ), - onPressed: () async { - bool isAuthenticated = - await AuthService.authenticateWithBiometrics(); - if (isAuthenticated) { - // ignore: use_build_context_synchronously - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => NotificationsPage()), - ); - } - }, - child: const SizedBox( - height: 60, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'App absichern', - style: TextStyle( - fontSize: 20, + ), + CircularProgressIndicator( + color: CustomColors.primary, + ), + ], + ), + ) + : Scaffold( + appBar: AppBar( + leading: BackButton( + color: CustomColors.primary, + onPressed: () async { + await _storageService.setAccountLevel(0); + if (mounted) { + Navigator.pop(context); + } + }, + ), + iconTheme: IconThemeData(color: CustomColors.primary), + ), + body: Padding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), + child: Center( + child: Column( + children: [ + const Spacer(), + const Hero( + tag: 'flow-icon', + child: Icon( + Icons.fingerprint, + color: Colors.white, + size: 200, + ), + ), + const Spacer(), + const Text( + 'Deine Sicherheit kommt an erster Stelle', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontSize: 25, + fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 30, + ), + const Text( + 'Schütze dein Konto mit der biometrischen Erkennung deines Geräts oder lege einen Code fest.', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontSize: 18, + fontWeight: FontWeight.bold), + ), + const Spacer(), + Hero( + tag: 'flow-button', + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: CustomColors.primary, + ), + onPressed: () async { + bool isAuthenticated = + await AuthService.authenticateWithBiometrics(); + if (isAuthenticated) { + await _storageService.setAccountLevel(2); + // ignore: use_build_context_synchronously + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const NotificationsPage()), + ); + } + }, + child: const SizedBox( + height: 60, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'App absichern', + style: TextStyle( + fontSize: 20, + ), + ), + ], ), ), - ], + ), ), - ), + // const SizedBox( + // height: 10, + // ), + // ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: CustomColors.secondary, + // ), + // onPressed: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (builder) => SecurityPage(), + // ), + // ); + // }, + // child: const SizedBox( + // height: 60, + // child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // 'Eigenen Code festlegen', + // style: TextStyle( + // color: Colors.white, + // fontSize: 22, + // ), + // ), + // ], + // ), + // ), + // ), + const Spacer( + flex: 2, + ), + ], ), ), - // const SizedBox( - // height: 10, - // ), - // ElevatedButton( - // style: ElevatedButton.styleFrom( - // backgroundColor: CustomColors.secondary, - // ), - // onPressed: () { - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (builder) => SecurityPage(), - // ), - // ); - // }, - // child: const SizedBox( - // height: 60, - // child: Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Text( - // 'Eigenen Code festlegen', - // style: TextStyle( - // color: Colors.white, - // fontSize: 22, - // ), - // ), - // ], - // ), - // ), - // ), - const Spacer( - flex: 2, - ), - ], + ), ), - ), - ), - ), ); } } diff --git a/frontend/app/lib/pages/start_page.dart b/frontend/app/lib/pages/start_page.dart index 4ed1dee..65fbf89 100644 --- a/frontend/app/lib/pages/start_page.dart +++ b/frontend/app/lib/pages/start_page.dart @@ -1,112 +1,166 @@ +import 'package:app/model/services/storage_service.dart'; import 'package:app/pages/agb_page.dart'; import 'package:app/pages/security_page.dart'; import 'package:app/util/colors.dart'; import 'package:flutter/material.dart'; -class StartPage extends StatelessWidget { +class StartPage extends StatefulWidget { const StartPage({super.key}); + @override + State createState() => _StartPageState(); +} + +class _StartPageState extends State { + final StorageService _storageService = StorageService(); + bool _loading = true; + + @override + void initState() { + _init(); + super.initState(); + } + + void _init() async { + int accountLevel = await _storageService.accountLevel; + if (accountLevel > 0 && mounted) { + await Navigator.push(context, + MaterialPageRoute(builder: (builder) => const SecurityPage())); + setState(() { + _loading = false; + }); + } else { + setState(() { + _loading = false; + }); + } + } + @override Widget build(BuildContext context) { return SafeArea( - child: Scaffold( - appBar: AppBar( - iconTheme: IconThemeData(color: CustomColors.primary), - ), - body: Padding( - padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), - child: Column( - children: [ - Hero( - tag: 'flow-icon', - child: Image.asset( - 'assets/JPEG.jpg', - height: 180, - ), - ), - const SizedBox( - height: 30, - ), - const Text( - 'Hallo. Digitale Spuren\nentfernen\nper Knopfdruck.', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontWeight: FontWeight.bold, - letterSpacing: 2.0, - fontSize: 25, - ), - ), - const SizedBox( - height: 20, - ), - const Text( - 'Mit uns finden Sie Ihre Digitalen Spuren und können diese entfernen.', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontSize: 18, - ), - ), - const Spacer( - flex: 1, - ), - Hero( - tag: 'flow-button', - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: CustomColors.primary, + child: _loading + ? Center( + child: Column( + children: [ + const SizedBox( + height: 150, ), - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (builder) => SecurityPage(), - // builder: (builder) => SecurityPage(), - ), - ); - }, - child: const SizedBox( - height: 60, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Weiter', - style: TextStyle( - fontSize: 20, - ), - ), - ], + Hero( + tag: 'logo', + child: Image.asset( + 'assets/JPEG.jpg', + height: 180, ), ), + CircularProgressIndicator( + color: CustomColors.primary, + ), + ], + ), + ) + : Scaffold( + appBar: AppBar( + iconTheme: IconThemeData(color: CustomColors.primary), + ), + body: Padding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 16), + child: Column( + children: [ + Hero( + tag: 'flow-icon', + child: Image.asset( + 'assets/JPEG.jpg', + height: 180, + ), + ), + const SizedBox( + height: 30, + ), + const Text( + 'Hallo. Digitale Spuren\nentfernen\nper Knopfdruck.', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontWeight: FontWeight.bold, + letterSpacing: 2.0, + fontSize: 25, + ), + ), + const SizedBox( + height: 20, + ), + const Text( + 'Mit uns finden Sie Ihre Digitalen Spuren und können diese entfernen.', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontSize: 18, + ), + ), + const Spacer( + flex: 1, + ), + Hero( + tag: 'flow-button', + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: CustomColors.primary, + ), + onPressed: () async { + await _storageService.setAccountLevel(1); + if (mounted) { + Navigator.push( + context, + MaterialPageRoute( + builder: (builder) => const SecurityPage(), + // builder: (builder) => SecurityPage(), + ), + ); + } + }, + child: const SizedBox( + height: 60, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Weiter', + style: TextStyle( + fontSize: 20, + ), + ), + ], + ), + ), + ), + ), + const Spacer( + flex: 1, + ), + const Text( + 'Mit der weiteren Nutzung stimmst du den folgenden Bedingungen zu:', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontSize: 16, + ), + ), + TextButton( + onPressed: () { + showDialog( + context: context, + builder: (builder) => AgbPage()); + }, + child: Text( + 'AGB - Datenschutzerklärung', + textAlign: TextAlign.center, + style: TextStyle(color: CustomColors.primary), + )) + ], ), ), - const Spacer( - flex: 1, - ), - const Text( - 'Mit der weiteren Nutzung stimmst du den folgenden Bedingungen zu:', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontSize: 16, - ), - ), - TextButton( - onPressed: () { - showDialog( - context: context, builder: (builder) => AgbPage()); - }, - child: Text( - 'AGB - Datenschutzerklärung', - textAlign: TextAlign.center, - style: TextStyle(color: CustomColors.primary), - )) - ], - ), - ), - ), + ), ); } } diff --git a/frontend/app/lib/pages/verify_email_page.dart b/frontend/app/lib/pages/verify_email_page.dart index 156cbf3..ddc26c4 100644 --- a/frontend/app/lib/pages/verify_email_page.dart +++ b/frontend/app/lib/pages/verify_email_page.dart @@ -1,110 +1,156 @@ +import 'package:app/model/services/storage_service.dart'; import 'package:app/util/colors.dart'; import 'package:flutter/material.dart'; -class VerifyEmailPage extends StatelessWidget { - VerifyEmailPage({super.key}); +class VerifyEmailPage extends StatefulWidget { + const VerifyEmailPage({super.key}); + + @override + State createState() => _VerifyEmailPageState(); +} + +class _VerifyEmailPageState extends State { + final StorageService _storageService = StorageService(); + bool _loading = true; + + @override + void initState() { + super.initState(); + setState(() { + _loading = false; + }); + } @override Widget build(BuildContext context) { return SafeArea( - child: Scaffold( - appBar: AppBar( - iconTheme: IconThemeData( - color: CustomColors.primary, - ), - ), - body: Padding( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), - child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - height: 80, - ), - const Text( - 'Verifizieren', - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: 'sans-serif', - fontWeight: FontWeight.bold, - letterSpacing: 2.0, - fontSize: 25, - ), - ), - const SizedBox( - height: 50, - ), - const Text( - 'Wir haben dir eine E-Mail geschickt.', - textAlign: TextAlign.center, - // textAlign: TextAlign.center, - ), - const SizedBox( - height: 20, - ), - const Text( - 'Bitte verifiziere deine E-Mail Adresse, dann geht es weiter.', - textAlign: TextAlign.center, - // textAlign: TextAlign.center, - ), - const SizedBox( - height: 80, - ), - Hero( - tag: 'flow-button', - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: CustomColors.primary, + child: _loading + ? Center( + child: Column( + children: [ + const SizedBox( + height: 150, ), - onPressed: () {}, - child: const SizedBox( - height: 50, - width: 100, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Weiter', - style: TextStyle( - fontSize: 20, - ), - ), - ], + Hero( + tag: 'logo', + child: Image.asset( + 'assets/JPEG.jpg', + height: 180, ), ), - ), - ), - const SizedBox( - height: 60, - ), - const Text( - 'Noch keine E-Mail erhalten?', - // textAlign: TextAlign.center, - ), - const Text( - 'Schon im Spam-Ordner nachgeschaut?', - // textAlign: TextAlign.center, - ), - const SizedBox( - height: 20, - ), - TextButton( - onPressed: () {}, - child: Text( - 'Erneut senden', - // textAlign: TextAlign.center, - style: TextStyle( + CircularProgressIndicator( color: CustomColors.primary, ), + ], + ), + ) + : Scaffold( + appBar: AppBar( + leading: BackButton( + color: CustomColors.primary, + onPressed: () async { + await _storageService.setAccountLevel(3); + if (mounted) { + Navigator.pop(context); + } + }, + ), + iconTheme: IconThemeData( + color: CustomColors.primary, ), ), - const Spacer( - flex: 2, + body: Padding( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), + child: Column( + // mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox( + height: 80, + ), + const Text( + 'Verifizieren', + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: 'sans-serif', + fontWeight: FontWeight.bold, + letterSpacing: 2.0, + fontSize: 25, + ), + ), + const SizedBox( + height: 50, + ), + const Text( + 'Wir haben dir eine E-Mail geschickt.', + textAlign: TextAlign.center, + // textAlign: TextAlign.center, + ), + const SizedBox( + height: 20, + ), + const Text( + 'Bitte verifiziere deine E-Mail Adresse, dann geht es weiter.', + textAlign: TextAlign.center, + // textAlign: TextAlign.center, + ), + const SizedBox( + height: 80, + ), + Hero( + tag: 'flow-button', + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: CustomColors.primary, + ), + onPressed: () {}, + child: const SizedBox( + height: 50, + width: 100, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Weiter', + style: TextStyle( + fontSize: 20, + ), + ), + ], + ), + ), + ), + ), + const SizedBox( + height: 60, + ), + const Text( + 'Noch keine E-Mail erhalten?', + // textAlign: TextAlign.center, + ), + const Text( + 'Schon im Spam-Ordner nachgeschaut?', + // textAlign: TextAlign.center, + ), + const SizedBox( + height: 20, + ), + TextButton( + onPressed: () {}, + child: Text( + 'Erneut senden', + // textAlign: TextAlign.center, + style: TextStyle( + color: CustomColors.primary, + ), + ), + ), + const Spacer( + flex: 2, + ), + ], + ), ), - ], - ), - ), - ), + ), ); } } diff --git a/frontend/app/lib/util/colors.dart b/frontend/app/lib/util/colors.dart index 3000db3..e7a4484 100644 --- a/frontend/app/lib/util/colors.dart +++ b/frontend/app/lib/util/colors.dart @@ -6,7 +6,7 @@ class CustomColors { } static Color get success { - return const Color.fromARGB(200, 55, 125, 55); + return const Color.fromARGB(255, 51, 217, 178); } static Color get primary {