feat(flutter): refresh kebab on system playlist cards (#416 parity)

Closes the Flutter half of Fable #416. Web got a generalized
system-playlist refresh kebab in d12afda; this brings Flutter to
parity instead of leaving the affordance web-only.

- PlaylistsApi.refreshSystem(variant): POST
  /api/playlists/system/{for-you|discover}/refresh, maps the
  underscore model variant to the hyphenated route segment,
  returns the rotated playlist id.
- PlaylistCard: top-right PopupMenuButton on system playlists
  with a context-labelled "Refresh For You" / "Refresh Discover"
  item. Calls refreshSystem, invalidates playlistsListProvider
  (which reconciles the rotated UUID + new tracks), snackbars
  the result. ScaffoldMessenger captured pre-await.
- Tests: kebab present for system, absent for user playlists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 06:35:55 -04:00
parent d12afdad6e
commit 7a0437087a
3 changed files with 87 additions and 0 deletions
@@ -53,4 +53,20 @@ class PlaylistsApi {
data: {'track_ids': trackIds},
);
}
/// POST /api/playlists/system/{variant}/refresh. Synchronously
/// rebuilds the caller's system playlist for the given variant
/// ("for_you" | "discover") and returns the new playlist id, or
/// null when the library is empty so there's nothing to build.
///
/// The variant uses underscores in the model (system_variant) but
/// the route segment is hyphenated ("for-you"), so map here.
Future<String?> refreshSystem(String variant) async {
final segment = variant == 'for_you' ? 'for-you' : variant;
final r = await _dio.post<Map<String, dynamic>>(
'/api/playlists/system/$segment/refresh',
data: const <String, dynamic>{},
);
return (r.data ?? const {})['playlist_id'] as String?;
}
}