use providers on finish shopping page

This commit is contained in:
SFJ 2025-03-18 14:09:01 +01:00
parent 9603651e05
commit fcaad4f876
6 changed files with 82 additions and 77 deletions

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mobile/controllers/session.dart';
import 'package:mobile/models/cart_item.dart'; import 'package:mobile/models/cart_item.dart';
import 'package:mobile/models/product.dart'; import 'package:mobile/models/product.dart';
import 'package:mobile/results.dart'; import 'package:mobile/results.dart';
@ -27,7 +28,9 @@ class CartControllerMemory extends CartController {
final Server server; final Server server;
final List<CartItem> cart = []; final List<CartItem> cart = [];
CartControllerMemory({required this.server}); final SessionController sessionController;
CartControllerMemory({required this.server, required this.sessionController});
@override @override
List<CartItem> allCartItems() { List<CartItem> allCartItems() {
@ -116,10 +119,12 @@ class CartControllerMemory extends CartController {
notifyListeners(); notifyListeners();
} }
Future<Result<Null, String>> purchase(String token) async { Future<Result<Null, String>> purchase() async {
final res = await server.purchaseCart(token, cart); final res = await sessionController.requestWithSession(
(server, sessionToken) => server.purchaseCart(sessionToken, cart));
switch (res) { switch (res) {
case Ok<Null, String>(): case Ok<Null, String>():
await sessionController.loadUpdatedUser();
return const Ok(null); return const Ok(null);
case Err<Null, String>(value: final message): case Err<Null, String>(value: final message):
return Err(message); return Err(message);
@ -133,7 +138,8 @@ class CartControllerCache extends CartControllerMemory {
return File("${directory.path}/cart.json").create(); return File("${directory.path}/cart.json").create();
} }
CartControllerCache({required super.server}) { CartControllerCache(
{required super.server, required super.sessionController}) {
load(); load();
} }

View File

@ -26,24 +26,22 @@ class SessionController {
} }
} }
Future<Result<Null, Null>> loadUser() async { Future<Result<Null, Null>> loadCachedUser() async {
// TODO: retrieve session from cache, if exists // TODO: retrieve session from cache, if exists
return _loadCurrentUser(); return _loadCurrentUser();
} }
Future<Result<Null, Null>> loadUpdatedUser() async {
return _loadCurrentUser();
}
Future<Result<Null, Null>> _loadCurrentUser() async { Future<Result<Null, Null>> _loadCurrentUser() async {
final sessionUserResult = await _requestWithSession<User>( final sessionUserResult = await requestWithSession<User>(
(server, sessionToken) => server.sessionUser(sessionToken)); (server, sessionToken) => server.sessionUser(sessionToken));
switch (sessionUserResult) { switch (sessionUserResult) {
case Ok<User, String>(value: final sessionUser): case Ok<User, String>(value: final sessionUser):
_sessionUser = sessionUser; _sessionUser = sessionUser;
notifyUserChangeListeners(); 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); return const Ok(null);
case Err<User, String>(): case Err<User, String>():
return const Err(null); return const Err(null);
@ -73,7 +71,7 @@ class SessionController {
} }
Future<Result<Null, String>> addBalance() async { Future<Result<Null, String>> addBalance() async {
final addBalanceResult = await _requestWithSession( final addBalanceResult = await requestWithSession(
(server, sessionToken) => server.addBalance(sessionToken)); (server, sessionToken) => server.addBalance(sessionToken));
if (addBalanceResult case Err<Null, String>(value: final message)) { if (addBalanceResult case Err<Null, String>(value: final message)) {
return Err(message); return Err(message);
@ -85,7 +83,7 @@ class SessionController {
} }
/// Package private. /// Package private.
Future<Result<T, String>> _requestWithSession<T>( Future<Result<T, String>> requestWithSession<T>(
Future<Result<T, String>> Function(Server server, String sessionToken) Future<Result<T, String>> Function(Server server, String sessionToken)
func) async { func) async {
final sessionToken = _sessionToken; final sessionToken = _sessionToken;
@ -105,29 +103,25 @@ class SessionController {
return result; return result;
} }
/// Package private.
void _addSessionChangeListener(_ChangeListener listener) {
_sessionChangeListeners.add(listener);
}
/// Package private.
void _addUserChangeListener(_ChangeListener listener) {
_userChangeListeners.add(listener);
}
/// Class private.
void notifySessionChangeListeners() { void notifySessionChangeListeners() {
for (final listener in _sessionChangeListeners) { for (final listener in _sessionChangeListeners) {
listener.notify(); listener.notify();
} }
} }
/// Class private.
void notifyUserChangeListeners() { void notifyUserChangeListeners() {
for (final listener in _userChangeListeners) { for (final listener in _userChangeListeners) {
listener.notify(); listener.notify();
} }
} }
void _addSessionChangeListener(_ChangeListener listener) {
_sessionChangeListeners.add(listener);
}
void _addUserChangeListener(_ChangeListener listener) {
_userChangeListeners.add(listener);
}
} }
abstract class _ChangeListener { abstract class _ChangeListener {

View File

@ -21,7 +21,7 @@ void main() {
final usersController = UsersController(server: server); final usersController = UsersController(server: server);
final sessionController = SessionController(server: server); final sessionController = SessionController(server: server);
sessionController.loadUser(); sessionController.loadCachedUser();
runApp(MyApp( runApp(MyApp(
usersController: usersController, usersController: usersController,
@ -54,7 +54,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => ProductController(server: server)), create: (_) => ProductController(server: server)),
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => CartControllerCache(server: server)), create: (_) => CartControllerCache(
server: server, sessionController: sessionController)),
ChangeNotifierProvider(create: (_) => ReceiptController()), ChangeNotifierProvider(create: (_) => ReceiptController()),
ChangeNotifierProvider(create: (_) => PayingStateController()), ChangeNotifierProvider(create: (_) => PayingStateController()),
ChangeNotifierProvider(create: (_) => AddToCartStateController()), ChangeNotifierProvider(create: (_) => AddToCartStateController()),
@ -71,13 +72,13 @@ class MyApp extends StatelessWidget {
GoogleFonts.merriweatherTextTheme(Theme.of(context).textTheme), GoogleFonts.merriweatherTextTheme(Theme.of(context).textTheme),
useMaterial3: true, useMaterial3: true,
), ),
home: Consumer<SessionProvider>( home: Consumer2<SessionProvider, CurrentUserProvider>(
builder: (_, provider, ___) { builder: (_, provider1, provider2, ___) {
if (provider.controller.hasUser) { if (provider1.controller.hasUser) {
return Dashboard(); return Dashboard();
} }
return FutureBuilder( return FutureBuilder(
future: provider.controller.loadUser(), future: provider1.controller.loadCachedUser(),
builder: (_, snapshot) { builder: (_, snapshot) {
final error = snapshot.error; final error = snapshot.error;
if (error != null) { if (error != null) {

View File

@ -57,42 +57,40 @@ class FinishShoppingPage extends StatelessWidget {
child: Center( child: Center(
child: PrimaryButton( child: PrimaryButton(
onPressed: () async { onPressed: () async {
// final session = context.read<SessionController>(); payingStateRepo.next();
// payingStateRepo.next(); await Future.delayed(const Duration(seconds: 1));
// await Future.delayed(const Duration(seconds: 1)); if (cartController.purchase() is Err) {
// if (cartController.purchase(session.sessionToken!) if (context.mounted) {
// is Err) { showDialog<String>(
// if (context.mounted) { context: context,
// showDialog<String>( builder: (BuildContext context) =>
// context: context, AlertDialog(
// builder: (BuildContext context) => content: const Text(
// AlertDialog( 'Du har desværre ikke råd til at købe dette'),
// content: const Text( actions: <Widget>[
// 'Du har desværre ikke råd til at købe dette'), TextButton(
// actions: <Widget>[ onPressed: () =>
// TextButton( Navigator.pop(context, 'OK'),
// onPressed: () => child: const Text('OK'),
// Navigator.pop(context, 'OK'), ),
// child: const Text('OK'), ],
// ), ),
// ], );
// ), }
// ); payingStateRepo.reset();
// } return;
// payingStateRepo.reset(); }
// return; receiptRepo.createReceipt(cart);
// } payingStateRepo.next();
// receiptRepo.createReceipt(cart); await Future.delayed(const Duration(seconds: 1));
// payingStateRepo.next(); cartController.clearCart();
// await Future.delayed(const Duration(seconds: 1)); payingStateRepo.reset();
// cartController.clearCart(); if (context.mounted) {
// payingStateRepo.reset(); Navigator.pop(context);
// if (context.mounted) { final RoutingController routing =
// Navigator.pop(context); context.read<RoutingController>();
// final RoutingController routing = routing.routeTo(PageSelector.homePage);
// context.read<RoutingController>(); }
// routing.routeTo(PageSelector.homePage);
// }
}, },
child: const Text("Betal"))), child: const Text("Betal"))),
), ),

View File

@ -102,15 +102,21 @@ class BackendServer implements Server {
@override @override
Future<Result<Null, String>> purchaseCart( Future<Result<Null, String>> purchaseCart(
String token, List<CartItem> cartItems) async { String token, List<CartItem> cartItems) async {
final res = await http.post(Uri.parse("$_apiUrl/carts/purchase"), headers: { final res = await http
"Content-Type": "application/json", .post(Uri.parse("$_apiUrl/carts/purchase"),
"Session-Token": token headers: {
}, body: { "Content-Type": "application/json",
"cart_items": cartItems "Session-Token": token
.map((cartItem) => },
{"product_id": cartItem.product.id, "amount": cartItem.amount}) body: json.encode({
.toList() "cart_items": cartItems
}).then((res) => json.decode(res.body)); .map((cartItem) => {
"product_id": cartItem.product.id,
"amount": cartItem.amount
})
.toList()
}))
.then((res) => json.decode(res.body));
if (res["ok"]) { if (res["ok"]) {
return Ok(null); return Ok(null);