import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:pyramid/core/theme.dart'; import 'package:pyramid/core/update_checker.dart'; import 'package:pyramid/widgets/update_download_dialog.dart'; /// Slim top banner shown when a new Pyramid version is available. class UpdateBanner extends ConsumerWidget { const UpdateBanner({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return ref.watch(updateInfoProvider).when( loading: () => const SizedBox.shrink(), error: (e, _) => const SizedBox.shrink(), data: (info) => info == null ? const SizedBox.shrink() : _Banner(info: info), ); } } class _Banner extends ConsumerWidget { final UpdateInfo info; const _Banner({required this.info}); @override Widget build(BuildContext context, WidgetRef ref) { final pt = PyramidTheme.of(context); final canDownload = info.canDownload; return Container( width: double.infinity, decoration: BoxDecoration( color: pt.accent.withAlpha(28), border: Border( bottom: BorderSide(color: pt.accent.withAlpha(80)), ), ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ Icon(Icons.system_update_alt_rounded, size: 15, color: pt.accent), const SizedBox(width: 8), Expanded( child: Text( 'Pyramid ${info.tagName} ist verfügbar', style: TextStyle( color: pt.fg, fontSize: 12.5, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), _BannerBtn( label: 'Was ist neu', icon: Icons.open_in_new_rounded, color: pt.bg3, fgColor: pt.fg, onTap: () => _openUrl(info.htmlUrl), ), const SizedBox(width: 6), _BannerBtn( label: canDownload ? 'Herunterladen' : 'Öffnen', icon: canDownload ? Icons.download_rounded : Icons.open_in_new_rounded, color: pt.accent, fgColor: pt.accentFg, onTap: () { if (canDownload) { showUpdateDownloadDialog(context, info); } else { _openUrl(info.htmlUrl); } }, ), const SizedBox(width: 6), Tooltip( message: 'Für diese Version ausblenden', child: InkWell( borderRadius: BorderRadius.circular(4), onTap: () async { await skipUpdateVersion(info.tagName); ref.invalidate(updateInfoProvider); }, child: Padding( padding: const EdgeInsets.all(4), child: Icon(Icons.close_rounded, size: 14, color: pt.fgDim), ), ), ), ], ), ); } Future _openUrl(String url) async { final uri = Uri.parse(url); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } } } class _BannerBtn extends StatelessWidget { final String label; final IconData icon; final Color color; final Color fgColor; final VoidCallback onTap; const _BannerBtn({ required this.label, required this.icon, required this.color, required this.fgColor, required this.onTap, }); @override Widget build(BuildContext context) { return InkWell( borderRadius: BorderRadius.circular(6), onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(6)), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 12, color: fgColor), const SizedBox(width: 4), Text(label, style: TextStyle( color: fgColor, fontSize: 12, fontWeight: FontWeight.w600)), ], ), ), ); } }