From 9603651e051445328886396eddfb3e74ce6c0291 Mon Sep 17 00:00:00 2001 From: SFJ Date: Tue, 18 Mar 2025 13:48:08 +0100 Subject: [PATCH] refactor controllers --- git-authors.toml | 7 + mobile/lib/controllers/cart.dart | 4 +- mobile/lib/controllers/product.dart | 4 +- mobile/lib/controllers/session.dart | 163 ++++++++++++++++++ mobile/lib/controllers/user.dart | 132 -------------- mobile/lib/controllers/users.dart | 4 +- mobile/lib/main.dart | 48 ++++-- mobile/lib/pages/finish_shopping_page.dart | 74 ++++---- mobile/lib/pages/home_page.dart | 45 ++--- mobile/lib/pages/log_in_page.dart | 22 +-- mobile/lib/pages/settings_page.dart | 10 +- mobile/lib/pages/settings_pages/saldo.dart | 39 +++-- mobile/lib/server/backend_server.dart | 48 +++--- mobile/lib/server/mock_server.dart | 38 ++-- mobile/lib/server/server.dart | 28 +-- mobile/lib/utils/build_if_session_exists.dart | 27 +-- mobile/test/widget_test.dart | 30 ---- 17 files changed, 364 insertions(+), 359 deletions(-) create mode 100644 git-authors.toml create mode 100644 mobile/lib/controllers/session.dart delete mode 100644 mobile/lib/controllers/user.dart delete mode 100644 mobile/test/widget_test.dart diff --git a/git-authors.toml b/git-authors.toml new file mode 100644 index 0000000..85f4b7e --- /dev/null +++ b/git-authors.toml @@ -0,0 +1,7 @@ +[authors.mtk] +name = "Mikkel Troels Kongsted" +email = "mtkongsted@gmail.com" + +[authors.sfj] +name = "SFJ" +email = "simonfromjakobsen@gmail.com" \ No newline at end of file diff --git a/mobile/lib/controllers/cart.dart b/mobile/lib/controllers/cart.dart index 1baa3c1..2cfa3cf 100644 --- a/mobile/lib/controllers/cart.dart +++ b/mobile/lib/controllers/cart.dart @@ -119,9 +119,9 @@ class CartControllerMemory extends CartController { Future> purchase(String token) async { final res = await server.purchaseCart(token, cart); switch (res) { - case Success(): + case Ok(): return const Ok(null); - case Error(message: final message): + case Err(value: final message): return Err(message); } } diff --git a/mobile/lib/controllers/product.dart b/mobile/lib/controllers/product.dart index a555c94..caa06a1 100644 --- a/mobile/lib/controllers/product.dart +++ b/mobile/lib/controllers/product.dart @@ -15,10 +15,10 @@ class ProductController extends ChangeNotifier { Future fetchProductsFromServer() async { final res = await server.allProducts(); switch (res) { - case Success>(data: final data): + case Ok, String>(value: final data): products = data; notifyListeners(); - case Error>(): + case Err, String>(): return; } } diff --git a/mobile/lib/controllers/session.dart b/mobile/lib/controllers/session.dart new file mode 100644 index 0000000..c3cba2d --- /dev/null +++ b/mobile/lib/controllers/session.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/models/user.dart'; +import 'package:mobile/results.dart'; +import 'package:mobile/server/server.dart'; + +class SessionController { + final Server server; + + String? _sessionToken; + User? _sessionUser; + + final List<_ChangeListener> _sessionChangeListeners = []; + final List<_ChangeListener> _userChangeListeners = []; + + SessionController({required this.server}); + + Future> loginUser(String email, String password) async { + final loginResult = await server.login(email, password); + switch (loginResult) { + case Ok(value: final sessionToken): + _sessionToken = sessionToken; + notifySessionChangeListeners(); + return const Ok(null); + case Err(value: final message): + return Err(message); + } + } + + Future> loadUser() async { + // TODO: retrieve session from cache, if exists + return _loadCurrentUser(); + } + + Future> _loadCurrentUser() async { + final sessionUserResult = await _requestWithSession( + (server, sessionToken) => server.sessionUser(sessionToken)); + switch (sessionUserResult) { + case Ok(value: final sessionUser): + _sessionUser = sessionUser; + notifyUserChangeListeners(); + + // The mechanism for checking that a user is logged in, only listens on + // the session provider. There we also notify sessions listeners, to + // account for this one specific case. Is this smart? idk. + notifySessionChangeListeners(); + + return const Ok(null); + case Err(): + return const Err(null); + } + } + + Future logout() async { + final sessionToken = _sessionToken; + if (sessionToken == null) { + return; + } + await server.logout(sessionToken); + _sessionToken = null; + notifySessionChangeListeners(); + } + + User get user { + final user = _sessionUser; + if (user == null) { + throw NoUser(); + } + return user; + } + + bool get hasUser { + return _sessionUser != null; + } + + Future> addBalance() async { + final addBalanceResult = await _requestWithSession( + (server, sessionToken) => server.addBalance(sessionToken)); + if (addBalanceResult case Err(value: final message)) { + return Err(message); + } + if (await _loadCurrentUser() case Err()) { + return const Err("could not fetch user"); + } + return const Ok(null); + } + + /// Package private. + Future> _requestWithSession( + Future> Function(Server server, String sessionToken) + func) async { + final sessionToken = _sessionToken; + if (sessionToken == null) { + return const Err("unathorized"); + } + final result = await func(server, sessionToken); + if (result case Err(value: final message)) { + if (message == "unauthorized") { + _sessionToken = null; + _sessionUser = null; + notifySessionChangeListeners(); + notifyUserChangeListeners(); + return const Err("unathorized"); + } + } + return result; + } + + /// Package private. + void _addSessionChangeListener(_ChangeListener listener) { + _sessionChangeListeners.add(listener); + } + + /// Package private. + void _addUserChangeListener(_ChangeListener listener) { + _userChangeListeners.add(listener); + } + + /// Class private. + void notifySessionChangeListeners() { + for (final listener in _sessionChangeListeners) { + listener.notify(); + } + } + + /// Class private. + void notifyUserChangeListeners() { + for (final listener in _userChangeListeners) { + listener.notify(); + } + } +} + +abstract class _ChangeListener { + void notify(); +} + +class NoUser implements Exception {} + +class SessionProvider extends ChangeNotifier implements _ChangeListener { + final SessionController controller; + + SessionProvider({required this.controller}) { + controller._addSessionChangeListener(this); + } + + @override + void notify() { + notifyListeners(); + } +} + +class CurrentUserProvider extends ChangeNotifier implements _ChangeListener { + final SessionController controller; + + CurrentUserProvider({required this.controller}) { + controller._addUserChangeListener(this); + } + + @override + void notify() { + notifyListeners(); + } +} diff --git a/mobile/lib/controllers/user.dart b/mobile/lib/controllers/user.dart deleted file mode 100644 index 6a81b1d..0000000 --- a/mobile/lib/controllers/user.dart +++ /dev/null @@ -1,132 +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 UserController extends ChangeNotifier { - final Server server; - String? _sessionToken; - User? _user; - - Future> userLoad = Future.error(Null); - - 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(): - 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 = _sessionToken; - if (token == null) { - return const Err("No token"); - } - final res = await server.addBalance(token); - notifyListeners(); - switch (res) { - case Success(): - return const Ok(null); - case Error(message: final message): - return Err(message); - } - } -} - -class NoUserExcept implements Exception {} diff --git a/mobile/lib/controllers/users.dart b/mobile/lib/controllers/users.dart index 847078e..15fd89e 100644 --- a/mobile/lib/controllers/users.dart +++ b/mobile/lib/controllers/users.dart @@ -10,9 +10,9 @@ class UsersController { String name, String email, String password) async { final res = await server.register(name, email, password); switch (res) { - case Success(): + case Ok(): return const Ok(null); - case Error(message: final message): + case Err(value: final message): return Err(message); } } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 9406f0e..4a1fb12 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/user.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/pages/dashboard.dart'; import 'package:mobile/pages/log_in_page.dart'; import 'package:mobile/controllers/add_to_cart_state.dart'; @@ -10,6 +10,7 @@ import 'package:mobile/controllers/paying_state.dart'; import 'package:mobile/controllers/product.dart'; import 'package:mobile/controllers/receipt.dart'; import 'package:mobile/controllers/users.dart'; +import 'package:mobile/results.dart'; import 'package:mobile/server/backend_server.dart'; import 'package:mobile/server/server.dart'; import 'package:provider/provider.dart'; @@ -17,29 +18,38 @@ import 'package:mobile/controllers/routing.dart'; void main() { final server = BackendServer(); - final users = UsersController(server: server); + final usersController = UsersController(server: server); + final sessionController = SessionController(server: server); - final user = UserController(server: server); - user.loadUser().ignore(); + sessionController.loadUser(); runApp(MyApp( - users: users, + usersController: usersController, + sessionController: sessionController, server: server, )); } class MyApp extends StatelessWidget { - final UsersController users; + final UsersController usersController; + final SessionController sessionController; final Server server; - const MyApp({super.key, required this.users, required this.server}); + const MyApp( + {super.key, + required this.usersController, + required this.sessionController, + required this.server}); @override Widget build(BuildContext context) { return MultiProvider( providers: [ - ChangeNotifierProvider(create: (_) => UserController(server: server)), + ChangeNotifierProvider( + create: (_) => SessionProvider(controller: sessionController)), + ChangeNotifierProvider( + create: (_) => CurrentUserProvider(controller: sessionController)), ChangeNotifierProvider(create: (_) => RoutingController()), ChangeNotifierProvider( create: (_) => ProductController(server: server)), @@ -49,7 +59,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => PayingStateController()), ChangeNotifierProvider(create: (_) => AddToCartStateController()), ChangeNotifierProvider(create: (_) => LocationImageController()), - Provider(create: (_) => users), + Provider(create: (_) => usersController), ], child: MaterialApp( title: 'Fresh Plaza', @@ -61,12 +71,24 @@ class MyApp extends StatelessWidget { GoogleFonts.merriweatherTextTheme(Theme.of(context).textTheme), useMaterial3: true, ), - home: Consumer( - builder: (_, sessionController, __) { - if (sessionController.sessionToken is String) { + home: Consumer( + builder: (_, provider, ___) { + if (provider.controller.hasUser) { return Dashboard(); } - return const LogInPage(); + return FutureBuilder( + future: provider.controller.loadUser(), + builder: (_, snapshot) { + final error = snapshot.error; + if (error != null) { + throw error; + } + if (snapshot.data != null && + snapshot.data is Err) { + return const LoginPage(); + } + return const Scaffold(body: CircularProgressIndicator()); + }); }, )), ); diff --git a/mobile/lib/pages/finish_shopping_page.dart b/mobile/lib/pages/finish_shopping_page.dart index 392c9a8..1e08115 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/user.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/results.dart'; import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/primary_button.dart'; @@ -57,42 +57,42 @@ class FinishShoppingPage extends StatelessWidget { child: Center( child: PrimaryButton( onPressed: () async { - final session = context.read(); - payingStateRepo.next(); - await Future.delayed(const Duration(seconds: 1)); - if (cartController.purchase(session.sessionToken!) - is Err) { - if (context.mounted) { - showDialog( - context: context, - builder: (BuildContext context) => - AlertDialog( - content: const Text( - 'Du har desværre ikke råd til at købe dette'), - actions: [ - TextButton( - onPressed: () => - Navigator.pop(context, 'OK'), - child: const Text('OK'), - ), - ], - ), - ); - } - payingStateRepo.reset(); - return; - } - receiptRepo.createReceipt(cart); - payingStateRepo.next(); - await Future.delayed(const Duration(seconds: 1)); - cartController.clearCart(); - payingStateRepo.reset(); - if (context.mounted) { - Navigator.pop(context); - final RoutingController routing = - context.read(); - routing.routeTo(PageSelector.homePage); - } + // final session = context.read(); + // payingStateRepo.next(); + // await Future.delayed(const Duration(seconds: 1)); + // if (cartController.purchase(session.sessionToken!) + // is Err) { + // if (context.mounted) { + // showDialog( + // context: context, + // builder: (BuildContext context) => + // AlertDialog( + // content: const Text( + // 'Du har desværre ikke råd til at købe dette'), + // actions: [ + // TextButton( + // onPressed: () => + // Navigator.pop(context, 'OK'), + // child: const Text('OK'), + // ), + // ], + // ), + // ); + // } + // payingStateRepo.reset(); + // return; + // } + // receiptRepo.createReceipt(cart); + // payingStateRepo.next(); + // await Future.delayed(const Duration(seconds: 1)); + // cartController.clearCart(); + // payingStateRepo.reset(); + // if (context.mounted) { + // Navigator.pop(context); + // final RoutingController routing = + // context.read(); + // routing.routeTo(PageSelector.homePage); + // } }, child: const Text("Betal"))), ), diff --git a/mobile/lib/pages/home_page.dart b/mobile/lib/pages/home_page.dart index 4e6f063..f58c057 100644 --- a/mobile/lib/pages/home_page.dart +++ b/mobile/lib/pages/home_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/user.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/pages/settings_page.dart'; -import 'package:mobile/utils/build_if_session_exists.dart'; import 'package:mobile/utils/price.dart'; import 'package:provider/provider.dart'; @@ -10,8 +9,6 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { - final userController = context.watch(); - return Column( children: [ Row( @@ -30,29 +27,33 @@ class HomePage extends StatelessWidget { ), Card( child: Container( - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Color(0xFFFFFFFF), - ), - padding: const EdgeInsets.all(10), - child: BuildIfSessionUserExists( - sessionController: userController, - placeholder: const CircularProgressIndicator(), - builder: (context, user) => Text( - "Saldo: ${formatDkkCents(user.balanceDkkCents)}", - style: Theme.of(context).textTheme.headlineSmall))), + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Colors.white, + ), + padding: const EdgeInsets.all(10), + child: Consumer( + builder: (_, provider, ___) { + final user = provider.controller.user; + return Text("Saldo: ${formatDkkCents(user.balanceDkkCents)}", + style: Theme.of(context).textTheme.headlineSmall); + }, + ), + ), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - BuildIfSessionUserExists( - sessionController: userController, - placeholder: const CircularProgressIndicator(), - builder: (context, user) => Text( - "Velkommen ${user.name}", - style: Theme.of(context).textTheme.headlineMedium, - )) + Consumer( + builder: (_, provider, ___) { + final user = provider.controller.user; + return Text( + "Velkommen ${user.name}", + style: Theme.of(context).textTheme.headlineMedium, + ); + }, + ) ], ), ), diff --git a/mobile/lib/pages/log_in_page.dart b/mobile/lib/pages/log_in_page.dart index ccb9454..d5a8c32 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/user.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/pages/register_page.dart'; import 'package:mobile/results.dart'; import 'package:mobile/widgets/error_box.dart'; @@ -7,30 +7,30 @@ import 'package:mobile/widgets/primary_button.dart'; import 'package:mobile/widgets/primary_input.dart'; import 'package:provider/provider.dart'; -class LogInPage extends StatelessWidget { - const LogInPage({super.key}); +class LoginPage extends StatelessWidget { + const LoginPage({super.key}); @override Widget build(BuildContext context) { return const Scaffold( body: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [LogInForm()])); + children: [LoginForm()])); } } -class LogInForm extends StatefulWidget { - const LogInForm({super.key}); +class LoginForm extends StatefulWidget { + const LoginForm({super.key}); @override - State createState() => LogInFormState(); + State createState() => LoginFormState(); } -class LogInFormState extends State { +class LoginFormState extends State { bool loginError = false; @override Widget build(BuildContext context) { - final userController = context.read(); + final sessionProvider = context.read(); final mailController = TextEditingController(); final passwordController = TextEditingController(); @@ -63,8 +63,8 @@ class LogInFormState extends State { ), PrimaryButton( onPressed: () async { - final loginResult = await userController.login( - mailController.text, passwordController.text); + final loginResult = await sessionProvider.controller + .loginUser(mailController.text, passwordController.text); switch (loginResult) { case Ok(): setState(() => loginError = false); diff --git a/mobile/lib/pages/settings_page.dart b/mobile/lib/pages/settings_page.dart index 68553c2..1b814c8 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/user.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/pages/settings_pages/saldo.dart'; import 'package:provider/provider.dart'; @@ -30,9 +30,11 @@ class SettingsPage extends StatelessWidget { icon: Icons.door_back_door, title: "Log ud", action: (context) async { - final sessionsController = context.read(); - Navigator.pop(context); - await sessionsController.logout(); + final sessionProvider = context.read(); + await sessionProvider.controller.logout(); + if (context.mounted) { + Navigator.pop(context); + } }), ]; diff --git a/mobile/lib/pages/settings_pages/saldo.dart b/mobile/lib/pages/settings_pages/saldo.dart index 5de59d5..88d96db 100644 --- a/mobile/lib/pages/settings_pages/saldo.dart +++ b/mobile/lib/pages/settings_pages/saldo.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/user.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/results.dart'; import 'package:mobile/utils/build_if_session_exists.dart'; import 'package:mobile/utils/price.dart'; @@ -10,7 +10,6 @@ class SaldoSettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - final sessionController = context.watch(); return Scaffold( backgroundColor: Colors.white, body: SafeArea( @@ -26,22 +25,30 @@ class SaldoSettingsPage extends StatelessWidget { ), ], ), - BuildIfSessionUserExists( - sessionController: sessionController, - placeholder: const CircularProgressIndicator(), - builder: (context, user) { - return Text( - "Nuværende saldo: ${formatDkkCents(user.balanceDkkCents)}", - style: Theme.of(context).textTheme.bodyLarge); - }), + Consumer(builder: (_, provider, ___) { + final user = provider.controller.user; + return Text( + "Nuværende saldo: ${formatDkkCents(user.balanceDkkCents)}", + style: Theme.of(context).textTheme.bodyLarge); + }), ElevatedButton.icon( onPressed: () async { - final res = await sessionController.addBalance(); - switch (res) { - case Ok(): - print("yay"); - case Err(value: final message): - print("Womp womp fejled er: $message"); + final currentUserProvider = context.read(); + final res = await currentUserProvider.controller.addBalance(); + if (res case Err(value: final message)) { + if (context.mounted) { + showDialog( + context: context, + builder: (context) => AlertDialog( + content: Text('Serverfejl: $message'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'OK'), + child: const Text('OK'), + ), + ], + )); + } } }, icon: const Icon(Icons.add), diff --git a/mobile/lib/server/backend_server.dart b/mobile/lib/server/backend_server.dart index 4082d90..b24f319 100644 --- a/mobile/lib/server/backend_server.dart +++ b/mobile/lib/server/backend_server.dart @@ -4,6 +4,7 @@ import 'package:http/http.dart' as http; import 'package:mobile/models/cart_item.dart'; import 'package:mobile/models/product.dart'; import 'package:mobile/models/user.dart'; +import 'package:mobile/results.dart'; import 'package:mobile/server/server.dart'; class BackendServer implements Server { @@ -21,24 +22,23 @@ class BackendServer implements Server { } @override - Future>> allProducts() async { + Future, String>> allProducts() async { final res = await http .get( Uri.parse("$_apiUrl/products/all"), ) .then((res) => json.decode(res.body)); if (res["ok"]) { - return Success( - data: (res["products"] as List) - .map(((product) => Product.fromJson(product))) - .toList()); + return Ok((res["products"] as List) + .map(((product) => Product.fromJson(product))) + .toList()); } else { - return Error(message: res["msg"]); + return Err(res["msg"]); } } @override - Future> register( + Future> register( String name, String email, String password, @@ -49,14 +49,14 @@ class BackendServer implements Server { ).then((res) => json.decode(res.body)); if (res["ok"]) { - return Success(data: null); + return Ok(null); } else { - return Error(message: res["msg"]); + return Err(res["msg"]); } } @override - Future> login( + Future> login( String email, String password, ) async { @@ -66,41 +66,41 @@ class BackendServer implements Server { ).then((res) => json.decode(res.body)); if (res["ok"]) { - return Success(data: res["token"]); + return Ok(res["token"]); } else { - return Error(message: res["msg"]); + return Err(res["msg"]); } } @override - Future> logout(String token) async { + Future> logout(String token) async { final res = await _post( endpoint: "sessions/logout", ).then((res) => json.decode(res.body)); if (res["ok"]) { - return Success(data: null); + return Ok(null); } else { - return Error(message: res["msg"]); + return Err(res["msg"]); } } @override - Future> sessionUser(String token) async { + Future> sessionUser(String token) async { ("sending request fr with token $token"); final res = await http.get( Uri.parse("$_apiUrl/sessions/user"), headers: {"Session-Token": token}, ).then((res) => json.decode(res.body)); if (res["ok"]) { - return Success(data: User.fromJson(res["user"])); + return Ok(User.fromJson(res["user"])); } else { - return Error(message: res["msg"]); + return Err(res["msg"]); } } @override - Future> purchaseCart( + Future> purchaseCart( String token, List cartItems) async { final res = await http.post(Uri.parse("$_apiUrl/carts/purchase"), headers: { "Content-Type": "application/json", @@ -113,14 +113,14 @@ class BackendServer implements Server { }).then((res) => json.decode(res.body)); if (res["ok"]) { - return Success(data: null); + return Ok(null); } else { - return Error(message: res["msg"]); + return Err(res["msg"]); } } @override - Future> addBalance(String token) async { + Future> addBalance(String token) async { print("$_apiUrl/api/users/balance/add"); final res = await http.post( Uri.parse("$_apiUrl/users/balance/add"), @@ -132,9 +132,9 @@ class BackendServer implements Server { ).then((res) => json.decode(res.body)); if (res["ok"]) { - return Success(data: null); + return Ok(null); } else { - return Error(message: res["msg"]); + return Err(res["msg"]); } } } diff --git a/mobile/lib/server/mock_server.dart b/mobile/lib/server/mock_server.dart index 0500231..33ad079 100644 --- a/mobile/lib/server/mock_server.dart +++ b/mobile/lib/server/mock_server.dart @@ -2,13 +2,14 @@ import 'package:mobile/models/cart_item.dart'; import 'package:mobile/models/coordinate.dart'; import 'package:mobile/models/product.dart'; import 'package:mobile/models/user.dart'; +import 'package:mobile/results.dart'; import 'package:mobile/server/server.dart'; class MockServer implements Server { @override - Future>> allProducts() async { + Future, String>> allProducts() async { var nextId = 0; - return Success(data: [ + return Ok([ Product( id: nextId++, name: "Minimælk", @@ -91,45 +92,44 @@ class MockServer implements Server { } @override - Future> register( + Future> register( String name, String email, String password, ) async { - return Success(data: null); + return Ok(null); } @override - Future> login( + Future> login( String email, String password, ) async { - return Success(data: "asdsadasdsad"); + return Ok("asdsadasdsad"); } @override - Future> logout(String token) async { - return Success(data: null); + Future> logout(String token) async { + return Ok(null); } @override - Future> sessionUser(String token) async { - return Success( - data: User( - id: 0, - email: "test@test.com", - name: "testuser", - balanceDkkCents: 10000)); + Future> sessionUser(String token) async { + return Ok(User( + id: 0, + email: "test@test.com", + name: "testuser", + balanceDkkCents: 10000)); } @override - Future> purchaseCart( + Future> purchaseCart( String token, List cartItems) async { - return Success(data: null); + return Ok(null); } @override - Future> addBalance(String token) async { - return Success(data: null); + Future> addBalance(String token) async { + return Ok(null); } } diff --git a/mobile/lib/server/server.dart b/mobile/lib/server/server.dart index c224055..cd3ba7d 100644 --- a/mobile/lib/server/server.dart +++ b/mobile/lib/server/server.dart @@ -1,37 +1,27 @@ import 'package:mobile/models/cart_item.dart'; import 'package:mobile/models/product.dart'; import 'package:mobile/models/user.dart'; +import 'package:mobile/results.dart'; abstract class Server { - Future>> allProducts(); + Future, String>> allProducts(); - Future> register( + Future> register( String name, String email, String password, ); - Future> login( + Future> login( String email, String password, ); - Future> logout(String token); + Future> logout(String token); - Future> sessionUser(String token); + Future> sessionUser(String token); - Future> purchaseCart(String token, List cartItems); + Future> purchaseCart( + String token, List cartItems); - Future> addBalance(String token); -} - -sealed class Response {} - -class Success extends Response { - Data data; - Success({required this.data}); -} - -class Error extends Response { - String message; - Error({required this.message}); + Future> addBalance(String token); } diff --git a/mobile/lib/utils/build_if_session_exists.dart b/mobile/lib/utils/build_if_session_exists.dart index ec8524f..4282fc3 100644 --- a/mobile/lib/utils/build_if_session_exists.dart +++ b/mobile/lib/utils/build_if_session_exists.dart @@ -1,28 +1,3 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/user.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/models/user.dart'; - -class BuildIfSessionUserExists extends StatelessWidget { - final UserController sessionController; - final Widget placeholder; - final Widget Function(BuildContext, User) builder; - - const BuildIfSessionUserExists( - {super.key, - required this.sessionController, - required this.placeholder, - required this.builder}); - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: sessionController.loadUserOld(), - builder: (context, snapshot) { - final user = sessionController.userOld; - if (user == null) { - return placeholder; - } - return builder(context, user); - }); - } -} diff --git a/mobile/test/widget_test.dart b/mobile/test/widget_test.dart deleted file mode 100644 index a6b7d51..0000000 --- a/mobile/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:mobile/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}