mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-16 08:24:16 +02:00
fix: preserve infinite list cache on component remount (fixes random sort reshuffling) (#2097)
* fix: preserve infinite list cache on component remount When browsing with random sort, navigating to a detail view and coming back would reshuffle the list. This happens because the list component unmounts, losing its internal ref guard, and the reset effect re-fetches all pages — returning a new random order from the server.
This commit is contained in:
@@ -13,7 +13,7 @@ import { useListContext } from '/@/renderer/context/list-context';
|
|||||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||||
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
|
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
|
||||||
import { getListRefreshMutationKey } from '/@/renderer/features/shared/components/list-refresh-button';
|
import { getListRefreshMutationKey } from '/@/renderer/features/shared/components/list-refresh-button';
|
||||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
import { LibraryItem, SortKeyRandom } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
export const getListQueryKeyName = (itemType: LibraryItem): string => {
|
export const getListQueryKeyName = (itemType: LibraryItem): string => {
|
||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
@@ -108,8 +108,19 @@ export const useItemListInfiniteLoader = ({
|
|||||||
[serverId, itemType, query],
|
[serverId, itemType, query],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isRandomSort = query?.sortBy === SortKeyRandom;
|
||||||
|
|
||||||
const fetchPage = useCallback(
|
const fetchPage = useCallback(
|
||||||
async (pageNumber: number) => {
|
async (pageNumber: number) => {
|
||||||
|
if (isRandomSort) {
|
||||||
|
const existingData =
|
||||||
|
queryClient.getQueryData<InfiniteLoaderCacheData>(dataQueryKey);
|
||||||
|
if (existingData?.pagesLoaded?.[pageNumber]) {
|
||||||
|
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const startIndex = pageNumber * itemsPerPage;
|
const startIndex = pageNumber * itemsPerPage;
|
||||||
const queryParams = {
|
const queryParams = {
|
||||||
limit: itemsPerPage,
|
limit: itemsPerPage,
|
||||||
@@ -118,6 +129,7 @@ export const useItemListInfiniteLoader = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const result = await queryClient.fetchQuery({
|
const result = await queryClient.fetchQuery({
|
||||||
|
gcTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
|
||||||
queryFn: async ({ signal }) => {
|
queryFn: async ({ signal }) => {
|
||||||
const result = await listQueryFn({
|
const result = await listQueryFn({
|
||||||
apiClientProps: { serverId, signal },
|
apiClientProps: { serverId, signal },
|
||||||
@@ -127,6 +139,7 @@ export const useItemListInfiniteLoader = ({
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
queryKey: queryKeys[getListQueryKeyName(itemType)].list(serverId, queryParams),
|
queryKey: queryKeys[getListQueryKeyName(itemType)].list(serverId, queryParams),
|
||||||
|
staleTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the query data with the fetched page
|
// Update the query data with the fetched page
|
||||||
@@ -154,13 +167,32 @@ export const useItemListInfiniteLoader = ({
|
|||||||
// Track the last fetched page
|
// Track the last fetched page
|
||||||
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
|
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
|
||||||
},
|
},
|
||||||
[itemsPerPage, query, queryClient, serverId, dataQueryKey, listQueryFn, itemType],
|
[
|
||||||
|
itemsPerPage,
|
||||||
|
query,
|
||||||
|
queryClient,
|
||||||
|
serverId,
|
||||||
|
dataQueryKey,
|
||||||
|
listQueryFn,
|
||||||
|
itemType,
|
||||||
|
isRandomSort,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reset the loaded pages and refetch current page when the query changes
|
// Reset the loaded pages and refetch current page when the query changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentDataQueryKey = JSON.stringify(dataQueryKey);
|
const currentDataQueryKey = JSON.stringify(dataQueryKey);
|
||||||
|
|
||||||
|
if (isRandomSort) {
|
||||||
|
const existingData = queryClient.getQueryData<InfiniteLoaderCacheData | undefined>(
|
||||||
|
dataQueryKey,
|
||||||
|
);
|
||||||
|
if (existingData?.dataMap && existingData.dataMap.size > 0) {
|
||||||
|
previousDataQueryKeyRef.current = currentDataQueryKey;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (previousDataQueryKeyRef.current === currentDataQueryKey || isRefetchingRef.current) {
|
if (previousDataQueryKeyRef.current === currentDataQueryKey || isRefetchingRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { useListContext } from '/@/renderer/context/list-context';
|
|||||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||||
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
|
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
|
||||||
import { getListRefreshMutationKey } from '/@/renderer/features/shared/components/list-refresh-button';
|
import { getListRefreshMutationKey } from '/@/renderer/features/shared/components/list-refresh-button';
|
||||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
import { LibraryItem, SortKeyRandom } from '/@/shared/types/domain-types';
|
||||||
|
|
||||||
const getQueryKeyName = (itemType: LibraryItem): string => {
|
const getQueryKeyName = (itemType: LibraryItem): string => {
|
||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
@@ -76,6 +76,8 @@ export const useItemListPaginatedLoader = ({
|
|||||||
const fetchRange = getFetchRange(currentPage, itemsPerPage);
|
const fetchRange = getFetchRange(currentPage, itemsPerPage);
|
||||||
const startIndex = fetchRange.startIndex;
|
const startIndex = fetchRange.startIndex;
|
||||||
|
|
||||||
|
const isRandomSort = query?.sortBy === SortKeyRandom;
|
||||||
|
|
||||||
const queryParams = useMemo(
|
const queryParams = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
limit: itemsPerPage,
|
limit: itemsPerPage,
|
||||||
@@ -86,7 +88,7 @@ export const useItemListPaginatedLoader = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
gcTime: 1000 * 15,
|
gcTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
|
||||||
placeholderData: { items: getInitialData(itemsPerPage) },
|
placeholderData: { items: getInitialData(itemsPerPage) },
|
||||||
queryFn: async ({ signal }) => {
|
queryFn: async ({ signal }) => {
|
||||||
const result = await listQueryFn({
|
const result = await listQueryFn({
|
||||||
@@ -97,7 +99,7 @@ export const useItemListPaginatedLoader = ({
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
queryKey: queryKeys[getQueryKeyName(itemType)].list(serverId, queryParams),
|
queryKey: queryKeys[getQueryKeyName(itemType)].list(serverId, queryParams),
|
||||||
staleTime: 1000 * 15,
|
staleTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshMutation = useMutation({
|
const refreshMutation = useMutation({
|
||||||
|
|||||||
@@ -465,6 +465,8 @@ export const tagListSortMap: TagListSortMap = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SortKeyRandom = 'random';
|
||||||
|
|
||||||
export enum AlbumListSort {
|
export enum AlbumListSort {
|
||||||
ALBUM_ARTIST = 'albumArtist',
|
ALBUM_ARTIST = 'albumArtist',
|
||||||
ARTIST = 'artist',
|
ARTIST = 'artist',
|
||||||
@@ -476,7 +478,7 @@ export enum AlbumListSort {
|
|||||||
ID = 'id',
|
ID = 'id',
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
PLAY_COUNT = 'playCount',
|
PLAY_COUNT = 'playCount',
|
||||||
RANDOM = 'random',
|
RANDOM = SortKeyRandom,
|
||||||
RATING = 'rating',
|
RATING = 'rating',
|
||||||
RECENTLY_ADDED = 'recentlyAdded',
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
RECENTLY_PLAYED = 'recentlyPlayed',
|
RECENTLY_PLAYED = 'recentlyPlayed',
|
||||||
@@ -598,7 +600,7 @@ export enum SongListSort {
|
|||||||
ID = 'id',
|
ID = 'id',
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
PLAY_COUNT = 'playCount',
|
PLAY_COUNT = 'playCount',
|
||||||
RANDOM = 'random',
|
RANDOM = SortKeyRandom,
|
||||||
RATING = 'rating',
|
RATING = 'rating',
|
||||||
RECENTLY_ADDED = 'recentlyAdded',
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
RECENTLY_PLAYED = 'recentlyPlayed',
|
RECENTLY_PLAYED = 'recentlyPlayed',
|
||||||
@@ -725,7 +727,7 @@ export enum AlbumArtistListSort {
|
|||||||
FAVORITED = 'favorited',
|
FAVORITED = 'favorited',
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
PLAY_COUNT = 'playCount',
|
PLAY_COUNT = 'playCount',
|
||||||
RANDOM = 'random',
|
RANDOM = SortKeyRandom,
|
||||||
RATING = 'rating',
|
RATING = 'rating',
|
||||||
RECENTLY_ADDED = 'recentlyAdded',
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
RELEASE_DATE = 'releaseDate',
|
RELEASE_DATE = 'releaseDate',
|
||||||
@@ -814,7 +816,7 @@ export enum ArtistListSort {
|
|||||||
FAVORITED = 'favorited',
|
FAVORITED = 'favorited',
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
PLAY_COUNT = 'playCount',
|
PLAY_COUNT = 'playCount',
|
||||||
RANDOM = 'random',
|
RANDOM = SortKeyRandom,
|
||||||
RATING = 'rating',
|
RATING = 'rating',
|
||||||
RECENTLY_ADDED = 'recentlyAdded',
|
RECENTLY_ADDED = 'recentlyAdded',
|
||||||
RELEASE_DATE = 'releaseDate',
|
RELEASE_DATE = 'releaseDate',
|
||||||
|
|||||||
Reference in New Issue
Block a user