forked from Nexus/nexus
85 lines
2.8 KiB
Dart
85 lines
2.8 KiB
Dart
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
|
|
? []
|
|
: [
|
|
?status.error,
|
|
if (status.errorCount > 0) "Failed attempts: ${status.errorCount}.",
|
|
if (status.lastSync case final lastSync?)
|
|
"Last synced ${format(lastSync)}.",
|
|
];
|
|
|
|
return AnimatedSize(
|
|
duration: Durations.short4,
|
|
alignment: .topCenter,
|
|
child: banner == null
|
|
? const SizedBox(width: double.infinity)
|
|
: Material(
|
|
color: banner.background,
|
|
child: InkWell(
|
|
onTap: details.isEmpty
|
|
? 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.isNotEmpty)
|
|
Icon(Icons.info_outline, color: banner.foreground),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|