diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 3516894..56ee7c7 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -5,6 +5,7 @@ import '../services/webdav_service.dart'; import '../widgets/magnetic_scroll_physics.dart'; import 'webdav_setup_page.dart'; import 'settings_page.dart'; +import '../database/song_database.dart'; import 'webdav_file_list_page.dart'; import '../constants/ui_constants.dart'; import '../base/base_state.dart'; @@ -236,7 +237,7 @@ class _HomePageState extends BaseState { safeSetState(() => _isLoading = true); try { await WebDAVService.instance.loadCredentials(); - _favorites = []; + await _loadFavoritesFromDb(); } catch (e) { _favorites = []; } finally { @@ -244,6 +245,34 @@ class _HomePageState extends BaseState { } } + /// 收藏列表直接绑定 favorites 表 + songs 元数据 + Future _loadFavoritesFromDb() async { + final db = SongDatabase(); + final favRows = await db.getFavorites(); + final paths = favRows + .map((row) => row['song_id'] as String?) + .whereType() + .toList(); + final meta = await db.getSongsByPaths(paths); + + final items = []; + for (final path in paths) { + final row = meta[path]; + final title = (row?['title'] as String?)?.trim() ?? ''; + final artist = (row?['artist'] as String?)?.trim() ?? ''; + final hasTitle = title.isNotEmpty; + items.add(SongItem( + path: path, + fileName: path.split('/').last, + title: hasTitle ? title : null, + artist: artist.isNotEmpty ? artist : null, + sourceTag: '收藏', + metadataState: hasTitle ? 'success' : 'unknown', + )); + } + _favorites = items; + } + void _playSong(SongItem song) async { try { // 先校验登录,再开始播放。 @@ -416,6 +445,7 @@ class _HomePageState extends BaseState { builder: (_) => const WebDAVFileListPage(), ), ); + await _loadFavoritesFromDb(); _resetSnapState(); safeSetState(() {}); } else { @@ -674,12 +704,11 @@ class _HomePageState extends BaseState { delegate: SliverChildBuilderDelegate( (context, index) { final song = _favorites[index]; - final itemProgress = - (_listReveal * 2 - index / 5) - .clamp(0.0, 1.0); + // 收藏项不做滚动渐显:_listReveal 在首屏为 0, + // 会让条目完全透明(看起来"收藏了但不显示")。 return AnimatedOpacity( - opacity: itemProgress, + opacity: 1.0, duration: const Duration(milliseconds: 150), child: Padding( padding: const EdgeInsets.symmetric( @@ -713,13 +742,14 @@ class _HomePageState extends BaseState { trailing: IconButton( icon: const Icon( Icons.favorite, - color: Color(0xFFB8D4D0), + color: Color(0xFFE05C5C), size: 20, ), - onPressed: () { - safeSetState(() { - _favorites.removeAt(index); - }); + onPressed: () async { + await SongDatabase() + .removeFavorite(song.path); + await _loadFavoritesFromDb(); + if (mounted) safeSetState(() {}); }, ), onTap: () => _playSong(song), diff --git a/lib/pages/webdav_file_list_page.dart b/lib/pages/webdav_file_list_page.dart index 3cfb5c9..0c72220 100644 --- a/lib/pages/webdav_file_list_page.dart +++ b/lib/pages/webdav_file_list_page.dart @@ -27,6 +27,9 @@ class _WebDAVFileListPageState extends BaseState { Map> _metaByPath = {}; String? _lastSyncedSongPath; + /// 已收藏的歌曲路径 + Set _favoritePaths = {}; + // 安全解码 String _safeDecode(String input) { try { @@ -69,6 +72,30 @@ class _WebDAVFileListPageState extends BaseState { }); } + /// 收藏 / 取消收藏:整行点击是播放,右侧红心专门做收藏 + Future _toggleFavorite(String path) async { + final wasFavorite = _favoritePaths.contains(path); + try { + await SongDatabase().toggleFavorite(path); + if (!mounted) return; + safeSetState(() { + final next = {..._favoritePaths}; + if (wasFavorite) { + next.remove(path); + } else { + next.add(path); + } + _favoritePaths = next; + }); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('收藏失败: $e')), + ); + } + } + } + // ============================================================ // 加载目录 // ============================================================ @@ -94,10 +121,22 @@ class _WebDAVFileListPageState extends BaseState { debugPrint('⚠️ [WebDAVList] 批量读取元数据失败: $e'); } + var favorites = {}; + try { + final favRows = await SongDatabase().getFavorites(); + favorites = favRows + .map((row) => row['song_id'] as String?) + .whereType() + .toSet(); + } catch (e) { + debugPrint('⚠️ [WebDAVList] 读取收藏失败: $e'); + } + if (mounted) { safeSetState(() { _items = items; _metaByPath = meta; + _favoritePaths = favorites; _isLoading = false; }); // 如果当前正在播的歌就在这个目录里,补一次同步 @@ -382,8 +421,16 @@ class _WebDAVFileListPageState extends BaseState { ), ), trailing: IconButton( - icon: const Icon(Icons.play_arrow, color: Color(0xFFB8D4D0)), - onPressed: () => _playSong(item), + tooltip: '收藏', + icon: Icon( + _favoritePaths.contains(item.path) + ? Icons.favorite + : Icons.favorite_border, + color: _favoritePaths.contains(item.path) + ? const Color(0xFFE05C5C) + : Colors.grey, + ), + onPressed: () => _toggleFavorite(item.path), ), onTap: () => _playSong(item), );