88 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			88 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:hooks_riverpod/hooks_riverpod.dart';
 | |
| import 'package:logging/logging.dart';
 | |
| import 'package:moneymgr_mobile/services/api/api_client.dart';
 | |
| import 'package:moneymgr_mobile/services/api/files_api.dart';
 | |
| import 'package:moneymgr_mobile/services/api/inbox_api.dart';
 | |
| import 'package:moneymgr_mobile/services/storage/expenses.dart';
 | |
| import 'package:moneymgr_mobile/utils/extensions.dart';
 | |
| import 'package:moneymgr_mobile/utils/hooks.dart';
 | |
| import 'package:riverpod_annotation/riverpod_annotation.dart';
 | |
| 
 | |
| part 'synchronize_button.g.dart';
 | |
| 
 | |
| /// Synchronize expenses list with backend
 | |
| @riverpod
 | |
| Future<void> _performSynchronization(Ref ref) async {
 | |
|   final expenses = ref.watch(expensesProvider).requireValue;
 | |
|   final apiService = ref.watch(apiServiceProvider)!;
 | |
| 
 | |
|   final list = await expenses.getList();
 | |
| 
 | |
|   for (final exp in list) {
 | |
|     // First, upload file
 | |
|     final bytes = await expenses.loadFile(exp);
 | |
|     final file = await apiService.uploadFile(
 | |
|       filename: exp.localFileName,
 | |
|       mimeType: exp.mimeType,
 | |
|       bytes: bytes,
 | |
|     );
 | |
| 
 | |
|     // Then, create the inbox entry
 | |
|     await apiService.createInboxEntry(
 | |
|       UpdateInboxEntryRequest(
 | |
|         file_id: file.id,
 | |
|         time: exp.time,
 | |
|         label: exp.label,
 | |
|         amount: -1 * exp.cost,
 | |
|       ),
 | |
|     );
 | |
| 
 | |
|     // Lastly delete the local expense
 | |
|     ref.watch(expensesProvider).requireValue.deleteExpense(exp);
 | |
|   }
 | |
| 
 | |
|   ref.invalidate(expensesProvider);
 | |
| }
 | |
| 
 | |
| class SynchronizeButton extends HookConsumerWidget {
 | |
|   const SynchronizeButton({super.key});
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context, WidgetRef ref) {
 | |
|     final (:pending, :snapshot, :hasError) = useAsyncTask();
 | |
| 
 | |
|     handleSynchronize() async {
 | |
|       try {
 | |
|         await ref.watch(
 | |
|           _performSynchronizationProvider.selectAsync((it) => it),
 | |
|         );
 | |
|       } catch (e, s) {
 | |
|         Logger.root.warning("Failed to synchronize expenses! $e $s");
 | |
|         if (context.mounted) {
 | |
|           context.showTextSnackBar("Failed to synchronize expenses! $e");
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return snapshot.connectionState == ConnectionState.waiting
 | |
|         ? Padding(
 | |
|             padding: const EdgeInsets.all(12.0),
 | |
|             child: SizedBox(
 | |
|               height: 20,
 | |
|               width: 20,
 | |
|               child: CircularProgressIndicator(strokeWidth: 2),
 | |
|             ),
 | |
|           )
 | |
|         : IconButton(
 | |
|             onPressed: () => pending.value = handleSynchronize(),
 | |
|             style: ButtonStyle(
 | |
|               backgroundColor: hasError
 | |
|                   ? WidgetStatePropertyAll(Colors.red)
 | |
|                   : null,
 | |
|             ),
 | |
|             icon: Icon(Icons.sync_rounded),
 | |
|           );
 | |
|   }
 | |
| }
 |