5511f87b4b
Liked tab loaded into an infinite spinner when the user had likes in one category but not all three. Root cause: the three liked-tab providers share one _populateLikeIds function. When the populate writes track rows, drift watch fires for cached_likes (the table all three providers watch). The track provider's stream re-emits with rows.isNotEmpty → yields populated. The album and artist streams re-emit with rows.isEmpty (user has no album/artist likes), re-enter cacheFirst's rows-empty branch, fire populate AGAIN, drift fires again, repeat — never yielding, .isLoading stays true forever, UI spins. Generalises beyond the liked case: any cacheFirst with a populate that writes to a watched table but produces no rows matching this filter would loop. Fix tracks coldFetchAttempted per subscription so the first fetch is the only fetch via the rows-empty branch; subsequent empty emissions yield empty. Also yields current rows after a successful populate so a true no-op fetchAndPopulate (server genuinely empty, fresh-install with no library data) doesn't hang when drift doesn't re-emit for an empty batch. For populated cases, the order is: spinner → brief empty yield from the post-populate yield → drift watch re-emits with rows → populated. UI flashes empty for one frame. Acceptable trade-off for the no-spin guarantee. Also matches the timeout pattern: liked providers' isOnline gains the same 3-second timeout the home/library-list providers already had, so a stuck connectivity check can't extend the hang.