import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; /// User-facing app name. Platform launcher labels (AndroidManifest, Info.plist, /// web/manifest.json, windows/runner) are set separately. const appName = 'Tagesbyte'; String timeAgo(DateTime? date) { if (date == null) return ''; final diff = DateTime.now().difference(date.toLocal()); if (diff.inMinutes < 1) return 'just now'; if (diff.inMinutes < 60) return '${diff.inMinutes} min ago'; if (diff.inHours < 24) return '${diff.inHours} h ago'; if (diff.inDays == 1) return 'yesterday'; return '${diff.inDays} days ago'; } String formatDateTime(DateTime date) { final d = date.toLocal(); String two(int n) => n.toString().padLeft(2, '0'); return '${two(d.day)}.${two(d.month)}.${d.year} ${two(d.hour)}:${two(d.minute)}'; } const weekdayNames = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', ]; const monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; bool isSameDay(DateTime a, DateTime b) => a.year == b.year && a.month == b.month && a.day == b.day; Future openUrl(BuildContext context, String url) async { final ok = await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication); if (!ok && context.mounted) { ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text('Could not open $url'))); } } /// Centered icon + text + optional action, used for empty and error states. class EmptyState extends StatelessWidget { const EmptyState({ super.key, required this.icon, required this.title, this.message, this.action, }); final IconData icon; final String title; final String? message; final Widget? action; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 56, color: theme.colorScheme.primary), const SizedBox(height: 16), Text(title, style: theme.textTheme.titleMedium, textAlign: TextAlign.center), if (message != null) ...[ const SizedBox(height: 8), Text( message!, style: theme.textTheme.bodyMedium ?.copyWith(color: theme.colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), ], if (action != null) ...[const SizedBox(height: 20), action!], ], ), ), ); } }