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),
 | 
						|
          );
 | 
						|
  }
 | 
						|
}
 |