Watch
1
0
Fork
You've already forked nexus
0
forked from Nexus/nexus

add syncing waiting / error / failed indicator

This commit is contained in:
Henry Hiles 2026-10-14 23:21:31 -04:00
commit 07700c1eff
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
6 changed files with 186 additions and 75 deletions

View file

@ -0,0 +1,85 @@
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/controllers/sync_status.dart";
import "package:nexus/widgets/error_dialog.dart";
import "package:timeago/timeago.dart";
class const SyncStatusBanner({super.key}) extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final status = ref.watch(SyncStatusController.provider);
final colorScheme = Theme.of(context).colorScheme;
final banner = switch (status?.type) {
null || .ok => null,
.waiting => (
title: "Waiting for sync...",
icon: Icons.sync,
background: colorScheme.primary,
foreground: colorScheme.onPrimary,
),
.erroring => (
title: "Sync is failing, retrying...",
icon: Icons.sync_problem,
background: colorScheme.error,
foreground: colorScheme.onError,
),
.permanentlyFailed => (
title: "Sync has permanently failed",
icon: Icons.cloud_off,
background: colorScheme.error,
foreground: colorScheme.onError,
),
};
final details = status == null
? null
: [
?status.error,
if (status.errorCount > 0) "Failed attempts: ${status.errorCount}.",
if (status.lastSync case final lastSync?)
"Last synced ${format(lastSync)}.",
];
return AnimatedSize(
duration: const .new(milliseconds: 200),
alignment: .topCenter,
child: banner == null
? const SizedBox(width: double.infinity)
: Material(
color: banner.background,
child: InkWell(
onTap: details == null
? null
: () => showDialog(
context: context,
builder: (context) =>
ErrorDialog(details.join("\n\n"), null),
),
child: Padding(
padding: const .symmetric(horizontal: 16, vertical: 8),
child: Row(
spacing: 12,
children: [
Icon(banner.icon, size: 20, color: banner.foreground),
Expanded(
child: Text(
banner.title,
maxLines: 1,
overflow: .ellipsis,
style: TextStyle(
color: banner.foreground,
fontWeight: .bold,
),
),
),
if (details != null)
Icon(Icons.info_outline, color: banner.foreground),
],
),
),
),
),
);
}
}