From be00f1c965ae11db728f645caf36bcfeace2e3f1 Mon Sep 17 00:00:00 2001 From: Mikkel Troels Kongsted Date: Wed, 12 Mar 2025 15:56:04 +0100 Subject: [PATCH] implement auth - almost works --- mobile/lib/controllers/cart.dart | 5 + mobile/lib/controllers/product.dart | 5 +- mobile/lib/controllers/session.dart | 59 ++++++++ mobile/lib/controllers/user.dart | 140 ++---------------- mobile/lib/main.dart | 38 +++-- mobile/lib/models/user.dart | 35 +++++ mobile/lib/pages/cart_page.dart | 6 +- mobile/lib/pages/dashboard.dart | 11 +- mobile/lib/pages/finish_shopping_page.dart | 16 +- mobile/lib/pages/home_page.dart | 39 ++--- mobile/lib/pages/log_in_page.dart | 25 ++-- mobile/lib/pages/register_page.dart | 6 +- mobile/lib/pages/settings_page.dart | 9 +- mobile/lib/pages/settings_pages/saldo.dart | 19 ++- mobile/lib/server/backend_server.dart | 52 ++++++- mobile/lib/server/mock_server.dart | 24 ++- mobile/lib/server/server.dart | 9 +- mobile/lib/utils/build_if_session_exists.dart | 33 +++++ mobile/test/widget_test.dart | 2 +- 19 files changed, 312 insertions(+), 221 deletions(-) create mode 100644 mobile/lib/models/user.dart create mode 100644 mobile/lib/utils/build_if_session_exists.dart diff --git a/mobile/lib/controllers/cart.dart b/mobile/lib/controllers/cart.dart index 8e41574..d5c8011 100644 --- a/mobile/lib/controllers/cart.dart +++ b/mobile/lib/controllers/cart.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mobile/models/product.dart'; +import 'package:mobile/results.dart'; class ProductIdException implements Exception {} @@ -82,6 +83,10 @@ class CartController extends ChangeNotifier { cart.clear(); notifyListeners(); } + + Result pay() { + return const Err("Not implemented"); + } } class CartItem { diff --git a/mobile/lib/controllers/product.dart b/mobile/lib/controllers/product.dart index c50c1e8..a6103fb 100644 --- a/mobile/lib/controllers/product.dart +++ b/mobile/lib/controllers/product.dart @@ -1,15 +1,14 @@ import 'package:flutter/material.dart'; import 'package:mobile/models/product.dart'; import 'package:mobile/results.dart'; -import 'package:mobile/server/mock_server.dart'; import 'package:mobile/server/server.dart'; class ProductController extends ChangeNotifier { - final server = MockServer(); + final Server server; List products = []; String query = ""; - ProductController() { + ProductController({required this.server}) { fetchProductsFromServer(); } diff --git a/mobile/lib/controllers/session.dart b/mobile/lib/controllers/session.dart index e69de29..8efb018 100644 --- a/mobile/lib/controllers/session.dart +++ b/mobile/lib/controllers/session.dart @@ -0,0 +1,59 @@ +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; + + 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> user() async { + final token = _sessionToken; + if (token == null) { + notifyListeners(); + return const Err(null); + } + final res = await server.sessionUser(token); + switch (res) { + case Success(data: final user): + return Ok(user); + case Error(): + _sessionToken = null; + notifyListeners(); + return const Err(null); + } + } + + get sessionToken { + return _sessionToken; + } + + Future logout() async { + final token = _sessionToken; + if (token != null) { + server.logout(token); + _sessionToken = null; + } + print(_sessionToken); + print("notifying listeners"); + notifyListeners(); + } + + Result pay(int userId, int amount) { + return const Err("not implemented"); + } +} diff --git a/mobile/lib/controllers/user.dart b/mobile/lib/controllers/user.dart index f49f73a..caa0db9 100644 --- a/mobile/lib/controllers/user.dart +++ b/mobile/lib/controllers/user.dart @@ -1,136 +1,20 @@ import 'package:flutter/material.dart'; import 'package:mobile/results.dart'; +import 'package:mobile/server/server.dart'; -class UsersControllerOld extends ChangeNotifier { - int nextId = 0; - final List users = []; +class UsersController extends ChangeNotifier { + Server server; - User? _loggedInUser; + UsersController({required this.server}); - UsersControllerOld() { - addTestUsers(); - } - - Result getUserById(int id) { - for (var i = 0; i < users.length; i++) { - if (users[i].id == id) { - return Ok(users[i]); - } + 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); } - return Err("User with id $id doesn't exist"); - } - - Result getUserByMail(String mail) { - for (var i = 0; i < users.length; i++) { - if (users[i].mail == mail) { - return Ok(users[i]); - } - } - return Err("User with mail $mail doesn't exist"); - } - - Result addUser(String name, String mail, String password) { - if (getUserByMail(mail) is Ok) { - return Err("User with mail $mail already exists"); - } - - final user = User( - id: nextId++, - name: name, - mail: mail, - password: password, - balanceInDkkCents: 0); - users.add(user); - - return Ok(user); - } - - Result login(String mail, String password) { - User? user; - for (var i = 0; i < users.length; i++) { - if (users[i].mail == mail) { - user = users[i]; - } - } - if (user == null) { - return Err("User with mail $mail doesn't exist"); - } - if (user.password != password) { - return Err("Wrong password for user with mail $mail"); - } - _loggedInUser = user; - notifyListeners(); - return Ok(user); - } - - void logout() { - _loggedInUser = null; - notifyListeners(); - } - - User? loggedInUser() { - return _loggedInUser; - } - - Result pay(int userId, int amount) { - final user = getUserById(userId); - if (user is Ok) { - return (user as User).pay(amount); - } - return Err("User with id $userId doesn't exist"); - } - - void addTestUsers() { - users - ..add(User( - id: nextId++, - mail: "test@test.com", - name: "test", - password: "test", - balanceInDkkCents: 10000)) - ..add(User( - id: nextId++, - mail: "", - name: "", - password: "", - balanceInDkkCents: 100000)); - } - - void veryBadNotifyAll() { - // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️ - // TODO: THIS SHOULD BE FIXED - // FIXME: DO SOMETHING ELSE PLEASE!!!!! - // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️ - notifyListeners(); - } -} - -class User { - final int id; - final String mail; - final String name; - final String password; - - // balance is in øre - int balanceInDkkCents; - - User({ - required this.id, - required this.mail, - required this.name, - required this.password, - required this.balanceInDkkCents, - }); - - void addBalanceFounds(int amount) { - balanceInDkkCents += amount; - } - - Result pay(int amount) { - if (balanceInDkkCents < amount) { - return Err("User can not afford paying amount $amount"); - } - balanceInDkkCents -= amount; - return Ok(balanceInDkkCents); } } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index efbf729..c270aba 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:mobile/controllers/session.dart'; +import 'package:mobile/models/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'; import 'package:mobile/controllers/cart.dart'; @@ -8,6 +11,7 @@ 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/server/mock_server.dart'; import 'package:provider/provider.dart'; import 'package:mobile/controllers/routing.dart'; @@ -20,29 +24,37 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + final server = MockServer(); return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => RoutingController()), - ChangeNotifierProvider(create: (_) => ProductController()), + ChangeNotifierProvider( + create: (_) => ProductController(server: server)), ChangeNotifierProvider(create: (_) => CartController()), ChangeNotifierProvider(create: (_) => ReceiptController()), ChangeNotifierProvider(create: (_) => PayingStateController()), ChangeNotifierProvider(create: (_) => AddToCartStateController()), ChangeNotifierProvider(create: (_) => LocationImageController()), - ChangeNotifierProvider(create: (_) => UsersControllerOld()), + ChangeNotifierProvider(create: (_) => UsersController(server: server)), + ChangeNotifierProvider( + create: (_) => SessionController(server: server)), ], child: MaterialApp( - title: 'Fresh Plaza', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed( - seedColor: const Color.fromARGB(255, 149, 92, 255)), - scaffoldBackgroundColor: const Color(0xFFFAFAFF), - textTheme: - GoogleFonts.merriweatherTextTheme(Theme.of(context).textTheme), - useMaterial3: true, - ), - home: const LogInPage(), - ), + title: 'Fresh Plaza', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: const Color.fromARGB(255, 149, 92, 255)), + scaffoldBackgroundColor: const Color(0xFFFAFAFF), + textTheme: + GoogleFonts.merriweatherTextTheme(Theme.of(context).textTheme), + useMaterial3: true, + ), + home: Consumer( + builder: (_, sessionController, __) { + if (sessionController.sessionToken is String) return Dashboard(); + return const LogInPage(); + }, + )), ); } } diff --git a/mobile/lib/models/user.dart b/mobile/lib/models/user.dart new file mode 100644 index 0000000..3ac99cc --- /dev/null +++ b/mobile/lib/models/user.dart @@ -0,0 +1,35 @@ +import 'package:mobile/results.dart'; + +class User { + final int id; + final String email; + final String name; + + // balance is in øre + int balanceInDkkCents; + + User({ + required this.id, + required this.email, + required this.name, + required this.balanceInDkkCents, + }); + + User.fromJson(Map json) + : id = json["id"], + email = json["email"], + name = json["name"], + balanceInDkkCents = json["balanceInDkkCents"]; + + void addBalanceFounds(int amount) { + balanceInDkkCents += amount; + } + + Result pay(int amount) { + if (balanceInDkkCents < amount) { + return Err("User can not afford paying amount $amount"); + } + balanceInDkkCents -= amount; + return Ok(balanceInDkkCents); + } +} diff --git a/mobile/lib/pages/cart_page.dart b/mobile/lib/pages/cart_page.dart index 9f4a599..0a3af96 100644 --- a/mobile/lib/pages/cart_page.dart +++ b/mobile/lib/pages/cart_page.dart @@ -6,7 +6,6 @@ import 'package:mobile/models/product.dart'; import 'package:mobile/pages/finish_shopping_page.dart'; import 'package:mobile/controllers/cart.dart'; import 'package:mobile/controllers/product.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'; @@ -144,8 +143,7 @@ class CartItemView extends StatelessWidget { } class CartPage extends StatelessWidget { - final User user; - const CartPage({super.key, required this.user}); + const CartPage({super.key}); @override Widget build(BuildContext context) { @@ -316,7 +314,7 @@ class CartPage extends StatelessWidget { context, MaterialPageRoute( builder: (context) => - FinishShoppingPage(user: user))); + const FinishShoppingPage())); }, child: const Text("Afslut indkøb")), ), diff --git a/mobile/lib/pages/dashboard.dart b/mobile/lib/pages/dashboard.dart index dded58e..9ff2a2f 100644 --- a/mobile/lib/pages/dashboard.dart +++ b/mobile/lib/pages/dashboard.dart @@ -5,21 +5,16 @@ import 'package:mobile/pages/all_receipts_page.dart'; import 'package:mobile/pages/home_page.dart'; import 'package:mobile/controllers/routing.dart'; import 'package:mobile/controllers/cart.dart'; -import 'package:mobile/controllers/user.dart'; import 'package:provider/provider.dart'; class Dashboard extends StatelessWidget { - final User user; - final List pages = []; - Dashboard({super.key, required this.user}) { + Dashboard({super.key}) { pages.addAll([ - HomePage( - user: user, - ), + const HomePage(), const AllProductsPage(), - CartPage(user: user), + const CartPage(), const AllReceiptsPage(), ]); } diff --git a/mobile/lib/pages/finish_shopping_page.dart b/mobile/lib/pages/finish_shopping_page.dart index 16793ea..7449e60 100644 --- a/mobile/lib/pages/finish_shopping_page.dart +++ b/mobile/lib/pages/finish_shopping_page.dart @@ -3,7 +3,6 @@ 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/results.dart'; import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/primary_button.dart'; @@ -11,17 +10,15 @@ import 'package:mobile/widgets/receipt_item.dart'; import 'package:provider/provider.dart'; class FinishShoppingPage extends StatelessWidget { - final User user; - - const FinishShoppingPage({super.key, required this.user}); + const FinishShoppingPage({super.key}); @override Widget build(BuildContext context) { - final CartController cartRepo = context.read(); + final CartController cartController = context.read(); final ReceiptController receiptRepo = context.read(); final PayingStateController payingStateRepo = context.watch(); - final cart = cartRepo.allCartItems(); + final cart = cartController.allCartItems(); return Scaffold( body: SafeArea( @@ -50,7 +47,7 @@ class FinishShoppingPage extends StatelessWidget { "Total:", style: TextStyle(fontWeight: FontWeight.bold), ), - Text(formatDkkCents(cartRepo.totalPrice())), + Text(formatDkkCents(cartController.totalPrice())), ], ), ), @@ -60,7 +57,8 @@ class FinishShoppingPage extends StatelessWidget { onPressed: () async { payingStateRepo.next(); await Future.delayed(const Duration(seconds: 1)); - if (user.pay(cartRepo.totalPrice()) is Err) { + // TODO: implement paying for user + if (cartController.pay() is Err) { if (context.mounted) { showDialog( context: context, @@ -84,7 +82,7 @@ class FinishShoppingPage extends StatelessWidget { receiptRepo.createReceipt(cart); payingStateRepo.next(); await Future.delayed(const Duration(seconds: 1)); - cartRepo.clearCart(); + cartController.clearCart(); payingStateRepo.reset(); if (context.mounted) { Navigator.pop(context); diff --git a/mobile/lib/pages/home_page.dart b/mobile/lib/pages/home_page.dart index b5b2c96..5d686b0 100644 --- a/mobile/lib/pages/home_page.dart +++ b/mobile/lib/pages/home_page.dart @@ -1,15 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/pages/settings_page.dart'; -import 'package:mobile/controllers/user.dart'; +import 'package:mobile/utils/build_if_session_exists.dart'; import 'package:mobile/utils/price.dart'; import 'package:provider/provider.dart'; class HomePage extends StatelessWidget { - final User user; - const HomePage({super.key, required this.user}); + const HomePage({super.key}); @override Widget build(BuildContext context) { + final sessionController = context.read(); return Column( children: [ Row( @@ -28,25 +29,29 @@ 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: Consumer( - builder: (context, usersRepo, _) => Text( - "Saldo: ${formatDkkCents(user.balanceInDkkCents)}", - style: Theme.of(context).textTheme.headlineSmall)), - ), + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Color(0xFFFFFFFF), + ), + padding: const EdgeInsets.all(10), + child: BuildIfSessionExists( + sessionController: sessionController, + placeholder: const CircularProgressIndicator(), + builder: (context, user) => Text( + "Saldo: ${formatDkkCents(user.balanceInDkkCents)}", + style: Theme.of(context).textTheme.headlineSmall))), ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - "Velkommen ${user.name}", - style: Theme.of(context).textTheme.headlineMedium, - ), + BuildIfSessionExists( + sessionController: sessionController, + placeholder: const CircularProgressIndicator(), + builder: (context, user) => 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 2fac9bb..8867846 100644 --- a/mobile/lib/pages/log_in_page.dart +++ b/mobile/lib/pages/log_in_page.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/pages/register_page.dart'; -import 'package:mobile/controllers/user.dart'; import 'package:mobile/results.dart'; import 'package:mobile/widgets/error_box.dart'; import 'package:mobile/widgets/primary_button.dart'; import 'package:mobile/widgets/primary_input.dart'; import 'package:provider/provider.dart'; -import 'dashboard.dart'; class LogInPage extends StatelessWidget { const LogInPage({super.key}); @@ -61,19 +60,15 @@ class LogInFormState extends State { controller: passwordController, ), PrimaryButton( - onPressed: () { - final usersRepo = context.read(); - final loginResult = - usersRepo.login(mailController.text, passwordController.text); - - if (loginResult is Ok) { - setState(() => loginError = false); - Navigator.of(context).popUntil((_) => false); - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => - Dashboard(user: (loginResult as Ok).value))); - } else { - setState(() => loginError = true); + onPressed: () async { + final sessionController = context.read(); + final loginResult = await sessionController.login( + mailController.text, passwordController.text); + switch (loginResult) { + case Ok(): + setState(() => loginError = false); + case Err(): + setState(() => loginError = true); } }, child: const Text("Log ind")), diff --git a/mobile/lib/pages/register_page.dart b/mobile/lib/pages/register_page.dart index 27d5ff6..e2750cf 100644 --- a/mobile/lib/pages/register_page.dart +++ b/mobile/lib/pages/register_page.dart @@ -71,9 +71,9 @@ class RegisterFormState extends State { obscure: true), PrimaryButton( onPressed: () { - final usersRepo = context.read(); - if (usersRepo.addUser(nameController.text, mailController.text, - passwordController.text) is Ok) { + final sessionsRepo = context.read(); + if (sessionsRepo.register(nameController.text, + mailController.text, passwordController.text) is Ok) { setState(() => registerError = false); Navigator.of(context).pop(); } else { diff --git a/mobile/lib/pages/settings_page.dart b/mobile/lib/pages/settings_page.dart index b0cdd6e..4fb3adf 100644 --- a/mobile/lib/pages/settings_page.dart +++ b/mobile/lib/pages/settings_page.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:mobile/pages/log_in_page.dart'; +import 'package:mobile/controllers/session.dart'; import 'package:mobile/pages/settings_pages/saldo.dart'; -import 'package:mobile/controllers/user.dart'; import 'package:provider/provider.dart'; class _Page { @@ -31,11 +30,9 @@ class SettingsPage extends StatelessWidget { icon: Icons.door_back_door, title: "Log ud", action: (context) { - final users = context.read(); - users.logout(); + final sessionsController = context.read(); Navigator.popUntil(context, (_) => false); - Navigator.of(context) - .push(MaterialPageRoute(builder: (context) => const LogInPage())); + sessionsController.logout(); }), ]; diff --git a/mobile/lib/pages/settings_pages/saldo.dart b/mobile/lib/pages/settings_pages/saldo.dart index 5704514..26d09d7 100644 --- a/mobile/lib/pages/settings_pages/saldo.dart +++ b/mobile/lib/pages/settings_pages/saldo.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:mobile/controllers/user.dart'; +import 'package:mobile/controllers/session.dart'; +import 'package:mobile/utils/build_if_session_exists.dart'; import 'package:mobile/utils/price.dart'; import 'package:provider/provider.dart'; @@ -8,8 +9,7 @@ class SaldoSettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - final usersRepo = context.watch(); - final user = usersRepo.loggedInUser()!; + final sessionController = context.watch(); return Scaffold( backgroundColor: Colors.white, body: SafeArea( @@ -25,12 +25,17 @@ class SaldoSettingsPage extends StatelessWidget { ), ], ), - Text("Nuværende saldo: ${formatDkkCents(user.balanceInDkkCents)}", - style: Theme.of(context).textTheme.bodyLarge), + BuildIfSessionExists( + sessionController: sessionController, + placeholder: const CircularProgressIndicator(), + builder: (context, user) => Text( + "Nuværende saldo: ${formatDkkCents(user.balanceInDkkCents)}", + style: Theme.of(context).textTheme.bodyLarge), + ), ElevatedButton.icon( onPressed: () { - user.addBalanceFounds(10000); - usersRepo.veryBadNotifyAll(); + // TODO: implement add balance + throw Exception("not implemented: Adding funds"); }, icon: const Icon(Icons.add), label: const Text("Tilføj 100,00 kr"), diff --git a/mobile/lib/server/backend_server.dart b/mobile/lib/server/backend_server.dart index 6ade9eb..a221129 100644 --- a/mobile/lib/server/backend_server.dart +++ b/mobile/lib/server/backend_server.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:mobile/models/product.dart'; +import 'package:mobile/models/user.dart'; import 'package:mobile/server/server.dart'; class BackendServer implements Server { @@ -51,16 +52,61 @@ class BackendServer implements Server { } @override - Future> login( - String name, + Future> login( String email, String password, ) async { final res = await _post( - endpoint: "auth/login", + endpoint: "sessions/login", body: {"email": email, "password": password}, ).then((res) => json.decode(res.body)); + if (res["ok"]) { + return Success(data: res["token"]); + } else { + return Error(message: res["message"]); + } + } + + @override + Future> logout(String token) async { + final res = await _post( + endpoint: "sessions/logout", + body: { + "token": token, + }, + ).then((res) => json.decode(res.body)); + + if (res["ok"]) { + return Success(data: null); + } else { + return Error(message: res["message"]); + } + } + + @override + Future> sessionUser(String token) async { + final res = await http + .get( + Uri.parse("$_apiUrl/sessions/user/$token"), + ) + .then((res) => json.decode(res.body)); + if (res["ok"]) { + return Error(message: res["message"]); + } else { + return Success(data: User.fromJson(res)); + } + } + + @override + Future> payForCart(String token) async { + final res = await _post( + endpoint: "cart/pay", + body: { + "token": token, + }, + ).then((res) => json.decode(res.body)); + if (res["ok"]) { return Success(data: null); } else { diff --git a/mobile/lib/server/mock_server.dart b/mobile/lib/server/mock_server.dart index 50359ec..127b7e8 100644 --- a/mobile/lib/server/mock_server.dart +++ b/mobile/lib/server/mock_server.dart @@ -1,5 +1,6 @@ import 'package:mobile/models/coordinate.dart'; import 'package:mobile/models/product.dart'; +import 'package:mobile/models/user.dart'; import 'package:mobile/server/server.dart'; class MockServer implements Server { @@ -108,11 +109,30 @@ class MockServer implements Server { } @override - Future> login( - String name, + Future> login( String email, String password, ) async { + return Success(data: "asdsadasdsad"); + } + + @override + Future> logout(String token) async { + return Success(data: null); + } + + @override + Future> sessionUser(String token) async { + return Success( + data: User( + id: 0, + email: "test@test.com", + name: "testuser", + balanceInDkkCents: 10000)); + } + + @override + Future> payForCart(String token) async { return Success(data: null); } } diff --git a/mobile/lib/server/server.dart b/mobile/lib/server/server.dart index 42e6852..f888d65 100644 --- a/mobile/lib/server/server.dart +++ b/mobile/lib/server/server.dart @@ -1,4 +1,5 @@ import 'package:mobile/models/product.dart'; +import 'package:mobile/models/user.dart'; abstract class Server { Future>> allProducts(); @@ -9,11 +10,15 @@ abstract class Server { String password, ); - Future> login( - String name, + Future> login( String email, String password, ); + Future> logout(String token); + + Future> sessionUser(String token); + + Future> payForCart(String token); } sealed class Response {} diff --git a/mobile/lib/utils/build_if_session_exists.dart b/mobile/lib/utils/build_if_session_exists.dart new file mode 100644 index 0000000..7705936 --- /dev/null +++ b/mobile/lib/utils/build_if_session_exists.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/controllers/session.dart'; +import 'package:mobile/models/user.dart'; +import 'package:mobile/results.dart'; + +class BuildIfSessionExists extends StatelessWidget { + final SessionController sessionController; + final Widget placeholder; + final Widget Function(BuildContext, User) builder; + + const BuildIfSessionExists( + {super.key, + required this.sessionController, + required this.placeholder, + required this.builder}); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: sessionController.user(), + builder: (context, snapshot) { + final data = snapshot.data; + if (data == null) { + return placeholder; + } + if (data is Ok) { + final user = data.value; + return builder(context, user); + } + return Container(); + }); + } +} diff --git a/mobile/test/widget_test.dart b/mobile/test/widget_test.dart index 7258710..a6b7d51 100644 --- a/mobile/test/widget_test.dart +++ b/mobile/test/widget_test.dart @@ -13,7 +13,7 @@ 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(MyApp()); + await tester.pumpWidget(const MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);