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 'package:flutter/material.dart';
import 'package:mobile/controllers/session.dart';
import 'package:mobile/models/cart_item.dart';
import 'package:mobile/models/product.dart';
import 'package:mobile/results.dart';
@ -27,7 +28,9 @@ class CartControllerMemory extends CartController {
final Server server;
final List<CartItem> cart = [];
CartControllerMemory({required this.server});
final SessionController sessionController;
CartControllerMemory({required this.server, required this.sessionController});
@override
List<CartItem> allCartItems() {
@ -116,10 +119,12 @@ class CartControllerMemory extends CartController {
notifyListeners();
}
Future<Result<Null, String>> purchase(String token) async {
final res = await server.purchaseCart(token, cart);
Future<Result<Null, String>> purchase() async {
final res = await sessionController.requestWithSession(
(server, sessionToken) => server.purchaseCart(sessionToken, cart));
switch (res) {
case Ok<Null, String>():
await sessionController.loadUpdatedUser();
return const Ok(null);
case Err<Null, String>(value: final message):
return Err(message);
@ -133,7 +138,8 @@ class CartControllerCache extends CartControllerMemory {
return File("${directory.path}/cart.json").create();
}
CartControllerCache({required super.server}) {
CartControllerCache(
{required super.server, required super.sessionController}) {
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
return _loadCurrentUser();
}
Future<Result<Null, Null>> loadUpdatedUser() async {
return _loadCurrentUser();
}
Future<Result<Null, Null>> _loadCurrentUser() async {
final sessionUserResult = await _requestWithSession<User>(
final sessionUserResult = await requestWithSession<User>(
(server, sessionToken) => server.sessionUser(sessionToken));
switch (sessionUserResult) {
case Ok<User, String>(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<User, String>():
return const Err(null);
@ -73,7 +71,7 @@ class SessionController {
}
Future<Result<Null, String>> addBalance() async {
final addBalanceResult = await _requestWithSession(
final addBalanceResult = await requestWithSession(
(server, sessionToken) => server.addBalance(sessionToken));
if (addBalanceResult case Err<Null, String>(value: final message)) {
return Err(message);
@ -85,7 +83,7 @@ class SessionController {
}
/// Package private.
Future<Result<T, String>> _requestWithSession<T>(
Future<Result<T, String>> requestWithSession<T>(
Future<Result<T, String>> Function(Server server, String sessionToken)
func) async {
final sessionToken = _sessionToken;
@ -105,29 +103,25 @@ class SessionController {
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();
}
}
void _addSessionChangeListener(_ChangeListener listener) {
_sessionChangeListeners.add(listener);
}
void _addUserChangeListener(_ChangeListener listener) {
_userChangeListeners.add(listener);
}
}
abstract class _ChangeListener {

View File

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

View File

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

View File

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