import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../data/repository.dart'; import '../data/models.dart'; final apiRepositoryProvider = Provider((ref) => ApiRepository()); class CategoryNotifier extends AsyncNotifier> { @override FutureOr> build() { return ref.watch(apiRepositoryProvider).getCategories(); } Future addCategory(String name, String? iconName) async { final newCategory = await ref.read(apiRepositoryProvider).addCategory(Category(id: 0, name: name, iconName: iconName)); if (state.value != null) { state = AsyncValue.data([...state.value!, newCategory]); } } } final categoryProvider = AsyncNotifierProvider>(() => CategoryNotifier()); class TransactionNotifier extends AsyncNotifier> { @override FutureOr> build() { return ref.watch(apiRepositoryProvider).getTransactions(); } Future addTransaction(Transaction transaction, {List>? base64Attachments}) async { final newTransaction = await ref.read(apiRepositoryProvider).addTransaction(transaction); // Upload attachments if any if (base64Attachments != null && base64Attachments.isNotEmpty) { for (var attachment in base64Attachments) { await ref.read(apiRepositoryProvider).addAttachment( newTransaction.id, attachment['fileName']!, attachment['contentType']!, attachment['base64Content']! ); } } if (state.value != null) { // Re-fetch transactions to get the fully populated transaction with attachments from backend ref.invalidateSelf(); } } Future updateTransaction(Transaction transaction, {List>? base64Attachments, List? deletedAttachmentIds}) async { final updatedTransaction = await ref.read(apiRepositoryProvider).updateTransaction(transaction); if (base64Attachments != null && base64Attachments.isNotEmpty) { for (var attachment in base64Attachments) { await ref.read(apiRepositoryProvider).addAttachment( updatedTransaction.id, attachment['fileName']!, attachment['contentType']!, attachment['base64Content']! ); } } if (deletedAttachmentIds != null && deletedAttachmentIds.isNotEmpty) { for (var id in deletedAttachmentIds) { await ref.read(apiRepositoryProvider).deleteAttachment(id); } } ref.invalidate(walletProvider); ref.invalidateSelf(); } Future deleteTransaction(int id) async { await ref.read(apiRepositoryProvider).deleteTransaction(id); ref.invalidate(walletProvider); if (state.value != null) { state = AsyncValue.data(state.value!.where((t) => t.id != id).toList()); } } Future closeInvestment(int id, double maturityAmount, DateTime closingDate, int toWalletId) async { final updatedTransaction = await ref.read(apiRepositoryProvider).closeInvestment(id, maturityAmount, closingDate, toWalletId); if (state.value != null) { final updatedList = state.value!.map((t) => t.id == updatedTransaction.id ? updatedTransaction : t).toList(); state = AsyncValue.data(updatedList); } } } final transactionProvider = AsyncNotifierProvider>(() => TransactionNotifier()); class BudgetNotifier extends AsyncNotifier> { @override FutureOr> build() { return ref.watch(apiRepositoryProvider).getBudgets(); } Future addOrUpdateBudget(Budget budget) async { final updatedBudget = await ref.read(apiRepositoryProvider).addOrUpdateBudget(budget); if (state.value != null) { final list = List.from(state.value!); final index = list.indexWhere((b) => (b.categoryId != null && b.categoryId == budget.categoryId) || (b.walletId != null && b.walletId == budget.walletId) ); if (index >= 0) { list[index] = updatedBudget; } else { list.add(updatedBudget); } state = AsyncValue.data(list); } } } final budgetProvider = AsyncNotifierProvider>(() => BudgetNotifier()); class RecurringTransactionNotifier extends AsyncNotifier> { @override FutureOr> build() { return ref.watch(apiRepositoryProvider).getRecurringTransactions(); } Future addRecurringTransaction(RecurringTransaction rt) async { final newRt = await ref.read(apiRepositoryProvider).addRecurringTransaction(rt); if (state.value != null) { state = AsyncValue.data([newRt, ...state.value!]); } } } final recurringTransactionProvider = AsyncNotifierProvider>(() => RecurringTransactionNotifier()); class WalletNotifier extends AsyncNotifier> { @override FutureOr> build() async { final wallets = await ref.watch(apiRepositoryProvider).getWallets(); wallets.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); return wallets; } Future createWallet({ required String name, String nature = 'CASH', double initialBalance = 0.0, DateTime? initialBalanceDate, String currency = 'INR', String? icon, String? color, String? subNature, double? creditLimit, double? fixedAmount, String? paymentCycle, int? cycleDate, }) async { final newWallet = await ref.read(apiRepositoryProvider).createWallet( name: name, nature: nature, initialBalance: initialBalance, initialBalanceDate: initialBalanceDate != null ? "${initialBalanceDate.year}-${initialBalanceDate.month.toString().padLeft(2, '0')}-${initialBalanceDate.day.toString().padLeft(2, '0')}" : null, currency: currency, icon: icon, color: color, subNature: subNature, creditLimit: creditLimit, fixedAmount: fixedAmount, paymentCycle: paymentCycle, cycleDate: cycleDate, ); if (state.value != null) { final updated = [...state.value!, newWallet]; updated.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); state = AsyncValue.data(updated); } return newWallet; } Future inviteUser(int walletId, String email) async { await ref.read(apiRepositoryProvider).inviteUserToWallet(walletId, email); } Future editWallet(int id, {String? name, String? nature, String? icon, String? color, double? initialBalance, DateTime? initialBalanceDate, String? subNature, double? creditLimit, double? fixedAmount, String? paymentCycle, int? cycleDate}) async { final updated = await ref.read(apiRepositoryProvider).editWallet( id: id, name: name, nature: nature, icon: icon, color: color, initialBalance: initialBalance, initialBalanceDate: initialBalanceDate != null ? "${initialBalanceDate.year}-${initialBalanceDate.month.toString().padLeft(2, '0')}-${initialBalanceDate.day.toString().padLeft(2, '0')}" : null, subNature: subNature, creditLimit: creditLimit, fixedAmount: fixedAmount, paymentCycle: paymentCycle, cycleDate: cycleDate, ); if (state.value != null) { final updatedList = state.value!.map((w) => w.id == id ? updated : w).toList(); updatedList.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); state = AsyncValue.data(updatedList); } } Future deleteWallet(int id) async { await ref.read(apiRepositoryProvider).deleteWallet(id); if (state.value != null) { state = AsyncValue.data( state.value!.where((w) => w.id != id).toList(), ); } } } class InvitationNotifier extends AsyncNotifier> { @override Future> build() async { return ref.read(apiRepositoryProvider).getInvitations(); } Future acceptInvitation(int invitationId) async { await ref.read(apiRepositoryProvider).acceptInvitation(invitationId); state = AsyncValue.data(state.value?.where((inv) => inv.id != invitationId).toList() ?? []); ref.invalidate(walletProvider); // Refresh wallets } Future rejectInvitation(int invitationId) async { await ref.read(apiRepositoryProvider).rejectInvitation(invitationId); state = AsyncValue.data(state.value?.where((inv) => inv.id != invitationId).toList() ?? []); } } final walletProvider = AsyncNotifierProvider>(() => WalletNotifier()); final invitationProvider = AsyncNotifierProvider>(() => InvitationNotifier());