sync transactions with pocketbase

This commit is contained in:
davmar
2026-09-09 22:24:16 +02:00
parent c6d1fdb19e
commit 96627a830e
9 changed files with 1104 additions and 324 deletions
+72 -5
View File
@@ -1,10 +1,13 @@
/// Signed-in account details and sign-out.
/// Signed-in account details, sync status, and sign-out.
library;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/auth_provider.dart';
import '../providers/bank_provider.dart';
import '../providers/sync_provider.dart';
import '../providers/tx_meta_provider.dart';
import '../utils/constants.dart';
import '../widgets/responsive_body.dart';
@@ -14,9 +17,23 @@ class AccountScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final auth = context.watch<AuthController>();
final sync = context.watch<SyncController>();
final meta = context.watch<TxMetaController>();
final bank = context.watch<BankController>();
final theme = Theme.of(context);
final colors = theme.colorScheme;
final lastSync = sync.lastSyncedAt;
final syncSubtitle = sync.isSyncing
? (sync.statusDetail.isEmpty
? 'Syncing… ${(sync.syncProgress * 100).round()}%'
: '${sync.statusDetail} · ${(sync.syncProgress * 100).round()}%')
: sync.lastError != null
? 'Last error: ${sync.lastError}'
: lastSync == null
? 'Not synced yet'
: 'Last sync ${_formatTime(lastSync)}';
return Scaffold(
appBar: AppBar(title: const Text('Account')),
body: ResponsiveBody(
@@ -63,12 +80,55 @@ class AccountScreen extends StatelessWidget {
),
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.phone_android_outlined),
title: const Text('Local data'),
subtitle: const Text(
'Labels, notes, and bank cache remain on this device.',
leading: sync.isSyncing
? SizedBox(
width: 24,
height: 24,
child: Padding(
padding: const EdgeInsets.all(2),
child: CircularProgressIndicator(
strokeWidth: 2,
value: sync.syncProgress <= 0
? null
: sync.syncProgress.clamp(0.02, 1.0),
),
),
)
: Icon(
sync.lastError == null
? Icons.sync
: Icons.sync_problem_outlined,
),
title: const Text('Cloud sync'),
subtitle: Text(syncSubtitle),
trailing: TextButton(
onPressed: sync.isSyncing ? null : () => sync.syncNow(),
child: const Text('Sync now'),
),
),
if (sync.lastSyncSummary.isNotEmpty) ...[
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.fact_check_outlined),
title: const Text('Last sync detail'),
subtitle: Text(sync.lastSyncSummary),
),
],
const Divider(height: 1),
ListTile(
leading: const Icon(Icons.phone_android_outlined),
title: const Text('What syncs'),
subtitle: Text(
'Bank cache stays on this device '
'(${bank.transactions.length} transactions).\n'
'Cloud gets labels/notes only: '
'${meta.syncableAnnotationCount} annotated txs, '
'${meta.merchantRuleCount} merchant rules, '
'${meta.customLabelCount} custom labels.\n'
'PocketBase tx_meta ≈ annotated count, not bank total.',
),
isThreeLine: true,
),
],
),
),
@@ -87,4 +147,11 @@ class AccountScreen extends StatelessWidget {
),
);
}
static String _formatTime(DateTime time) {
final local = time.toLocal();
final h = local.hour.toString().padLeft(2, '0');
final m = local.minute.toString().padLeft(2, '0');
return '$h:$m';
}
}