import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/services/cover_download_service.dart'; import 'package:spotiflac_android/utils/string_utils.dart'; /// Adds the app-wide long-press-to-save behavior to a remote cover image. /// /// The child keeps its normal tap behavior. Only the long-press gesture is /// claimed here, so a surrounding track or collection card can still open its /// usual options when the user presses outside the artwork. class DownloadableCover extends ConsumerStatefulWidget { final String? coverUrl; final String baseName; final Widget child; final bool enabled; const DownloadableCover({ super.key, required this.coverUrl, required this.baseName, required this.child, this.enabled = true, }); @override ConsumerState createState() => _DownloadableCoverState(); } class _DownloadableCoverState extends ConsumerState { bool _saving = false; String get _coverUrl => normalizeRemoteHttpUrl(widget.coverUrl) ?? ''; Future _saveCover() async { if (_saving) return; final coverUrl = _coverUrl; if (coverUrl.isEmpty) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(context.l10n.trackCoverNoSource))); return; } setState(() => _saving = true); ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar(SnackBar(content: Text(context.l10n.updateDownloading))); try { final saved = await CoverDownloadService.saveRemoteCover( coverUrl: coverUrl, baseName: widget.baseName, settings: ref.read(settingsProvider), ); if (!mounted) return; ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar(content: Text(context.l10n.trackCoverSaved(saved.fileName))), ); } catch (error) { if (!mounted) return; ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( SnackBar( content: Text( context.l10n.trackSaveFailed(context.friendlyError(error)), ), ), ); } finally { if (mounted) setState(() => _saving = false); } } @override Widget build(BuildContext context) { final canSave = widget.enabled && _coverUrl.isNotEmpty; final semanticLabel = '${context.l10n.dialogDownload} ' '${context.l10n.editMetadataFieldCover}: ${widget.baseName}'; return Semantics( button: canSave, label: semanticLabel, onLongPress: canSave ? _saveCover : null, child: GestureDetector( behavior: HitTestBehavior.opaque, excludeFromSemantics: true, onLongPress: canSave ? _saveCover : null, child: Stack( fit: StackFit.passthrough, children: [ widget.child, if (_saving) const Positioned.fill( child: ColoredBox( color: Color(0x33000000), child: Center( child: SizedBox.square( dimension: 22, child: CircularProgressIndicator(strokeWidth: 2), ), ), ), ), ], ), ), ); } }