feat(flutter/likes): LikeButton with optimistic toggle + rollback
LikedIdsController loads /api/likes/ids once and mutates the local set on toggle. Failed mutations roll the set back so the icon never lies. Wired into ArtistDetail header, AlbumDetail header, and per-track in the album track list.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
enum LikeKind { artist, album, track }
|
||||
|
||||
extension LikeKindPath on LikeKind {
|
||||
String get path => switch (this) {
|
||||
LikeKind.artist => 'artists',
|
||||
LikeKind.album => 'albums',
|
||||
LikeKind.track => 'tracks',
|
||||
};
|
||||
}
|
||||
|
||||
class LikesApi {
|
||||
LikesApi(this._dio);
|
||||
final Dio _dio;
|
||||
|
||||
Future<void> like(LikeKind kind, String id) async {
|
||||
await _dio.post<void>('/api/likes/${kind.path}/$id');
|
||||
}
|
||||
|
||||
Future<void> unlike(LikeKind kind, String id) async {
|
||||
await _dio.delete<void>('/api/likes/${kind.path}/$id');
|
||||
}
|
||||
|
||||
/// Returns sets of {artists, albums, tracks} the user has liked.
|
||||
/// Server response keys (verified in internal/api/likes.go
|
||||
/// `likedIDsResponse`): `artist_ids`, `album_ids`, `track_ids`.
|
||||
Future<({Set<String> artists, Set<String> albums, Set<String> tracks})>
|
||||
ids() async {
|
||||
final r = await _dio.get<Map<String, dynamic>>('/api/likes/ids');
|
||||
final body = r.data ?? const <String, dynamic>{};
|
||||
Set<String> set(String key) =>
|
||||
((body[key] as List?) ?? const []).map((e) => e.toString()).toSet();
|
||||
return (
|
||||
artists: set('artist_ids'),
|
||||
albums: set('album_ids'),
|
||||
tracks: set('track_ids'),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user