187 lines
6.0 KiB
Dart
187 lines
6.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:qpay/http/http.dart';
|
|
import 'package:qpay/models/api_response.dart';
|
|
import 'package:qpay/screens/home/home_controller.dart' as hc;
|
|
import 'package:qpay/screens/pay/pay_controller.dart' as pc;
|
|
|
|
// Provide access to the shared types from one place.
|
|
typedef BillProvider = hc.BillProvider;
|
|
typedef BillProduct = pc.BillProduct;
|
|
typedef PaymentProcessor = pc.PaymentProcessor;
|
|
|
|
class ProviderScreenModel {
|
|
List<BillProvider> providers = [];
|
|
List<BillProduct> products = [];
|
|
List<PaymentProcessor> processors = [];
|
|
BillProvider? selectedProvider;
|
|
BillProduct? selectedProduct;
|
|
PaymentProcessor? selectedProcessor;
|
|
bool isLoadingProviders = false;
|
|
bool isLoadingProducts = false;
|
|
bool isLoadingProcessors = false;
|
|
String? errorMessage;
|
|
}
|
|
|
|
class ProviderController extends ChangeNotifier {
|
|
final ProviderScreenModel model = ProviderScreenModel();
|
|
final Http http = Http();
|
|
BuildContext context;
|
|
bool _disposed = false;
|
|
|
|
ProviderController(this.context);
|
|
|
|
@override
|
|
void dispose() {
|
|
_disposed = true;
|
|
super.dispose();
|
|
}
|
|
|
|
dynamic _unwrap(dynamic response) {
|
|
if (response is Map && response.containsKey('body')) {
|
|
return response['body'];
|
|
}
|
|
return response;
|
|
}
|
|
|
|
// ── Providers ──────────────────────────────────────────────
|
|
|
|
Future<ApiResponse<List<BillProvider>>> loadProviders(String currency) async {
|
|
model.isLoadingProviders = true;
|
|
model.errorMessage = null;
|
|
model.providers = [];
|
|
model.selectedProvider = null;
|
|
model.products = [];
|
|
model.selectedProduct = null;
|
|
if (!_disposed) notifyListeners();
|
|
|
|
try {
|
|
final raw = await http.get(
|
|
'/public/providers?currency=$currency&sort=priority,asc',
|
|
);
|
|
final response = _unwrap(raw);
|
|
final List<dynamic> content;
|
|
if (response is Map && response.containsKey('content')) {
|
|
content = response['content'] as List<dynamic>;
|
|
} else if (response is List) {
|
|
content = response;
|
|
} else {
|
|
content = [];
|
|
}
|
|
model.providers = content
|
|
.map((e) => BillProvider.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
model.isLoadingProviders = false;
|
|
if (model.providers.isNotEmpty) {
|
|
// Use selectProvider to also reset products and load matching products
|
|
selectProvider(model.providers.first);
|
|
} else {
|
|
if (!_disposed) notifyListeners();
|
|
}
|
|
return ApiResponse.success(model.providers);
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
model.providers = [];
|
|
model.selectedProvider = null;
|
|
model.isLoadingProviders = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.failure(
|
|
"Problem fetching providers, are you connected to the internet?",
|
|
);
|
|
}
|
|
}
|
|
|
|
void selectProvider(BillProvider provider) {
|
|
model.selectedProvider = provider;
|
|
model.selectedProduct = null;
|
|
model.products = [];
|
|
if (!_disposed) notifyListeners();
|
|
// Automatically load products for the selected provider
|
|
loadProducts(provider.id);
|
|
}
|
|
|
|
// ── Products ───────────────────────────────────────────────
|
|
|
|
Future<ApiResponse<List<BillProduct>>> loadProducts(String providerId) async {
|
|
model.isLoadingProducts = true;
|
|
model.errorMessage = null;
|
|
model.selectedProduct = null;
|
|
model.products = [];
|
|
if (!_disposed) notifyListeners();
|
|
|
|
try {
|
|
final raw = await http.get('/public/providers/$providerId/products');
|
|
final response = _unwrap(raw);
|
|
final List<dynamic> list = response is List
|
|
? response
|
|
: (response['content'] ?? []);
|
|
model.products = list
|
|
.map((e) => BillProduct.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
model.selectedProduct = model.products.isNotEmpty
|
|
? model.products.first
|
|
: null;
|
|
model.isLoadingProducts = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.success(model.products);
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
model.products = [];
|
|
model.selectedProduct = null;
|
|
model.isLoadingProducts = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.failure(
|
|
"Problem fetching products, are you connected to the internet?",
|
|
);
|
|
}
|
|
}
|
|
|
|
void selectProduct(BillProduct product) {
|
|
model.selectedProduct = product;
|
|
if (!_disposed) notifyListeners();
|
|
}
|
|
|
|
// ── Payment Processors ─────────────────────────────────────
|
|
|
|
Future<ApiResponse<List<PaymentProcessor>>> loadProcessors(
|
|
String currency,
|
|
) async {
|
|
model.isLoadingProcessors = true;
|
|
model.errorMessage = null;
|
|
if (!_disposed) notifyListeners();
|
|
|
|
try {
|
|
final raw = await http.get(
|
|
'/public/payment-processors?currency=$currency',
|
|
);
|
|
final response = _unwrap(raw);
|
|
final List<dynamic> list = response is List
|
|
? response
|
|
: (response['content'] ?? []);
|
|
model.processors = list
|
|
.map((e) => PaymentProcessor.fromJson(e as Map<String, dynamic>))
|
|
.toList();
|
|
if (model.processors.isNotEmpty && model.selectedProcessor == null) {
|
|
model.selectedProcessor = model.processors.first;
|
|
}
|
|
model.isLoadingProcessors = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.success(model.processors);
|
|
} catch (e) {
|
|
logger.e(e);
|
|
model.errorMessage = e.toString();
|
|
model.isLoadingProcessors = false;
|
|
if (!_disposed) notifyListeners();
|
|
return ApiResponse.failure(
|
|
"Problem fetching payment processors, are you connected to the internet?",
|
|
);
|
|
}
|
|
}
|
|
|
|
void selectProcessor(PaymentProcessor processor) {
|
|
model.selectedProcessor = processor;
|
|
if (!_disposed) notifyListeners();
|
|
}
|
|
}
|