mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-27 16:24:07 +02:00
add balance
This commit is contained in:
parent
974f057dc1
commit
e7526452dd
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:mobile/models/cart_item.dart';
|
||||
import 'package:mobile/models/product.dart';
|
||||
import 'package:mobile/results.dart';
|
||||
import 'package:mobile/server/server.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class ProductIdException implements Exception {}
|
||||
@ -23,9 +24,10 @@ abstract class CartController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
class CartControllerMemory extends CartController {
|
||||
final Server server;
|
||||
final List<CartItem> cart = [];
|
||||
|
||||
CartControllerMemory();
|
||||
CartControllerMemory({required this.server});
|
||||
|
||||
@override
|
||||
List<CartItem> allCartItems() {
|
||||
@ -114,8 +116,14 @@ class CartControllerMemory extends CartController {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Result<Null, String> pay() {
|
||||
return const Err("Not implemented");
|
||||
Future<Result<Null, String>> purchase(String token) async {
|
||||
final res = await server.purchaseCart(token, cart);
|
||||
switch (res) {
|
||||
case Success<Null>():
|
||||
return const Ok(null);
|
||||
case Error<Null>(message: final message):
|
||||
return Err(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +133,7 @@ class CartControllerCache extends CartControllerMemory {
|
||||
return File("${directory.path}/cart.json").create();
|
||||
}
|
||||
|
||||
CartControllerCache() {
|
||||
CartControllerCache({required super.server}) {
|
||||
load();
|
||||
}
|
||||
|
||||
@ -137,7 +145,6 @@ class CartControllerCache extends CartControllerMemory {
|
||||
|
||||
void load() async {
|
||||
final json = await (await _cacheFile).readAsString();
|
||||
print("Loading cache: $json");
|
||||
if (json.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import 'package:mobile/server/server.dart';
|
||||
class SessionController extends ChangeNotifier {
|
||||
final Server server;
|
||||
String? _sessionToken;
|
||||
User? _user;
|
||||
|
||||
SessionController({required this.server});
|
||||
|
||||
@ -21,24 +22,53 @@ class SessionController extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Result<User, Null>> user() async {
|
||||
Future<void> _validateToken() async {
|
||||
final token = _sessionToken;
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
final res = await server.sessionUser(token);
|
||||
switch (res) {
|
||||
case Success<User>():
|
||||
return;
|
||||
case Error<User>():
|
||||
_sessionToken = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
User? get user {
|
||||
loadUser();
|
||||
return _user;
|
||||
}
|
||||
|
||||
Future<void> _notifyIfTokenChanged() async {
|
||||
final prev = _sessionToken;
|
||||
_validateToken();
|
||||
if (prev != _sessionToken) {
|
||||
notifyListeners();
|
||||
return const Err(null);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadUser() async {
|
||||
final token = _sessionToken;
|
||||
if (token == null) {
|
||||
_user = null;
|
||||
return;
|
||||
}
|
||||
final res = await server.sessionUser(token);
|
||||
switch (res) {
|
||||
case Success<User>(data: final user):
|
||||
return Ok(user);
|
||||
_user = user;
|
||||
return;
|
||||
case Error<User>():
|
||||
_sessionToken = null;
|
||||
notifyListeners();
|
||||
return const Err(null);
|
||||
_user = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String? get sessionToken {
|
||||
_notifyIfTokenChanged();
|
||||
return _sessionToken;
|
||||
}
|
||||
|
||||
@ -50,8 +80,4 @@ class SessionController extends ChangeNotifier {
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Result<int, String> pay(int userId, int amount) {
|
||||
return const Err("not implemented");
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile/controllers/session.dart';
|
||||
import 'package:mobile/results.dart';
|
||||
import 'package:mobile/server/server.dart';
|
||||
|
||||
class UsersController extends ChangeNotifier {
|
||||
Server server;
|
||||
SessionController sessionController;
|
||||
|
||||
UsersController({required this.server});
|
||||
UsersController({required this.server, required this.sessionController});
|
||||
|
||||
Future<Result<Null, String>> register(
|
||||
String name, String email, String password) async {
|
||||
@ -17,4 +19,19 @@ class UsersController extends ChangeNotifier {
|
||||
return Err(message);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Result<Null, String>> addBalance() async {
|
||||
final token = sessionController.sessionToken;
|
||||
if (token == null) {
|
||||
return const Err("No token");
|
||||
}
|
||||
final res = await server.addBalance(token);
|
||||
notifyListeners();
|
||||
switch (res) {
|
||||
case Success<Null>():
|
||||
return const Ok(null);
|
||||
case Error<Null>(message: final message):
|
||||
return Err(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,17 +26,21 @@ class MyApp extends StatelessWidget {
|
||||
final server = BackendServer();
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => SessionController(server: server)),
|
||||
ChangeNotifierProvider(create: (_) => RoutingController()),
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => ProductController(server: server)),
|
||||
ChangeNotifierProvider(create: (_) => CartControllerCache()),
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => CartControllerCache(server: server)),
|
||||
ChangeNotifierProvider(create: (_) => ReceiptController()),
|
||||
ChangeNotifierProvider(create: (_) => PayingStateController()),
|
||||
ChangeNotifierProvider(create: (_) => AddToCartStateController()),
|
||||
ChangeNotifierProvider(create: (_) => LocationImageController()),
|
||||
ChangeNotifierProvider(create: (_) => UsersController(server: server)),
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => SessionController(server: server)),
|
||||
create: (context) => UsersController(
|
||||
server: server,
|
||||
sessionController: context.read<SessionController>())),
|
||||
],
|
||||
child: MaterialApp(
|
||||
title: 'Fresh Plaza',
|
||||
|
@ -94,9 +94,23 @@ class ProductListItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class AllProductsPage extends StatelessWidget {
|
||||
class AllProductsPage extends StatefulWidget {
|
||||
const AllProductsPage({super.key});
|
||||
|
||||
@override
|
||||
State<AllProductsPage> createState() => _AllProductsPageState();
|
||||
}
|
||||
|
||||
class _AllProductsPageState extends State<AllProductsPage> {
|
||||
final seawchContwowwew = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final contwowwew = context.read<ProductController>();
|
||||
seawchContwowwew.text = contwowwew.query;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final productRepo = Provider.of<ProductController>(context);
|
||||
@ -114,6 +128,7 @@ class AllProductsPage extends StatelessWidget {
|
||||
onChanged: (query) {
|
||||
productRepo.searchProducts(query);
|
||||
},
|
||||
controller: seawchContwowwew,
|
||||
decoration: const InputDecoration(
|
||||
label: Text("Search"),
|
||||
contentPadding: EdgeInsets.only(top: 20))),
|
||||
@ -127,6 +142,7 @@ class AllProductsPage extends StatelessWidget {
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (_, idx) => ProductListItem(
|
||||
key: Key(products[idx].name),
|
||||
productId: products[idx].id,
|
||||
name: products[idx].name,
|
||||
price: products[idx].priceDkkCent,
|
||||
|
@ -8,7 +8,7 @@ import 'package:mobile/controllers/cart.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class Dashboard extends StatelessWidget {
|
||||
final List<StatelessWidget> pages = [];
|
||||
final List<Widget> pages = [];
|
||||
|
||||
Dashboard({super.key}) {
|
||||
pages.addAll([
|
||||
|
@ -3,6 +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/results.dart';
|
||||
import 'package:mobile/utils/price.dart';
|
||||
import 'package:mobile/widgets/primary_button.dart';
|
||||
@ -56,10 +57,11 @@ class FinishShoppingPage extends StatelessWidget {
|
||||
child: Center(
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
final session = context.read<SessionController>();
|
||||
payingStateRepo.next();
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
// TODO: implement paying for user
|
||||
if (cartController.pay() is Err) {
|
||||
if (cartController.purchase(session.sessionToken!)
|
||||
is Err) {
|
||||
if (context.mounted) {
|
||||
showDialog<String>(
|
||||
context: context,
|
||||
|
@ -1,5 +1,6 @@
|
||||
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';
|
||||
import 'package:mobile/utils/price.dart';
|
||||
@ -10,7 +11,8 @@ class HomePage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sessionController = context.read<SessionController>();
|
||||
final sessionController = context.watch<SessionController>();
|
||||
context.watch<UsersController>();
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
@ -34,7 +36,7 @@ class HomePage extends StatelessWidget {
|
||||
color: Color(0xFFFFFFFF),
|
||||
),
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: BuildIfSessionExists(
|
||||
child: BuildIfSessionUserExists(
|
||||
sessionController: sessionController,
|
||||
placeholder: const CircularProgressIndicator(),
|
||||
builder: (context, user) => Text(
|
||||
@ -45,7 +47,7 @@ class HomePage extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
BuildIfSessionExists(
|
||||
BuildIfSessionUserExists(
|
||||
sessionController: sessionController,
|
||||
placeholder: const CircularProgressIndicator(),
|
||||
builder: (context, user) => Text(
|
||||
|
@ -74,6 +74,7 @@ class LogInFormState extends State<LogInForm> {
|
||||
child: const Text("Log ind")),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
setState(() => loginError = false);
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => const RegisterPage()));
|
||||
},
|
||||
|
@ -88,6 +88,7 @@ class RegisterFormState extends State<RegisterForm> {
|
||||
child: const Text("Opret bruger")),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
setState(() => registerError = false);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: RichText(
|
||||
|
@ -1,5 +1,7 @@
|
||||
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';
|
||||
import 'package:mobile/utils/price.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -10,6 +12,7 @@ class SaldoSettingsPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sessionController = context.watch<SessionController>();
|
||||
final userController = context.watch<UsersController>();
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
@ -25,17 +28,23 @@ class SaldoSettingsPage extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
BuildIfSessionExists(
|
||||
sessionController: sessionController,
|
||||
placeholder: const CircularProgressIndicator(),
|
||||
builder: (context, user) => Text(
|
||||
"Nuværende saldo: ${formatDkkCents(user.balanceDkkCents)}",
|
||||
style: Theme.of(context).textTheme.bodyLarge),
|
||||
),
|
||||
BuildIfSessionUserExists(
|
||||
sessionController: sessionController,
|
||||
placeholder: const CircularProgressIndicator(),
|
||||
builder: (context, user) {
|
||||
return Text(
|
||||
"Nuværende saldo: ${formatDkkCents(user.balanceDkkCents)}",
|
||||
style: Theme.of(context).textTheme.bodyLarge);
|
||||
}),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
// TODO: implement add balance
|
||||
throw Exception("not implemented: Adding funds");
|
||||
onPressed: () async {
|
||||
final res = await userController.addBalance();
|
||||
switch (res) {
|
||||
case Ok<Null, String>():
|
||||
print("yay");
|
||||
case Err<Null, String>(value: final message):
|
||||
print("Womp womp fejled er: $message");
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text("Tilføj 100,00 kr"),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
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/server/server.dart';
|
||||
@ -10,7 +11,7 @@ class BackendServer implements Server {
|
||||
// final _apiUrl = "http://127.0.0.1:8080/api";
|
||||
|
||||
Future<http.Response> _post(
|
||||
{required String endpoint, required Map<String, dynamic> body}) async {
|
||||
{required String endpoint, Map<String, dynamic>? body}) async {
|
||||
final encoded = json.encode(body);
|
||||
return await http.post(
|
||||
Uri.parse("$_apiUrl/$endpoint"),
|
||||
@ -75,9 +76,6 @@ class BackendServer implements Server {
|
||||
Future<Response<Null>> logout(String token) async {
|
||||
final res = await _post(
|
||||
endpoint: "sessions/logout",
|
||||
body: {
|
||||
"token": token,
|
||||
},
|
||||
).then((res) => json.decode(res.body));
|
||||
|
||||
if (res["ok"]) {
|
||||
@ -102,11 +100,34 @@ class BackendServer implements Server {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response<Null>> payForCart(String token) async {
|
||||
final res = await _post(
|
||||
endpoint: "cart/pay",
|
||||
body: {
|
||||
"token": token,
|
||||
Future<Response<Null>> 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));
|
||||
|
||||
if (res["ok"]) {
|
||||
return Success(data: null);
|
||||
} else {
|
||||
return Error(message: res["msg"]);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response<Null>> addBalance(String token) async {
|
||||
print("$_apiUrl/api/users/balance/add");
|
||||
final res = await http.post(
|
||||
Uri.parse("$_apiUrl/users/balance/add"),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"Session-Token": token
|
||||
},
|
||||
).then((res) => json.decode(res.body));
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
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';
|
||||
@ -122,7 +123,13 @@ class MockServer implements Server {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response<Null>> payForCart(String token) async {
|
||||
Future<Response<Null>> purchaseCart(
|
||||
String token, List<CartItem> cartItems) async {
|
||||
return Success(data: null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response<Null>> addBalance(String token) async {
|
||||
return Success(data: null);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:mobile/models/cart_item.dart';
|
||||
import 'package:mobile/models/product.dart';
|
||||
import 'package:mobile/models/user.dart';
|
||||
|
||||
@ -18,7 +19,9 @@ abstract class Server {
|
||||
|
||||
Future<Response<User>> sessionUser(String token);
|
||||
|
||||
Future<Response<Null>> payForCart(String token);
|
||||
Future<Response<Null>> purchaseCart(String token, List<CartItem> cartItems);
|
||||
|
||||
Future<Response<Null>> addBalance(String token);
|
||||
}
|
||||
|
||||
sealed class Response<Data> {}
|
||||
|
@ -1,14 +1,13 @@
|
||||
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 {
|
||||
class BuildIfSessionUserExists extends StatelessWidget {
|
||||
final SessionController sessionController;
|
||||
final Widget placeholder;
|
||||
final Widget Function(BuildContext, User) builder;
|
||||
|
||||
const BuildIfSessionExists(
|
||||
const BuildIfSessionUserExists(
|
||||
{super.key,
|
||||
required this.sessionController,
|
||||
required this.placeholder,
|
||||
@ -17,17 +16,13 @@ class BuildIfSessionExists extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: sessionController.user(),
|
||||
future: sessionController.loadUser(),
|
||||
builder: (context, snapshot) {
|
||||
final data = snapshot.data;
|
||||
if (data == null) {
|
||||
final user = sessionController.user;
|
||||
if (user == null) {
|
||||
return placeholder;
|
||||
}
|
||||
if (data is Ok<User, Null>) {
|
||||
final user = data.value;
|
||||
return builder(context, user);
|
||||
}
|
||||
return Container();
|
||||
return builder(context, user);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user