diff --git a/mobile/lib/controllers/session.dart b/mobile/lib/controllers/session.dart deleted file mode 100644 index e8e81d3..0000000 --- a/mobile/lib/controllers/session.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mobile/models/user.dart'; -import 'package:mobile/results.dart'; -import 'package:mobile/server/server.dart'; - -class SessionController extends ChangeNotifier { - final Server server; - String? _sessionToken; - User? _user; - - SessionController({required this.server}); - - Future> login(String email, String password) async { - switch (await server.login(email, password)) { - case Success(data: final token): - _sessionToken = token; - notifyListeners(); - return const Ok(null); - case Error(message: final message): - notifyListeners(); - return Err(message); - } - } - - Future _validateToken() async { - final token = _sessionToken; - if (token == null) { - return; - } - final res = await server.sessionUser(token); - switch (res) { - case Success(): - return; - case Error(): - _sessionToken = null; - return; - } - } - - User? get user { - loadUser(); - return _user; - } - - Future _notifyIfTokenChanged() async { - final prev = _sessionToken; - _validateToken(); - if (prev != _sessionToken) { - notifyListeners(); - } - } - - Future loadUser() async { - final token = _sessionToken; - if (token == null) { - _user = null; - return; - } - final res = await server.sessionUser(token); - switch (res) { - case Success(data: final user): - _user = user; - return; - case Error(): - _user = null; - return; - } - } - - String? get sessionToken { - _notifyIfTokenChanged(); - return _sessionToken; - } - - Future logout() async { - final token = _sessionToken; - if (token != null) { - await server.logout(token); - _sessionToken = null; - } - notifyListeners(); - } -} diff --git a/mobile/lib/controllers/user.dart b/mobile/lib/controllers/user.dart index 2d3cb9a..6a81b1d 100644 --- a/mobile/lib/controllers/user.dart +++ b/mobile/lib/controllers/user.dart @@ -1,27 +1,120 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/session.dart'; +import 'package:mobile/models/user.dart'; import 'package:mobile/results.dart'; import 'package:mobile/server/server.dart'; -class UsersController extends ChangeNotifier { - Server server; - SessionController sessionController; +class UserController extends ChangeNotifier { + final Server server; + String? _sessionToken; + User? _user; - UsersController({required this.server, required this.sessionController}); + Future> userLoad = Future.error(Null); - Future> register( - String name, String email, String password) async { - final res = await server.register(name, email, password); - switch (res) { - case Success(): + UserController({required this.server}); + + /// Make sure a user exists before calling using `.loadUser()`. + User get user { + final user = _user; + if (user == null) { + throw NoUserExcept(); + } + return user; + } + + Future> loadUser() async { + if (_sessionToken == null) { + return const Err(null); + } + + final userResult = await server.sessionUser(_sessionToken!); + switch (userResult) { + case Success(data: final user): + _user = user; return const Ok(null); - case Error(message: final message): + case Error(): + return const Err(null); + } + } + + Future> loadedUser() async { + throw Exception(); + } + + Future> login(String email, String password) async { + switch (await server.login(email, password)) { + case Success(data: final token): + _sessionToken = token; + notifyListeners(); + return const Ok(null); + case Error(message: final message): + notifyListeners(); return Err(message); } } + Future _validateToken() async { + final token = _sessionToken; + if (token == null) { + return; + } + final res = await server.sessionUser(token); + switch (res) { + case Success(): + return; + case Error(): + _sessionToken = null; + return; + } + } + + @Deprecated("Use 'user' instead.") + User? get userOld { + loadUserOld(); + return _user; + } + + Future _notifyIfTokenChanged() async { + final prev = _sessionToken; + _validateToken(); + if (prev != _sessionToken) { + notifyListeners(); + } + } + + @Deprecated("Use 'loadUser' instead.") + Future loadUserOld() async { + final token = _sessionToken; + if (token == null) { + _user = null; + return; + } + final res = await server.sessionUser(token); + switch (res) { + case Success(data: final user): + _user = user; + return; + case Error(): + _user = null; + return; + } + } + + String? get sessionToken { + _notifyIfTokenChanged(); + return _sessionToken; + } + + Future logout() async { + final token = _sessionToken; + if (token != null) { + await server.logout(token); + _sessionToken = null; + } + notifyListeners(); + } + Future> addBalance() async { - final token = sessionController.sessionToken; + final token = _sessionToken; if (token == null) { return const Err("No token"); } @@ -35,3 +128,5 @@ class UsersController extends ChangeNotifier { } } } + +class NoUserExcept implements Exception {} diff --git a/mobile/lib/controllers/users.dart b/mobile/lib/controllers/users.dart new file mode 100644 index 0000000..847078e --- /dev/null +++ b/mobile/lib/controllers/users.dart @@ -0,0 +1,19 @@ +import 'package:mobile/results.dart'; +import 'package:mobile/server/server.dart'; + +class UsersController { + Server server; + + UsersController({required this.server}); + + Future> register( + String name, String email, String password) async { + final res = await server.register(name, email, password); + switch (res) { + case Success(): + return const Ok(null); + case Error(message: final message): + return Err(message); + } + } +} diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 271885f..9406f0e 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:mobile/controllers/session.dart'; +import 'package:mobile/controllers/user.dart'; import 'package:mobile/pages/dashboard.dart'; import 'package:mobile/pages/log_in_page.dart'; import 'package:mobile/controllers/add_to_cart_state.dart'; @@ -9,25 +9,37 @@ import 'package:mobile/controllers/location_image.dart'; import 'package:mobile/controllers/paying_state.dart'; import 'package:mobile/controllers/product.dart'; import 'package:mobile/controllers/receipt.dart'; -import 'package:mobile/controllers/user.dart'; +import 'package:mobile/controllers/users.dart'; import 'package:mobile/server/backend_server.dart'; +import 'package:mobile/server/server.dart'; import 'package:provider/provider.dart'; import 'package:mobile/controllers/routing.dart'; void main() { - runApp(const MyApp()); + final server = BackendServer(); + final users = UsersController(server: server); + + final user = UserController(server: server); + user.loadUser().ignore(); + + runApp(MyApp( + users: users, + server: server, + )); } class MyApp extends StatelessWidget { - const MyApp({super.key}); + final UsersController users; + + final Server server; + + const MyApp({super.key, required this.users, required this.server}); @override Widget build(BuildContext context) { - final server = BackendServer(); return MultiProvider( providers: [ - ChangeNotifierProvider( - create: (_) => SessionController(server: server)), + ChangeNotifierProvider(create: (_) => UserController(server: server)), ChangeNotifierProvider(create: (_) => RoutingController()), ChangeNotifierProvider( create: (_) => ProductController(server: server)), @@ -37,10 +49,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => PayingStateController()), ChangeNotifierProvider(create: (_) => AddToCartStateController()), ChangeNotifierProvider(create: (_) => LocationImageController()), - ChangeNotifierProvider( - create: (context) => UsersController( - server: server, - sessionController: context.read())), + Provider(create: (_) => users), ], child: MaterialApp( title: 'Fresh Plaza', @@ -52,7 +61,7 @@ class MyApp extends StatelessWidget { GoogleFonts.merriweatherTextTheme(Theme.of(context).textTheme), useMaterial3: true, ), - home: Consumer( + home: Consumer( builder: (_, sessionController, __) { if (sessionController.sessionToken is String) { return Dashboard(); diff --git a/mobile/lib/pages/finish_shopping_page.dart b/mobile/lib/pages/finish_shopping_page.dart index 8cfb456..392c9a8 100644 --- a/mobile/lib/pages/finish_shopping_page.dart +++ b/mobile/lib/pages/finish_shopping_page.dart @@ -3,7 +3,7 @@ import 'package:mobile/controllers/routing.dart'; import 'package:mobile/controllers/cart.dart'; import 'package:mobile/controllers/paying_state.dart'; import 'package:mobile/controllers/receipt.dart'; -import 'package:mobile/controllers/session.dart'; +import 'package:mobile/controllers/user.dart'; import 'package:mobile/results.dart'; import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/primary_button.dart'; @@ -57,7 +57,7 @@ class FinishShoppingPage extends StatelessWidget { child: Center( child: PrimaryButton( onPressed: () async { - final session = context.read(); + final session = context.read(); payingStateRepo.next(); await Future.delayed(const Duration(seconds: 1)); if (cartController.purchase(session.sessionToken!) diff --git a/mobile/lib/pages/home_page.dart b/mobile/lib/pages/home_page.dart index f934154..4e6f063 100644 --- a/mobile/lib/pages/home_page.dart +++ b/mobile/lib/pages/home_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/session.dart'; import 'package:mobile/controllers/user.dart'; import 'package:mobile/pages/settings_page.dart'; import 'package:mobile/utils/build_if_session_exists.dart'; @@ -11,8 +10,8 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { - final sessionController = context.watch(); - context.watch(); + final userController = context.watch(); + return Column( children: [ Row( @@ -37,7 +36,7 @@ class HomePage extends StatelessWidget { ), padding: const EdgeInsets.all(10), child: BuildIfSessionUserExists( - sessionController: sessionController, + sessionController: userController, placeholder: const CircularProgressIndicator(), builder: (context, user) => Text( "Saldo: ${formatDkkCents(user.balanceDkkCents)}", @@ -48,7 +47,7 @@ class HomePage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ BuildIfSessionUserExists( - sessionController: sessionController, + sessionController: userController, placeholder: const CircularProgressIndicator(), builder: (context, user) => Text( "Velkommen ${user.name}", diff --git a/mobile/lib/pages/log_in_page.dart b/mobile/lib/pages/log_in_page.dart index 2edf715..ccb9454 100644 --- a/mobile/lib/pages/log_in_page.dart +++ b/mobile/lib/pages/log_in_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/session.dart'; +import 'package:mobile/controllers/user.dart'; import 'package:mobile/pages/register_page.dart'; import 'package:mobile/results.dart'; import 'package:mobile/widgets/error_box.dart'; @@ -30,6 +30,8 @@ class LogInFormState extends State { bool loginError = false; @override Widget build(BuildContext context) { + final userController = context.read(); + final mailController = TextEditingController(); final passwordController = TextEditingController(); @@ -61,8 +63,7 @@ class LogInFormState extends State { ), PrimaryButton( onPressed: () async { - final sessionController = context.read(); - final loginResult = await sessionController.login( + final loginResult = await userController.login( mailController.text, passwordController.text); switch (loginResult) { case Ok(): diff --git a/mobile/lib/pages/register_page.dart b/mobile/lib/pages/register_page.dart index 0afdaf1..263ba7a 100644 --- a/mobile/lib/pages/register_page.dart +++ b/mobile/lib/pages/register_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/user.dart'; +import 'package:mobile/controllers/users.dart'; import 'package:mobile/results.dart'; import 'package:mobile/widgets/error_box.dart'; import 'package:mobile/widgets/primary_button.dart'; @@ -29,11 +29,12 @@ class RegisterFormState extends State { bool registerError = false; String errorText = "Ingen fejlbesked jeg skal ikke vises"; + final nameController = TextEditingController(); + final mailController = TextEditingController(); + final passwordController = TextEditingController(); + @override Widget build(BuildContext context) { - final nameController = TextEditingController(); - final mailController = TextEditingController(); - final passwordController = TextEditingController(); return Column( spacing: 10, mainAxisAlignment: MainAxisAlignment.center, @@ -72,8 +73,8 @@ class RegisterFormState extends State { obscure: true), PrimaryButton( onPressed: () async { - final sessionsRepo = context.read(); - final res = await sessionsRepo.register(nameController.text, + final usersController = context.read(); + final res = await usersController.register(nameController.text, mailController.text, passwordController.text); if (res is Ok) { setState(() => registerError = false); diff --git a/mobile/lib/pages/settings_page.dart b/mobile/lib/pages/settings_page.dart index d0f1ddb..68553c2 100644 --- a/mobile/lib/pages/settings_page.dart +++ b/mobile/lib/pages/settings_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/session.dart'; +import 'package:mobile/controllers/user.dart'; import 'package:mobile/pages/settings_pages/saldo.dart'; import 'package:provider/provider.dart'; @@ -30,7 +30,7 @@ class SettingsPage extends StatelessWidget { icon: Icons.door_back_door, title: "Log ud", action: (context) async { - final sessionsController = context.read(); + final sessionsController = context.read(); Navigator.pop(context); await sessionsController.logout(); }), diff --git a/mobile/lib/pages/settings_pages/saldo.dart b/mobile/lib/pages/settings_pages/saldo.dart index f818743..5de59d5 100644 --- a/mobile/lib/pages/settings_pages/saldo.dart +++ b/mobile/lib/pages/settings_pages/saldo.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/session.dart'; import 'package:mobile/controllers/user.dart'; import 'package:mobile/results.dart'; import 'package:mobile/utils/build_if_session_exists.dart'; @@ -11,8 +10,7 @@ class SaldoSettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - final sessionController = context.watch(); - final userController = context.watch(); + final sessionController = context.watch(); return Scaffold( backgroundColor: Colors.white, body: SafeArea( @@ -38,7 +36,7 @@ class SaldoSettingsPage extends StatelessWidget { }), ElevatedButton.icon( onPressed: () async { - final res = await userController.addBalance(); + final res = await sessionController.addBalance(); switch (res) { case Ok(): print("yay"); diff --git a/mobile/lib/utils/build_if_session_exists.dart b/mobile/lib/utils/build_if_session_exists.dart index 20f2503..ec8524f 100644 --- a/mobile/lib/utils/build_if_session_exists.dart +++ b/mobile/lib/utils/build_if_session_exists.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/session.dart'; +import 'package:mobile/controllers/user.dart'; import 'package:mobile/models/user.dart'; class BuildIfSessionUserExists extends StatelessWidget { - final SessionController sessionController; + final UserController sessionController; final Widget placeholder; final Widget Function(BuildContext, User) builder; @@ -16,9 +16,9 @@ class BuildIfSessionUserExists extends StatelessWidget { @override Widget build(BuildContext context) { return FutureBuilder( - future: sessionController.loadUser(), + future: sessionController.loadUserOld(), builder: (context, snapshot) { - final user = sessionController.user; + final user = sessionController.userOld; if (user == null) { return placeholder; }