Compare commits
2 Commits
231887c3b7
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 89cf66f28a | |||
| f5a6b919aa |
@@ -5,6 +5,15 @@ import { REHYDRATE_API, type RehydrateApiPayload } from './rehydrate';
|
|||||||
export const api = createApi({
|
export const api = createApi({
|
||||||
reducerPath: 'api',
|
reducerPath: 'api',
|
||||||
baseQuery: baseQueryWithReauth,
|
baseQuery: baseQueryWithReauth,
|
||||||
|
// Stale-while-revalidate. The Tier-2 rehydrated cache (below) seeds fulfilled
|
||||||
|
// entries at startup, which would otherwise make RTKQ serve stale data and
|
||||||
|
// never hit the network. These flags keep showing the cached snapshot
|
||||||
|
// instantly but silently refetch from the server whenever it's reachable —
|
||||||
|
// on mount/arg change, on reconnect, and on window refocus. The result: the
|
||||||
|
// server is the source of truth when online; the cache is only a fallback.
|
||||||
|
refetchOnMountOrArgChange: true,
|
||||||
|
refetchOnReconnect: true,
|
||||||
|
refetchOnFocus: true,
|
||||||
tagTypes: [
|
tagTypes: [
|
||||||
'Track',
|
'Track',
|
||||||
'Album',
|
'Album',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@@ -54,8 +54,14 @@ export function LibraryPage() {
|
|||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [debouncedSearch] = useDebounce(search, 300);
|
const [debouncedSearch] = useDebounce(search, 300);
|
||||||
|
|
||||||
|
// Poll while any listed track is still being enriched, then stop. Enrichment
|
||||||
|
// runs asynchronously in a worker after import/upload; without this the row
|
||||||
|
// stays stuck on "Identifying metadata…" until something else invalidates the
|
||||||
|
// Track tag. Cleared to 0 once nothing is pending (and while offline).
|
||||||
|
const [tracksPollMs, setTracksPollMs] = useState(0);
|
||||||
const tracksQuery = useGetTracksQuery(
|
const tracksQuery = useGetTracksQuery(
|
||||||
debouncedSearch ? { search } : undefined,
|
debouncedSearch ? { search } : undefined,
|
||||||
|
{ pollingInterval: tracksPollMs },
|
||||||
);
|
);
|
||||||
const albumsQuery = useGetAlbumsQuery(
|
const albumsQuery = useGetAlbumsQuery(
|
||||||
debouncedSearch ? { search } : undefined,
|
debouncedSearch ? { search } : undefined,
|
||||||
@@ -73,6 +79,14 @@ export function LibraryPage() {
|
|||||||
const localArtists = useAppSelector(selectLocalArtists);
|
const localArtists = useAppSelector(selectLocalArtists);
|
||||||
const q = debouncedSearch.trim().toLowerCase();
|
const q = debouncedSearch.trim().toLowerCase();
|
||||||
|
|
||||||
|
const anyPending =
|
||||||
|
!offline &&
|
||||||
|
(tracksQuery.data?.items.some((tr) => tr.metadataStatus === 'pending') ??
|
||||||
|
false);
|
||||||
|
useEffect(() => {
|
||||||
|
setTracksPollMs(anyPending ? 4000 : 0);
|
||||||
|
}, [anyPending]);
|
||||||
|
|
||||||
// Live server data wins; offline we fall back to the locally-composed list.
|
// Live server data wins; offline we fall back to the locally-composed list.
|
||||||
const tracksToShow =
|
const tracksToShow =
|
||||||
tracksQuery.data?.items ??
|
tracksQuery.data?.items ??
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
|
import { setupListeners } from '@reduxjs/toolkit/query';
|
||||||
import { api } from '../api';
|
import { api } from '../api';
|
||||||
import authReducer from './slices/auth';
|
import authReducer from './slices/auth';
|
||||||
import connectionReducer from './slices/connection';
|
import connectionReducer from './slices/connection';
|
||||||
@@ -27,6 +28,10 @@ export const store = configureStore({
|
|||||||
getDefaultMiddleware().concat(api.middleware),
|
getDefaultMiddleware().concat(api.middleware),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enable refetchOnReconnect / refetchOnFocus by dispatching the browser's
|
||||||
|
// online + focus events into RTKQ (no-op without the api flags set in api/index).
|
||||||
|
setupListeners(store.dispatch);
|
||||||
|
|
||||||
// Flush queue/player changes back to localStorage (throttled).
|
// Flush queue/player changes back to localStorage (throttled).
|
||||||
startPersistence(store);
|
startPersistence(store);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user