feat(account-menu): move force full sync button from edit screen to account menu (#99)
Add "Force full sync" popup menu item below "Verify sync health" in the per-account menu on the account list screen, with a confirmation dialog. Remove the button and handler from the edit account screen.
This commit is contained in:
@@ -154,6 +154,10 @@ class _AccountTile extends ConsumerWidget {
|
||||
value: _AccountAction.verifySync,
|
||||
child: Text('Verify sync health'),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
value: _AccountAction.forceSync,
|
||||
child: Text('Force full sync'),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
value: _AccountAction.edit,
|
||||
child: Text('Edit'),
|
||||
@@ -204,6 +208,34 @@ class _AccountTile extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
break;
|
||||
case _AccountAction.forceSync:
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Force full sync?'),
|
||||
content: const Text(
|
||||
'This clears all locally-cached emails and mailboxes for this '
|
||||
'account and immediately re-downloads everything from the server. '
|
||||
'Previously viewed email content will not need to be re-downloaded.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
child: const Text('Force sync'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed == true && context.mounted) {
|
||||
await ProviderScope.containerOf(
|
||||
context,
|
||||
).read(syncManagerProvider).forceResync(account.id);
|
||||
}
|
||||
break;
|
||||
case _AccountAction.edit:
|
||||
await context.push('/accounts/${account.id}/edit');
|
||||
break;
|
||||
@@ -354,6 +386,7 @@ class _Step extends StatelessWidget {
|
||||
enum _AccountAction {
|
||||
syncLog,
|
||||
verifySync,
|
||||
forceSync,
|
||||
edit,
|
||||
emailFiltersRemote,
|
||||
emailFiltersLocal,
|
||||
|
||||
@@ -43,7 +43,6 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
|
||||
bool _tryTesting = false;
|
||||
String? _tryOk;
|
||||
String? _tryErr;
|
||||
bool _resyncing = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -171,43 +170,6 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _forceResync() async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Force full sync?'),
|
||||
content: const Text(
|
||||
'This clears all locally-cached emails and mailboxes for this '
|
||||
'account and immediately re-downloads everything from the server. '
|
||||
'Previously viewed email content will not need to be re-downloaded.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
child: const Text('Force sync'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed != true || !mounted) return;
|
||||
setState(() => _resyncing = true);
|
||||
try {
|
||||
await ref.read(syncManagerProvider).forceResync(widget.accountId);
|
||||
if (mounted) context.pop();
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_resyncing = false;
|
||||
_errorMessage = 'Force sync failed: $e';
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _save() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
final password = _passwordCtrl.text.isNotEmpty ? _passwordCtrl.text : null;
|
||||
@@ -268,7 +230,7 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Edit account')),
|
||||
body: _loading || _saving || _resyncing
|
||||
body: _loading || _saving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _buildForm(),
|
||||
);
|
||||
@@ -387,15 +349,6 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
FilledButton(onPressed: _save, child: const Text('Save')),
|
||||
const SizedBox(height: 8),
|
||||
OutlinedButton.icon(
|
||||
icon: const Icon(Icons.sync_problem),
|
||||
label: const Text('Force full sync'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
onPressed: _forceResync,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -153,6 +153,43 @@ void main() {
|
||||
expect(find.text('Export account'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('account popup menu contains Force full sync item', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/accounts',
|
||||
overrides: baseOverrides(accounts: [kTestAccount]),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.more_vert));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Force full sync'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Force full sync appears below Verify sync health in popup menu',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/accounts',
|
||||
overrides: baseOverrides(accounts: [kTestAccount]),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byIcon(Icons.more_vert));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final verifyPos = tester.getTopLeft(find.text('Verify sync health')).dy;
|
||||
final forcePos = tester.getTopLeft(find.text('Force full sync')).dy;
|
||||
expect(forcePos, greaterThan(verifyPos));
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('AppBar does not overflow at minimum supported window size', (
|
||||
tester,
|
||||
) async {
|
||||
|
||||
@@ -45,6 +45,18 @@ void main() {
|
||||
expect(find.text('Save'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('does not show Force full sync button', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildApp(
|
||||
initialLocation: '/accounts/acc-1/edit',
|
||||
overrides: baseOverrides(accounts: [kTestAccount]),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Force full sync'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('saving without password change pops back', (tester) async {
|
||||
tester.view.physicalSize = const Size(800, 1400);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
|
||||
Reference in New Issue
Block a user