fix(web): refresh Home on explicit Home click

loadHomeData now takes a force flag; navigating to Home via a click
(not back/forward) invalidates the home rows' API cache so they re-fetch
and the random row re-randomizes — matching the Android re-tap behavior.

CHANGELOG: note the Home-refresh fix under 1.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-06-26 08:19:38 +02:00
1 parent 211b8ec6c2
commit 58dacbabc3
3 files changed
+11 -9

No files matched your search

+2 -1
View File
@@ -70,6 +70,7 @@ Versioning: two-part `MAJOR.MINOR` — each release bumps the minor (features an
- **Android** — Download crash on Android 15 (targetSdk 35): `FOREGROUND_SERVICE_TYPE_DATA_SYNC` is banned for apps targeting API 35; WorkManager's expedited path used it and the resulting exception killed the app ~1.5 s after tapping download. Fixed by dropping `setExpedited()` and all foreground-service use from `DownloadWorker` — downloads run as silent background work with progress shown in the Offline tab
- **Android** — Offline detail screen flashed open then immediately returned: its initial empty state (before Room emitted) was mistaken for "deleted". Now shows a brief spinner until loaded, and only leaves when the last song is actually removed
- **Android** — Lyrics autoscroll: rewrote centering to use the active line's *measured* position in a `LazyColumn` instead of a fixed line-height estimate. Eliminates the stutter and the downward drift on wrapped (multi-line) lyrics — the active line now tracks smoothly and sits at a natural ~40 %-from-top reading anchor
- **Web** — Home now refreshes on an explicit Home click (matching Android): the album rows re-fetch and the random row re-randomizes instead of serving the 5-minute cache. Back/forward navigation still keeps the cached state
### Internal
- **Android** — Offline system: `Room` DB (`novasonic_offline.db`) with `downloaded_tracks`, `download_groups`, `group_track_cross_refs`; `OfflineRepository` holds hot `StateFlow` maps for O(1) local-path lookups; `DownloadWorker` streams audio + cover art via `HttpURLConnection` to `context.filesDir/offline/{audio|covers}/`. `MusicRepository.streamUrl()` checks local files first, so playback uses the `file://` URI on every entry point
@@ -306,4 +307,4 @@ Primary causes identified:
### Known Bugs
- **Backend (download)** — The duplicate check requires at least a `firstArtist` folder match. If the same song was previously stored under a very different artist name (e.g. before an iTunes-name fix), it will not be detected as a duplicate and will be re-downloaded.
- **Backend (metadata/sorting)** — MusicBrainz `release['artist-credit']` is often absent in the standard recording search response (requires `inc=artist-credits` which can slow the endpoint). The albumArtist field from MusicBrainz autofill may fall back to the recording artist rather than the release artist.
- **Backend (metadata/sorting)** — MBIDs are not embedded in downloaded tracks; Navidrome cannot use them for authoritative album grouping (low priority — album splitting is mostly solved via consistent `album_artist`).
- **Backend (metadata/sorting)** — MBIDs are not embedded in downloaded tracks; Navidrome cannot use them for authoritative album grouping (low priority — album splitting is mostly solved via consistent `album_artist`).
+8 -7
View File
@@ -1,6 +1,6 @@
import { S } from '../state.js';
import { esc } from '../utils.js';
import { api, apiCached, apiIsCached, coverUrl, fetchRomajiIndex, dispArtistById, dispAlbumName, cacheArtistSortNames } from '../api.js';
import { api, apiCached, apiIsCached, invalidateApiCache, coverUrl, fetchRomajiIndex, dispArtistById, dispAlbumName, cacheArtistSortNames } from '../api.js';
import { makeCard, makeAlbumCard, makeSongItem } from '../ui/components.js';
import { playQueue } from '../player/player.js';
// Circular — safe (called inside event handlers)
@@ -32,7 +32,7 @@ const _greetN = [
'Time to escape into music', 'Pick something great',
];
export async function loadHomeData() {
export async function loadHomeData(force = false) {
const name = S.cfg.user || '';
const g = name
? `${_greetW[Math.floor(Math.random() * _greetW.length)]} ${name}`
@@ -42,10 +42,10 @@ export async function loadHomeData() {
loadHeroCard();
loadGenreSection();
loadAlbumRow('home-newest', 'getAlbumList2', '&type=newest&size=20');
loadAlbumRow('home-recent', 'getAlbumList2', '&type=recent&size=20');
loadAlbumRow('home-random', 'getAlbumList2', '&type=random&size=20');
loadAlbumRow('home-frequent', 'getAlbumList2', '&type=frequent&size=20');
loadAlbumRow('home-newest', 'getAlbumList2', '&type=newest&size=20', force);
loadAlbumRow('home-recent', 'getAlbumList2', '&type=recent&size=20', force);
loadAlbumRow('home-random', 'getAlbumList2', '&type=random&size=20', force);
loadAlbumRow('home-frequent', 'getAlbumList2', '&type=frequent&size=20', force);
}
async function loadHeroCard() {
@@ -194,9 +194,10 @@ export async function loadGenreDetail(g) {
songs.forEach((s, i) => list.appendChild(makeSongItem(s, i, songs)));
}
export async function loadAlbumRow(id, method, extra) {
export async function loadAlbumRow(id, method, extra, force = false) {
const c = document.getElementById(id);
if(!c) return;
if (force) invalidateApiCache(method, extra); // re-fetch (re-randomizes) on explicit Home click
if (!apiIsCached(method, extra)) c.innerHTML = '<div class="spinner"></div>';
try {
const [r] = await Promise.all([
+1 -1
View File
@@ -48,7 +48,7 @@ export function navigate(page, data=null) {
if(['discover','new-releases','recommendations','random','spotify-importer'].includes(page)) switchNavMode('discover');
else if(['home','artists','albums','starred','playlists'].includes(page)) switchNavMode('library');
if(page==='home') loadHomeData();
if(page==='home') loadHomeData(!S._navJumping); // explicit Home click refreshes; back/forward keeps state
if(page==='artists' && !document.getElementById('artists-grid').children.length) loadArtists();
if(page==='albums' && !document.getElementById('albums-grid').children.length) loadAlbums();
if(page==='starred') loadStarred();