fix(flutter): album/artist detail covers + cacheFirst tracing for hangs
Album detail screen rendered the cover slot as a bare slate Container — no actual image. Wire ServerImage at /api/albums/<id>/cover (96dp, rounded). Same for artist detail: ClipOval around ServerImage of the artist's coverUrl, falling back to slate when empty. Artist navigation hang: artistProvider goes through cacheFirst, which had no logging — so we couldn't see whether the cold-cache fetch was running. Add an optional `tag` parameter that, when set, debugPrints each step (drift hit/miss, connectivity, fetch start/done/error). Wire tags into artistProvider and artistAlbumsProvider, plus add the 3s connectivity timeout there too as belt-and-suspenders. Next test pass should show cacheFirst[artist(<id>)]: lines that pinpoint where the artist screen is sticking.
This commit is contained in:
+13
-1
@@ -17,6 +17,8 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart' show debugPrint;
|
||||
|
||||
/// Wraps the watch + cold-cache fallback pattern. Generic over:
|
||||
/// D — the drift row type (or TypedResult for joins)
|
||||
/// T — the result type the caller wants (e.g. `List<ArtistRef>`)
|
||||
@@ -35,14 +37,19 @@ Stream<T> cacheFirst<D, T>({
|
||||
required T Function(List<D>) toResult,
|
||||
required Future<bool> Function() isOnline,
|
||||
bool alwaysRefresh = false,
|
||||
String? tag,
|
||||
}) async* {
|
||||
// Tracks whether we've already kicked off a stale-while-revalidate
|
||||
// refresh for this stream subscription, so we don't fire one on every
|
||||
// drift re-emission (otherwise the populate cycles forever).
|
||||
var revalidated = false;
|
||||
void log(String msg) {
|
||||
if (tag != null) debugPrint('cacheFirst[$tag]: $msg');
|
||||
}
|
||||
|
||||
await for (final rows in driftStream) {
|
||||
if (rows.isNotEmpty) {
|
||||
log('drift hit (${rows.length} rows)');
|
||||
yield toResult(rows);
|
||||
// Stale-while-revalidate: yield cache immediately, then kick off
|
||||
// a REST refresh in the background. Drift watch() picks up the
|
||||
@@ -55,15 +62,20 @@ Stream<T> cacheFirst<D, T>({
|
||||
}
|
||||
continue;
|
||||
}
|
||||
log('drift miss; checking connectivity');
|
||||
if (await isOnline()) {
|
||||
log('online; calling fetchAndPopulate');
|
||||
try {
|
||||
await fetchAndPopulate();
|
||||
log('fetchAndPopulate done; awaiting drift re-emit');
|
||||
// The drift watch() stream re-emits with the populated rows on
|
||||
// the next loop iteration. Don't yield here.
|
||||
} catch (_) {
|
||||
} catch (e, st) {
|
||||
log('fetchAndPopulate failed: $e\n$st');
|
||||
yield toResult(rows); // empty result; caller surfaces error
|
||||
}
|
||||
} else {
|
||||
log('offline; yielding empty');
|
||||
yield toResult(rows); // empty result; offline
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user