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
+103 -9
View File
@@ -5,16 +5,28 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/auth_provider.dart';
import '../providers/sync_provider.dart';
import '../screens/account_screen.dart';
import '../screens/login_screen.dart';
import '../screens/settings_screen.dart';
Widget authAppBarAction(BuildContext context) {
final auth = context.watch<AuthController>();
final sync = context.watch<SyncController>();
final showRing = sync.showProgressRing;
final progress = sync.syncProgress;
final success = sync.progressSuccess;
final syncActive = sync.isSyncing || showRing;
if (auth.isLoggedIn) {
final ringColor = success
? const Color(0xFF16A34A)
: Theme.of(context).colorScheme.primary;
return PopupMenuButton<_AccountMenuAction>(
tooltip: auth.displayName,
tooltip: sync.isSyncing
? 'Syncing… ${(progress * 100).round()}%'
: auth.displayName,
offset: const Offset(0, 48),
onSelected: (action) async {
switch (action) {
@@ -28,6 +40,9 @@ Widget authAppBarAction(BuildContext context) {
},
itemBuilder: (context) {
final email = auth.email;
// Capture whether sync was active when the menu opened; the tile
// itself keeps listening so the % can update while the menu is open.
final showSyncRow = syncActive;
return [
if (email != null)
PopupMenuItem<_AccountMenuAction>(
@@ -37,6 +52,11 @@ Widget authAppBarAction(BuildContext context) {
style: Theme.of(context).textTheme.bodySmall,
),
),
if (showSyncRow)
const PopupMenuItem<_AccountMenuAction>(
enabled: false,
child: _SyncProgressMenuTile(),
),
const PopupMenuItem<_AccountMenuAction>(
value: _AccountMenuAction.account,
child: ListTile(
@@ -57,14 +77,38 @@ Widget authAppBarAction(BuildContext context) {
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: CircleAvatar(
radius: 16,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
child: Text(
auth.initials,
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
child: SizedBox(
width: 32,
height: 32,
child: Stack(
alignment: Alignment.center,
children: [
CircleAvatar(
radius: 16,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
child: Text(
auth.initials,
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color:
Theme.of(context).colorScheme.onPrimaryContainer,
),
),
),
if (showRing)
SizedBox(
width: 32,
height: 32,
child: CircularProgressIndicator(
value: progress <= 0 ? null : progress.clamp(0.02, 1.0),
strokeWidth: 2.5,
color: ringColor,
backgroundColor: Theme.of(context)
.colorScheme
.outlineVariant
.withValues(alpha: 0.45),
),
),
],
),
),
),
@@ -82,6 +126,56 @@ Widget authAppBarAction(BuildContext context) {
);
}
/// Live sync status row inside the account popup (watches [SyncController]).
class _SyncProgressMenuTile extends StatelessWidget {
const _SyncProgressMenuTile();
@override
Widget build(BuildContext context) {
final sync = context.watch<SyncController>();
final theme = Theme.of(context);
final colors = theme.colorScheme;
final pct = (sync.syncProgress * 100).round().clamp(0, 100);
final detail = sync.statusDetail.trim();
if (sync.progressSuccess) {
return ListTile(
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.check_circle_outline, color: colors.primary),
title: Text(
'Sync complete',
style: theme.textTheme.bodyMedium?.copyWith(
color: colors.onSurface,
),
),
);
}
return ListTile(
contentPadding: EdgeInsets.zero,
leading: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
value: sync.syncProgress <= 0
? null
: sync.syncProgress.clamp(0.02, 1.0),
strokeWidth: 2.5,
),
),
title: Text(
detail.isEmpty ? 'Sync in progress ($pct%)' : detail,
style: theme.textTheme.bodyMedium?.copyWith(
color: colors.onSurface,
),
),
subtitle: detail.isEmpty
? null
: Text('Sync in progress ($pct%)'),
);
}
}
enum _AccountMenuAction { account, logout }
List<Widget> bankAppBarActions({
@@ -96,7 +190,6 @@ List<Widget> bankAppBarActions({
onPressed: refreshing ? null : onRefresh,
icon: const Icon(Icons.refresh),
),
authAppBarAction(context),
IconButton(
tooltip: 'Settings',
onPressed: () {
@@ -106,5 +199,6 @@ List<Widget> bankAppBarActions({
},
icon: const Icon(Icons.settings_outlined),
),
authAppBarAction(context),
];
}