fix(flutter): reconcile stale playlists + recover from 404 on tap
The 404 on tapping the For-You tile traced to stale drift rows. BuildSystemPlaylists rotates UUIDs on every rebuild, so old For-You / Songs-Like ids accumulate in cachedPlaylists. The list provider's fetchAndPopulate was only doing insertOrReplace, which adds new rows but never removes the obsolete ones — so the home tile renders 8 playlists when the server only knows 4, and 4 of those tiles 404 on tap. Two fixes: playlistsListProvider.fetchAndPopulate now reconciles. After fetching the fresh list, deleteWhere any user-owned drift row whose id isn't in the fresh response, then upsert the fresh set in the same batch. Public-from-others rows are left alone — they're not keyed by ownership and we don't want to drop someone else's public playlist just because the current user's response didn't enumerate it. Operates inside the existing batch so it's atomic. playlistDetailProvider.fetchAndPopulate now treats a DioException 404 as "this row is stale": delete the cachedPlaylists + any cachedPlaylistTracks rows for this id, return false so the UI yields emptyDetail. The next render of the home row sees the row gone and the tile disappears, completing the cleanup. Side note: every system-playlist rebuild discards drift rows for the just-evicted UUIDs and writes the new ones. That cycle's been silently churning since system playlists shipped — this is the first time the cleanup actually runs.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:drift/drift.dart' as drift;
|
||||
import 'package:flutter/foundation.dart' show debugPrint;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@@ -44,7 +45,21 @@ final playlistsListProvider =
|
||||
debugPrint(
|
||||
'playlistsListProvider($kind): wire returned owned=${fresh.owned.length} '
|
||||
'(system=$ownedSysCount) public=${fresh.public.length}');
|
||||
|
||||
// Reconcile: BuildSystemPlaylists rotates system-playlist UUIDs
|
||||
// every rebuild, so insertOrReplace alone leaves stale rows in
|
||||
// drift. Tapping one of those stale tiles 404s. Delete every
|
||||
// owned drift row whose id isn't in the fresh response, then
|
||||
// upsert the fresh set.
|
||||
final freshOwnedIds =
|
||||
fresh.owned.map((p) => p.id).toSet();
|
||||
await db.batch((b) {
|
||||
if (user != null) {
|
||||
b.deleteWhere(db.cachedPlaylists, (t) {
|
||||
return t.userId.equals(user.id) &
|
||||
t.id.isNotIn(freshOwnedIds);
|
||||
});
|
||||
}
|
||||
for (final p in fresh.all) {
|
||||
b.insert(db.cachedPlaylists, p.toDrift(),
|
||||
mode: drift.InsertMode.insertOrReplace);
|
||||
@@ -216,6 +231,21 @@ final playlistDetailProvider =
|
||||
'playlistDetailProvider($id): drift write done; awaiting watch re-emit');
|
||||
return true;
|
||||
} catch (e, st) {
|
||||
// 404 = the playlist row in drift is stale (BuildSystemPlaylists
|
||||
// rotates UUIDs on each rebuild, so old For-You / Songs-Like
|
||||
// tiles can outlive the actual server-side playlist). Wipe the
|
||||
// stale row + its track positions so the home tile disappears
|
||||
// on next render and we don't keep trying.
|
||||
if (e is DioException && e.response?.statusCode == 404) {
|
||||
debugPrint(
|
||||
'playlistDetailProvider($id): server says 404, evicting stale drift rows');
|
||||
await db.batch((b) {
|
||||
b.deleteWhere(
|
||||
db.cachedPlaylistTracks, (t) => t.playlistId.equals(id));
|
||||
b.deleteWhere(db.cachedPlaylists, (t) => t.id.equals(id));
|
||||
});
|
||||
return false;
|
||||
}
|
||||
debugPrint('playlistDetailProvider($id): fetch failed: $e\n$st');
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user