195 lines
5.4 KiB
Dart
195 lines
5.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'album_controller.dart';
|
|
import 'album_actions.dart';
|
|
import 'package:aves/widgets/common/thumbnail/aves_thumbnail_widget.dart';
|
|
|
|
/// Tile principale usato nella griglia "Tutti gli album"
|
|
/// Basato su AlbumRow (derivato da FolderStats)
|
|
class AlbumTile extends StatelessWidget {
|
|
final AlbumRow album;
|
|
final VoidCallback onTap;
|
|
final VoidCallback onLongPress;
|
|
|
|
const AlbumTile({
|
|
super.key,
|
|
required this.album,
|
|
required this.onTap,
|
|
required this.onLongPress,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
onLongPress: onLongPress,
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(12),
|
|
child: Stack(
|
|
children: [
|
|
// COVER
|
|
Positioned.fill(
|
|
child: Hero(
|
|
tag: 'album:${album.path}',
|
|
child: AlbumTileThumbnail(album: album),
|
|
),
|
|
),
|
|
|
|
// GRADIENT OVERLAY
|
|
Positioned.fill(
|
|
child: DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.bottomCenter,
|
|
end: Alignment.center,
|
|
colors: [
|
|
Colors.black.withOpacity(0.7),
|
|
Colors.black.withOpacity(0.2),
|
|
Colors.transparent,
|
|
],
|
|
stops: const [0.0, 0.4, 1.0],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// BADGES (remoto, vault, hybrid, pinned, hidden)
|
|
Positioned(
|
|
top: 6,
|
|
right: 6,
|
|
child: Row(
|
|
children: [
|
|
if (album.isRemote)
|
|
const _Badge(icon: Icons.cloud_outlined),
|
|
|
|
if (album.isVault)
|
|
const _Badge(icon: Icons.lock_outline),
|
|
|
|
if (album.hybridType != 0)
|
|
const _Badge(icon: Icons.all_inclusive),
|
|
|
|
if (album.priority > 0)
|
|
const _Badge(icon: Icons.push_pin_outlined),
|
|
|
|
if (album.hidden)
|
|
const _Badge(icon: Icons.visibility_off_outlined),
|
|
],
|
|
),
|
|
),
|
|
|
|
// TESTO (nome + count + size)
|
|
Positioned(
|
|
left: 8,
|
|
right: 8,
|
|
bottom: 8,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
album.displayName,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 15,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
'${album.fileCount} file',
|
|
style: const TextStyle(
|
|
color: Colors.white70,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
Text(
|
|
_formatSize(album.totalSize),
|
|
style: const TextStyle(
|
|
color: Colors.white60,
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// MENU RAPIDO (3 dots)
|
|
Positioned(
|
|
top: 0,
|
|
right: 0,
|
|
child: IconButton(
|
|
icon: const Icon(Icons.more_vert, color: Colors.white),
|
|
onPressed: () {
|
|
AlbumActions.showMenu(context, album);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
String _formatSize(int bytes) {
|
|
if (bytes < 1024) return '$bytes B';
|
|
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
|
if (bytes < 1024 * 1024 * 1024) return '${(bytes / 1024 / 1024).toStringAsFixed(1)} MB';
|
|
return '${(bytes / 1024 / 1024 / 1024).toStringAsFixed(1)} GB';
|
|
}
|
|
}
|
|
|
|
/// Thumbnail con fallback
|
|
class AlbumTileThumbnail extends StatelessWidget {
|
|
final AlbumRow album;
|
|
final double radius;
|
|
|
|
const AlbumTileThumbnail({
|
|
super.key,
|
|
required this.album,
|
|
this.radius = 12,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final entry = album.thumbEntry;
|
|
|
|
if (entry == null) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.shade900,
|
|
borderRadius: BorderRadius.circular(radius),
|
|
),
|
|
child: const Icon(Icons.folder, size: 48, color: Colors.white54),
|
|
);
|
|
}
|
|
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.circular(radius),
|
|
child: AvesThumbnailWidget(
|
|
entry: entry,
|
|
heroTag: 'album:${album.path}',
|
|
fit: BoxFit.cover,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Badge icona piccolo
|
|
class _Badge extends StatelessWidget {
|
|
final IconData icon;
|
|
|
|
const _Badge({required this.icon});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.only(left: 4),
|
|
padding: const EdgeInsets.all(5),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black.withOpacity(0.55),
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: Icon(icon, color: Colors.white, size: 16),
|
|
);
|
|
}
|
|
}
|