mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-06 20:10:12 +02:00
re-add useTransition to router
This commit is contained in:
@@ -121,7 +121,6 @@
|
|||||||
"mpris-service": "^2.1.2",
|
"mpris-service": "^2.1.2",
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"node-mpv": "github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f",
|
"node-mpv": "github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f",
|
||||||
"nuqs": "^2.8.9",
|
|
||||||
"overlayscrollbars": "^2.14.0",
|
"overlayscrollbars": "^2.14.0",
|
||||||
"overlayscrollbars-react": "^0.5.6",
|
"overlayscrollbars-react": "^0.5.6",
|
||||||
"qs": "^6.15.0",
|
"qs": "^6.15.0",
|
||||||
|
|||||||
Generated
-51
@@ -156,9 +156,6 @@ importers:
|
|||||||
node-mpv:
|
node-mpv:
|
||||||
specifier: github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f
|
specifier: github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f
|
||||||
version: https://codeload.github.com/jeffvli/Node-MPV/tar.gz/32b4d64395289ad710c41d481d2707a7acfc228f
|
version: https://codeload.github.com/jeffvli/Node-MPV/tar.gz/32b4d64395289ad710c41d481d2707a7acfc228f
|
||||||
nuqs:
|
|
||||||
specifier: ^2.8.9
|
|
||||||
version: 2.8.9(react-router-dom@7.9.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-router@7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
|
|
||||||
overlayscrollbars:
|
overlayscrollbars:
|
||||||
specifier: ^2.14.0
|
specifier: ^2.14.0
|
||||||
version: 2.14.0
|
version: 2.14.0
|
||||||
@@ -2018,9 +2015,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
'@standard-schema/spec@1.0.0':
|
|
||||||
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
|
||||||
|
|
||||||
'@surma/rollup-plugin-off-main-thread@2.2.3':
|
'@surma/rollup-plugin-off-main-thread@2.2.3':
|
||||||
resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
|
resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
|
||||||
|
|
||||||
@@ -4283,27 +4277,6 @@ packages:
|
|||||||
nth-check@2.1.1:
|
nth-check@2.1.1:
|
||||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||||
|
|
||||||
nuqs@2.8.9:
|
|
||||||
resolution: {integrity: sha512-8ou6AEwsxMWSYo2qkfZtYFVzngwbKmg4c00HVxC1fF6CEJv3Fwm6eoZmfVPALB+vw8Udo7KL5uy96PFcYe1BIQ==}
|
|
||||||
peerDependencies:
|
|
||||||
'@remix-run/react': '>=2'
|
|
||||||
'@tanstack/react-router': ^1
|
|
||||||
next: '>=14.2.0'
|
|
||||||
react: '>=18.2.0 || ^19.0.0-0'
|
|
||||||
react-router: 7.14.0
|
|
||||||
react-router-dom: ^5 || ^6 || ^7
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@remix-run/react':
|
|
||||||
optional: true
|
|
||||||
'@tanstack/react-router':
|
|
||||||
optional: true
|
|
||||||
next:
|
|
||||||
optional: true
|
|
||||||
react-router:
|
|
||||||
optional: true
|
|
||||||
react-router-dom:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
object-assign@4.1.1:
|
object-assign@4.1.1:
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -4710,13 +4683,6 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
react-router-dom@7.9.4:
|
|
||||||
resolution: {integrity: sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==}
|
|
||||||
engines: {node: '>=20.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=18'
|
|
||||||
react-dom: '>=18'
|
|
||||||
|
|
||||||
react-router@7.14.0:
|
react-router@7.14.0:
|
||||||
resolution: {integrity: sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==}
|
resolution: {integrity: sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==}
|
||||||
engines: {node: '>=20.0.0'}
|
engines: {node: '>=20.0.0'}
|
||||||
@@ -7645,8 +7611,6 @@ snapshots:
|
|||||||
|
|
||||||
'@sindresorhus/is@4.6.0': {}
|
'@sindresorhus/is@4.6.0': {}
|
||||||
|
|
||||||
'@standard-schema/spec@1.0.0': {}
|
|
||||||
|
|
||||||
'@surma/rollup-plugin-off-main-thread@2.2.3':
|
'@surma/rollup-plugin-off-main-thread@2.2.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
ejs: 3.1.10
|
ejs: 3.1.10
|
||||||
@@ -10304,14 +10268,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
boolbase: 1.0.0
|
boolbase: 1.0.0
|
||||||
|
|
||||||
nuqs@2.8.9(react-router-dom@7.9.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-router@7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4):
|
|
||||||
dependencies:
|
|
||||||
'@standard-schema/spec': 1.0.0
|
|
||||||
react: 19.2.4
|
|
||||||
optionalDependencies:
|
|
||||||
react-router: 7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
|
||||||
react-router-dom: 7.9.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
|
||||||
|
|
||||||
object-assign@4.1.1: {}
|
object-assign@4.1.1: {}
|
||||||
|
|
||||||
object-inspect@1.13.4: {}
|
object-inspect@1.13.4: {}
|
||||||
@@ -10695,13 +10651,6 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.14
|
'@types/react': 19.2.14
|
||||||
|
|
||||||
react-router-dom@7.9.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
|
||||||
dependencies:
|
|
||||||
react: 19.2.4
|
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
|
||||||
react-router: 7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
react-router@7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
react-router@7.14.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
cookie: 1.1.1
|
cookie: 1.1.1
|
||||||
|
|||||||
@@ -349,9 +349,12 @@ export const useItemListInfiniteLoader = ({
|
|||||||
mutationKey: getListRefreshMutationKey(eventKey),
|
mutationKey: getListRefreshMutationKey(eventKey),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const refreshMutationRef = useRef(refreshMutation);
|
||||||
|
refreshMutationRef.current = refreshMutation;
|
||||||
|
|
||||||
const refresh = useCallback(
|
const refresh = useCallback(
|
||||||
async (force?: boolean) => refreshMutation.mutateAsync(force),
|
async (force?: boolean) => refreshMutationRef.current.mutateAsync(force),
|
||||||
[refreshMutation],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateItems = useCallback(
|
const updateItems = useCallback(
|
||||||
@@ -383,7 +386,7 @@ export const useItemListInfiniteLoader = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshMutation.mutate(true);
|
refreshMutationRef.current.mutate(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
eventEmitter.on('ITEM_LIST_REFRESH', handleRefresh);
|
eventEmitter.on('ITEM_LIST_REFRESH', handleRefresh);
|
||||||
@@ -391,7 +394,7 @@ export const useItemListInfiniteLoader = ({
|
|||||||
return () => {
|
return () => {
|
||||||
eventEmitter.off('ITEM_LIST_REFRESH', handleRefresh);
|
eventEmitter.off('ITEM_LIST_REFRESH', handleRefresh);
|
||||||
};
|
};
|
||||||
}, [eventKey, refreshMutation]);
|
}, [eventKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleFavorite = (payload: UserFavoriteEventPayload) => {
|
const handleFavorite = (payload: UserFavoriteEventPayload) => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
useSuspenseQuery,
|
useSuspenseQuery,
|
||||||
UseSuspenseQueryOptions,
|
UseSuspenseQueryOptions,
|
||||||
} from '@tanstack/react-query';
|
} from '@tanstack/react-query';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||||
import { useListContext } from '/@/renderer/context/list-context';
|
import { useListContext } from '/@/renderer/context/list-context';
|
||||||
@@ -115,6 +115,9 @@ export const useItemListPaginatedLoader = ({
|
|||||||
mutationKey: getListRefreshMutationKey(eventKey ?? 'paginated'),
|
mutationKey: getListRefreshMutationKey(eventKey ?? 'paginated'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const refreshMutationRef = useRef(refreshMutation);
|
||||||
|
refreshMutationRef.current = refreshMutation;
|
||||||
|
|
||||||
const updateItems = useCallback(
|
const updateItems = useCallback(
|
||||||
(indexes: number[], value: object) => {
|
(indexes: number[], value: object) => {
|
||||||
return queryClient.setQueryData(
|
return queryClient.setQueryData(
|
||||||
@@ -153,7 +156,7 @@ export const useItemListPaginatedLoader = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshMutation.mutate(true);
|
refreshMutationRef.current.mutate(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFavorite = (payload: UserFavoriteEventPayload) => {
|
const handleFavorite = (payload: UserFavoriteEventPayload) => {
|
||||||
@@ -220,7 +223,7 @@ export const useItemListPaginatedLoader = ({
|
|||||||
eventEmitter.off('USER_FAVORITE', handleFavorite);
|
eventEmitter.off('USER_FAVORITE', handleFavorite);
|
||||||
eventEmitter.off('USER_RATING', handleRating);
|
eventEmitter.off('USER_RATING', handleRating);
|
||||||
};
|
};
|
||||||
}, [data, eventKey, itemType, refreshMutation, serverId, updateItems]);
|
}, [data, eventKey, itemType, serverId, updateItems]);
|
||||||
|
|
||||||
return { data: data?.items || [], pageCount, totalItemCount };
|
return { data: data?.items || [], pageCount, totalItemCount };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
setMultipleSearchParams,
|
setMultipleSearchParams,
|
||||||
setSearchParam,
|
setSearchParam,
|
||||||
} from '/@/renderer/utils/query-params';
|
} from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { AlbumListSort, SortOrder } from '/@/shared/types/domain-types';
|
import { AlbumListSort, SortOrder } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -74,8 +75,10 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setGenreId = useCallback(
|
const setGenreId = useCallback(
|
||||||
(value: null | string[]) => {
|
(value: null | string[]) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.GENRE_ID, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.GENRE_ID, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -83,8 +86,13 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setAlbumArtist = useCallback(
|
const setAlbumArtist = useCallback(
|
||||||
(value: null | string[]) => {
|
(value: null | string[]) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.ARTIST_IDS, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams(
|
||||||
|
(prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.ARTIST_IDS, value),
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -92,8 +100,10 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setMinYear = useCallback(
|
const setMinYear = useCallback(
|
||||||
(value: null | number) => {
|
(value: null | number) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.MIN_YEAR, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.MIN_YEAR, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -101,8 +111,10 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setMaxYear = useCallback(
|
const setMaxYear = useCallback(
|
||||||
(value: null | number) => {
|
(value: null | number) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.MAX_YEAR, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.MAX_YEAR, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -110,8 +122,10 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setFavorite = useCallback(
|
const setFavorite = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.FAVORITE, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.FAVORITE, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -119,8 +133,13 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setCompilation = useCallback(
|
const setCompilation = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.COMPILATION, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams(
|
||||||
|
(prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.COMPILATION, value),
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -128,8 +147,13 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setHasRating = useCallback(
|
const setHasRating = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.HAS_RATING, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams(
|
||||||
|
(prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.HAS_RATING, value),
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -137,65 +161,71 @@ export const useAlbumListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setRecentlyPlayed = useCallback(
|
const setRecentlyPlayed = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.RECENTLY_PLAYED, value),
|
setSearchParams(
|
||||||
{
|
(prev) => setSearchParam(prev, FILTER_KEYS.ALBUM.RECENTLY_PLAYED, value),
|
||||||
replace: true,
|
{
|
||||||
},
|
replace: true,
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const setCustom = useCallback(
|
const setCustom = useCallback(
|
||||||
(value: null | Record<string, any>) => {
|
(value: null | Record<string, any>) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const previousValue = prev.get(FILTER_KEYS.ALBUM._CUSTOM);
|
(prev) => {
|
||||||
|
const previousValue = prev.get(FILTER_KEYS.ALBUM._CUSTOM);
|
||||||
|
|
||||||
const newCustom = {
|
const newCustom = {
|
||||||
...(previousValue ? JSON.parse(previousValue) : {}),
|
...(previousValue ? JSON.parse(previousValue) : {}),
|
||||||
...value,
|
...value,
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredNewCustom = Object.fromEntries(
|
const filteredNewCustom = Object.fromEntries(
|
||||||
Object.entries(newCustom).filter(
|
Object.entries(newCustom).filter(
|
||||||
([, value]) => value !== null && value !== undefined,
|
([, value]) => value !== null && value !== undefined,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
prev.set(FILTER_KEYS.ALBUM._CUSTOM, JSON.stringify(filteredNewCustom));
|
prev.set(FILTER_KEYS.ALBUM._CUSTOM, JSON.stringify(filteredNewCustom));
|
||||||
return prev;
|
return prev;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const clear = useCallback(() => {
|
const clear = useCallback(() => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) =>
|
setSearchParams(
|
||||||
setMultipleSearchParams(
|
(prev) =>
|
||||||
prev,
|
setMultipleSearchParams(
|
||||||
{
|
prev,
|
||||||
[FILTER_KEYS.ALBUM._CUSTOM]: null,
|
{
|
||||||
[FILTER_KEYS.ALBUM.ARTIST_IDS]: null,
|
[FILTER_KEYS.ALBUM._CUSTOM]: null,
|
||||||
[FILTER_KEYS.ALBUM.COMPILATION]: null,
|
[FILTER_KEYS.ALBUM.ARTIST_IDS]: null,
|
||||||
[FILTER_KEYS.ALBUM.FAVORITE]: null,
|
[FILTER_KEYS.ALBUM.COMPILATION]: null,
|
||||||
[FILTER_KEYS.ALBUM.GENRE_ID]: null,
|
[FILTER_KEYS.ALBUM.FAVORITE]: null,
|
||||||
[FILTER_KEYS.ALBUM.HAS_RATING]: null,
|
[FILTER_KEYS.ALBUM.GENRE_ID]: null,
|
||||||
[FILTER_KEYS.ALBUM.MAX_YEAR]: null,
|
[FILTER_KEYS.ALBUM.HAS_RATING]: null,
|
||||||
[FILTER_KEYS.ALBUM.MIN_YEAR]: null,
|
[FILTER_KEYS.ALBUM.MAX_YEAR]: null,
|
||||||
[FILTER_KEYS.ALBUM.RECENTLY_PLAYED]: null,
|
[FILTER_KEYS.ALBUM.MIN_YEAR]: null,
|
||||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: null,
|
[FILTER_KEYS.ALBUM.RECENTLY_PLAYED]: null,
|
||||||
},
|
[FILTER_KEYS.SHARED.SEARCH_TERM]: null,
|
||||||
new Set([FILTER_KEYS.ALBUM._CUSTOM]),
|
},
|
||||||
),
|
new Set([FILTER_KEYS.ALBUM._CUSTOM]),
|
||||||
{ replace: true },
|
),
|
||||||
);
|
{ replace: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
}, [setSearchParams]);
|
}, [setSearchParams]);
|
||||||
|
|
||||||
const query = useMemo(
|
const query = useMemo(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-f
|
|||||||
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
||||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||||
import { setMultipleSearchParams } from '/@/renderer/utils/query-params';
|
import { setMultipleSearchParams } from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { AlbumArtistListSort } from '/@/shared/types/domain-types';
|
import { AlbumArtistListSort } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -19,13 +20,15 @@ export const useAlbumArtistListFilters = () => {
|
|||||||
const [, setSearchParams] = useSearchParams();
|
const [, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
const clear = useCallback(() => {
|
const clear = useCallback(() => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) =>
|
setSearchParams(
|
||||||
setMultipleSearchParams(prev, {
|
(prev) =>
|
||||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: null,
|
setMultipleSearchParams(prev, {
|
||||||
}),
|
[FILTER_KEYS.SHARED.SEARCH_TERM]: null,
|
||||||
{ replace: true },
|
}),
|
||||||
);
|
{ replace: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
}, [setSearchParams]);
|
}, [setSearchParams]);
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-f
|
|||||||
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
||||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||||
import { parseJsonParam, setJsonSearchParam } from '/@/renderer/utils/query-params';
|
import { parseJsonParam, setJsonSearchParam } from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { SongListSort, SortOrder } from '/@/shared/types/domain-types';
|
import { SongListSort, SortOrder } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -29,13 +30,19 @@ export const useFolderListFilters = () => {
|
|||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
const setFolderPath = (path: FolderPathItem[]) => {
|
const setFolderPath = (path: FolderPathItem[]) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const newParams = setJsonSearchParam(prev, FILTER_KEYS.FOLDER.FOLDER_PATH, path);
|
(prev) => {
|
||||||
return newParams;
|
const newParams = setJsonSearchParam(
|
||||||
},
|
prev,
|
||||||
{ replace: false },
|
FILTER_KEYS.FOLDER.FOLDER_PATH,
|
||||||
);
|
path,
|
||||||
|
);
|
||||||
|
return newParams;
|
||||||
|
},
|
||||||
|
{ replace: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigate to a folder (adds to path)
|
// Navigate to a folder (adds to path)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useSortByFilter } from '/@/renderer/features/shared/hooks/use-sort-by-f
|
|||||||
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
import { useSortOrderFilter } from '/@/renderer/features/shared/hooks/use-sort-order-filter';
|
||||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||||
import { parseCustomFiltersParam } from '/@/renderer/utils/query-params';
|
import { parseCustomFiltersParam } from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { PlaylistListSort } from '/@/shared/types/domain-types';
|
import { PlaylistListSort } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -24,28 +25,30 @@ export const usePlaylistListFilters = () => {
|
|||||||
|
|
||||||
const setCustom = useCallback(
|
const setCustom = useCallback(
|
||||||
(value: null | Record<string, any>) => {
|
(value: null | Record<string, any>) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const previousValue = prev.get(FILTER_KEYS.ALBUM._CUSTOM);
|
(prev) => {
|
||||||
|
const previousValue = prev.get(FILTER_KEYS.ALBUM._CUSTOM);
|
||||||
|
|
||||||
const newCustom = {
|
const newCustom = {
|
||||||
...(previousValue ? JSON.parse(previousValue) : {}),
|
...(previousValue ? JSON.parse(previousValue) : {}),
|
||||||
...value,
|
...value,
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredNewCustom = Object.fromEntries(
|
const filteredNewCustom = Object.fromEntries(
|
||||||
Object.entries(newCustom).filter(
|
Object.entries(newCustom).filter(
|
||||||
([, value]) => value !== null && value !== undefined,
|
([, value]) => value !== null && value !== undefined,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
prev.set(FILTER_KEYS.ALBUM._CUSTOM, JSON.stringify(filteredNewCustom));
|
prev.set(FILTER_KEYS.ALBUM._CUSTOM, JSON.stringify(filteredNewCustom));
|
||||||
return prev;
|
return prev;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
setMultipleSearchParams,
|
setMultipleSearchParams,
|
||||||
setSearchParam,
|
setSearchParam,
|
||||||
} from '/@/renderer/utils/query-params';
|
} from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { SongListSort, SortOrder } from '/@/shared/types/domain-types';
|
import { SongListSort, SortOrder } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -74,18 +75,22 @@ export const usePlaylistSongListFilters = () => {
|
|||||||
|
|
||||||
const setAlbumArtistIds = useCallback(
|
const setAlbumArtistIds = useCallback(
|
||||||
(value: null | string[]) => {
|
(value: null | string[]) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => setSearchParam(prev, FILTER_KEYS.SONG.ALBUM_ARTIST_IDS, value),
|
setSearchParams(
|
||||||
{ replace: true },
|
(prev) => setSearchParam(prev, FILTER_KEYS.SONG.ALBUM_ARTIST_IDS, value),
|
||||||
);
|
{ replace: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const setGenreId = useCallback(
|
const setGenreId = useCallback(
|
||||||
(value: null | string[]) => {
|
(value: null | string[]) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.GENRE_ID, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.GENRE_ID, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -93,8 +98,13 @@ export const usePlaylistSongListFilters = () => {
|
|||||||
|
|
||||||
const setArtistIds = useCallback(
|
const setArtistIds = useCallback(
|
||||||
(value: null | string[]) => {
|
(value: null | string[]) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.ARTIST_IDS, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams(
|
||||||
|
(prev) => setSearchParam(prev, FILTER_KEYS.SONG.ARTIST_IDS, value),
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -102,8 +112,10 @@ export const usePlaylistSongListFilters = () => {
|
|||||||
|
|
||||||
const setMinYear = useCallback(
|
const setMinYear = useCallback(
|
||||||
(value: null | number) => {
|
(value: null | number) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MIN_YEAR, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MIN_YEAR, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -111,8 +123,10 @@ export const usePlaylistSongListFilters = () => {
|
|||||||
|
|
||||||
const setMaxYear = useCallback(
|
const setMaxYear = useCallback(
|
||||||
(value: null | number) => {
|
(value: null | number) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MAX_YEAR, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MAX_YEAR, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -120,8 +134,10 @@ export const usePlaylistSongListFilters = () => {
|
|||||||
|
|
||||||
const setFavorite = useCallback(
|
const setFavorite = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.FAVORITE, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.FAVORITE, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -129,8 +145,13 @@ export const usePlaylistSongListFilters = () => {
|
|||||||
|
|
||||||
const setHasRating = useCallback(
|
const setHasRating = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.HAS_RATING, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams(
|
||||||
|
(prev) => setSearchParam(prev, FILTER_KEYS.SONG.HAS_RATING, value),
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -153,51 +174,55 @@ export const usePlaylistSongListFilters = () => {
|
|||||||
|
|
||||||
const setCustom = useCallback(
|
const setCustom = useCallback(
|
||||||
(value: null | Record<string, any>) => {
|
(value: null | Record<string, any>) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const previousValue = prev.get(FILTER_KEYS.ALBUM._CUSTOM);
|
(prev) => {
|
||||||
|
const previousValue = prev.get(FILTER_KEYS.ALBUM._CUSTOM);
|
||||||
|
|
||||||
const newCustom = {
|
const newCustom = {
|
||||||
...(previousValue ? JSON.parse(previousValue) : {}),
|
...(previousValue ? JSON.parse(previousValue) : {}),
|
||||||
...value,
|
...value,
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredNewCustom = Object.fromEntries(
|
const filteredNewCustom = Object.fromEntries(
|
||||||
Object.entries(newCustom).filter(
|
Object.entries(newCustom).filter(
|
||||||
([, value]) => value !== null && value !== undefined,
|
([, value]) => value !== null && value !== undefined,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
prev.set(FILTER_KEYS.ALBUM._CUSTOM, JSON.stringify(filteredNewCustom));
|
prev.set(FILTER_KEYS.ALBUM._CUSTOM, JSON.stringify(filteredNewCustom));
|
||||||
return prev;
|
return prev;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const clear = useCallback(() => {
|
const clear = useCallback(() => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) =>
|
setSearchParams(
|
||||||
setMultipleSearchParams(
|
(prev) =>
|
||||||
prev,
|
setMultipleSearchParams(
|
||||||
{
|
prev,
|
||||||
[FILTER_KEYS.SONG._CUSTOM]: null,
|
{
|
||||||
[FILTER_KEYS.SONG.ALBUM_ARTIST_IDS]: null,
|
[FILTER_KEYS.SONG._CUSTOM]: null,
|
||||||
[FILTER_KEYS.SONG.ARTIST_IDS]: null,
|
[FILTER_KEYS.SONG.ALBUM_ARTIST_IDS]: null,
|
||||||
[FILTER_KEYS.SONG.FAVORITE]: null,
|
[FILTER_KEYS.SONG.ARTIST_IDS]: null,
|
||||||
[FILTER_KEYS.SONG.GENRE_ID]: null,
|
[FILTER_KEYS.SONG.FAVORITE]: null,
|
||||||
[FILTER_KEYS.SONG.HAS_RATING]: null,
|
[FILTER_KEYS.SONG.GENRE_ID]: null,
|
||||||
[FILTER_KEYS.SONG.MAX_YEAR]: null,
|
[FILTER_KEYS.SONG.HAS_RATING]: null,
|
||||||
[FILTER_KEYS.SONG.MIN_YEAR]: null,
|
[FILTER_KEYS.SONG.MAX_YEAR]: null,
|
||||||
},
|
[FILTER_KEYS.SONG.MIN_YEAR]: null,
|
||||||
new Set([FILTER_KEYS.SONG._CUSTOM]),
|
},
|
||||||
),
|
new Set([FILTER_KEYS.SONG._CUSTOM]),
|
||||||
{ replace: true },
|
),
|
||||||
);
|
{ replace: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
}, [setSearchParams]);
|
}, [setSearchParams]);
|
||||||
|
|
||||||
const query = useMemo(
|
const query = useMemo(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useRef, useState } from 'react';
|
import { useCallback, useDeferredValue, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
|
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
|
||||||
import { GoToCommands } from '/@/renderer/features/search/components/go-to-commands';
|
import { GoToCommands } from '/@/renderer/features/search/components/go-to-commands';
|
||||||
@@ -49,6 +49,7 @@ function CommandPaletteSearch({
|
|||||||
setQuery,
|
setQuery,
|
||||||
}: CommandPaletteSearchProps) {
|
}: CommandPaletteSearchProps) {
|
||||||
const [debouncedQuery] = useDebouncedValue(query, 400);
|
const [debouncedQuery] = useDebouncedValue(query, 400);
|
||||||
|
const deferredSearchQuery = useDeferredValue(debouncedQuery ?? '');
|
||||||
const searchSectionsExpanded = useAppStore(
|
const searchSectionsExpanded = useAppStore(
|
||||||
(state) => state.commandPaletteSearchSectionsExpanded,
|
(state) => state.commandPaletteSearchSectionsExpanded,
|
||||||
);
|
);
|
||||||
@@ -83,7 +84,7 @@ function CommandPaletteSearch({
|
|||||||
<Command.List>
|
<Command.List>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<SearchAlbumsSection
|
<SearchAlbumsSection
|
||||||
debouncedQuery={debouncedQuery ?? ''}
|
debouncedQuery={deferredSearchQuery}
|
||||||
expanded={searchSectionsExpanded[SEARCH_SECTION_IDS.albums] ?? true}
|
expanded={searchSectionsExpanded[SEARCH_SECTION_IDS.albums] ?? true}
|
||||||
isHome={isHome}
|
isHome={isHome}
|
||||||
onSelectResult={onSelectResult}
|
onSelectResult={onSelectResult}
|
||||||
@@ -96,7 +97,7 @@ function CommandPaletteSearch({
|
|||||||
query={query}
|
query={query}
|
||||||
/>
|
/>
|
||||||
<SearchAlbumArtistsSection
|
<SearchAlbumArtistsSection
|
||||||
debouncedQuery={debouncedQuery ?? ''}
|
debouncedQuery={deferredSearchQuery}
|
||||||
expanded={searchSectionsExpanded[SEARCH_SECTION_IDS.artists] ?? true}
|
expanded={searchSectionsExpanded[SEARCH_SECTION_IDS.artists] ?? true}
|
||||||
isHome={isHome}
|
isHome={isHome}
|
||||||
onSelectResult={onSelectResult}
|
onSelectResult={onSelectResult}
|
||||||
@@ -109,7 +110,7 @@ function CommandPaletteSearch({
|
|||||||
query={query}
|
query={query}
|
||||||
/>
|
/>
|
||||||
<SearchSongsSection
|
<SearchSongsSection
|
||||||
debouncedQuery={debouncedQuery ?? ''}
|
debouncedQuery={deferredSearchQuery}
|
||||||
expanded={searchSectionsExpanded[SEARCH_SECTION_IDS.tracks] ?? true}
|
expanded={searchSectionsExpanded[SEARCH_SECTION_IDS.tracks] ?? true}
|
||||||
isHome={isHome}
|
isHome={isHome}
|
||||||
onSelectResult={onSelectResult}
|
onSelectResult={onSelectResult}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { motion } from 'motion/react';
|
import { motion } from 'motion/react';
|
||||||
import { createContext, ReactNode, useContext, useMemo, useRef } from 'react';
|
import { createContext, ReactNode, useContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import styles from './list-with-sidebar-container.module.css';
|
import styles from './list-with-sidebar-container.module.css';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ import { animationProps } from '/@/shared/components/animations/animation-props'
|
|||||||
import { Portal } from '/@/shared/components/portal/portal';
|
import { Portal } from '/@/shared/components/portal/portal';
|
||||||
|
|
||||||
interface ListWithSidebarContainerContextValue {
|
interface ListWithSidebarContainerContextValue {
|
||||||
sidebarRef: React.RefObject<HTMLDivElement | null>;
|
sidebarElement: HTMLDivElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListWithSidebarContainerContext = createContext<ListWithSidebarContainerContextValue | null>(
|
const ListWithSidebarContainerContext = createContext<ListWithSidebarContainerContextValue | null>(
|
||||||
@@ -36,12 +36,12 @@ function Sidebar({ children }: SidebarProps) {
|
|||||||
throw new Error('Sidebar must be used within ListWithSidebarContainer');
|
throw new Error('Sidebar must be used within ListWithSidebarContainer');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context.sidebarRef?.current) {
|
if (!context.sidebarElement) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal target={context.sidebarRef.current}>
|
<Portal target={context.sidebarElement}>
|
||||||
<motion.div {...animationProps.slideInLeft} style={{ height: '100%', width: '100%' }}>
|
<motion.div {...animationProps.slideInLeft} style={{ height: '100%', width: '100%' }}>
|
||||||
{children}
|
{children}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
@@ -56,25 +56,25 @@ function SidebarPortal({ children }: SidebarPortalProps) {
|
|||||||
throw new Error('SidebarPortal must be used within ListWithSidebarContainer');
|
throw new Error('SidebarPortal must be used within ListWithSidebarContainer');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context.sidebarRef?.current) {
|
if (!context.sidebarElement) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Portal target={context.sidebarRef.current}>{children}</Portal>;
|
return <Portal target={context.sidebarElement}>{children}</Portal>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ListWithSidebarContainer = ({
|
export const ListWithSidebarContainer = ({
|
||||||
children,
|
children,
|
||||||
useBreakpoint = false,
|
useBreakpoint = false,
|
||||||
}: ListWithSidebarContainerProps) => {
|
}: ListWithSidebarContainerProps) => {
|
||||||
const sidebarRef = useRef<HTMLDivElement>(null);
|
const [sidebarElement, setSidebarElement] = useState<HTMLDivElement | null>(null);
|
||||||
const { isSidebarOpen = false } = useListContext();
|
const { isSidebarOpen = false } = useListContext();
|
||||||
|
|
||||||
const contextValue = useMemo(
|
const contextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
sidebarRef,
|
sidebarElement,
|
||||||
}),
|
}),
|
||||||
[],
|
[sidebarElement],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -84,7 +84,7 @@ export const ListWithSidebarContainer = ({
|
|||||||
data-sidebar-open={useBreakpoint ? undefined : isSidebarOpen}
|
data-sidebar-open={useBreakpoint ? undefined : isSidebarOpen}
|
||||||
data-use-breakpoint={useBreakpoint}
|
data-use-breakpoint={useBreakpoint}
|
||||||
>
|
>
|
||||||
<div className={styles.sidebarContainer} ref={sidebarRef} />
|
<div className={styles.sidebarContainer} ref={setSidebarElement} />
|
||||||
<div className={styles.contentContainer}>{children}</div>
|
<div className={styles.contentContainer}>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</ListWithSidebarContainerContext.Provider>
|
</ListWithSidebarContainerContext.Provider>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useSearchParams } from 'react-router';
|
|||||||
|
|
||||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||||
import { parseStringParam, setSearchParam } from '/@/renderer/utils/query-params';
|
import { parseStringParam, setSearchParam } from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { useDebouncedCallback } from '/@/shared/hooks/use-debounced-callback';
|
import { useDebouncedCallback } from '/@/shared/hooks/use-debounced-callback';
|
||||||
|
|
||||||
export const useSearchTermFilter = (defaultValue?: string) => {
|
export const useSearchTermFilter = (defaultValue?: string) => {
|
||||||
@@ -14,17 +15,19 @@ export const useSearchTermFilter = (defaultValue?: string) => {
|
|||||||
}, [searchParams, defaultValue]);
|
}, [searchParams, defaultValue]);
|
||||||
|
|
||||||
const handleSetSearchTerm = (value: null | string) => {
|
const handleSetSearchTerm = (value: null | string) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const newParams = setSearchParam(
|
(prev) => {
|
||||||
prev,
|
const newParams = setSearchParam(
|
||||||
FILTER_KEYS.SHARED.SEARCH_TERM,
|
prev,
|
||||||
value === '' ? null : value,
|
FILTER_KEYS.SHARED.SEARCH_TERM,
|
||||||
);
|
value === '' ? null : value,
|
||||||
return newParams;
|
);
|
||||||
},
|
return newParams;
|
||||||
{ replace: true },
|
},
|
||||||
);
|
{ replace: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const debouncedSetSearchTerm = useDebouncedCallback(handleSetSearchTerm, 300);
|
const debouncedSetSearchTerm = useDebouncedCallback(handleSetSearchTerm, 300);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
|
|
||||||
import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
|
import { useCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
|
||||||
import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
|
import { useDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
|
||||||
@@ -8,21 +8,26 @@ export const useSetFavorite = () => {
|
|||||||
const createFavoriteMutation = useCreateFavorite({});
|
const createFavoriteMutation = useCreateFavorite({});
|
||||||
const deleteFavoriteMutation = useDeleteFavorite({});
|
const deleteFavoriteMutation = useDeleteFavorite({});
|
||||||
|
|
||||||
|
const createFavoriteMutationRef = useRef(createFavoriteMutation);
|
||||||
|
const deleteFavoriteMutationRef = useRef(deleteFavoriteMutation);
|
||||||
|
createFavoriteMutationRef.current = createFavoriteMutation;
|
||||||
|
deleteFavoriteMutationRef.current = deleteFavoriteMutation;
|
||||||
|
|
||||||
const setFavorite = useCallback(
|
const setFavorite = useCallback(
|
||||||
(serverId: string, id: string[], itemType: LibraryItem, isFavorite: boolean) => {
|
(serverId: string, id: string[], itemType: LibraryItem, isFavorite: boolean) => {
|
||||||
if (isFavorite) {
|
if (isFavorite) {
|
||||||
createFavoriteMutation.mutate({
|
createFavoriteMutationRef.current.mutate({
|
||||||
apiClientProps: { serverId },
|
apiClientProps: { serverId },
|
||||||
query: { id, type: itemType },
|
query: { id, type: itemType },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deleteFavoriteMutation.mutate({
|
deleteFavoriteMutationRef.current.mutate({
|
||||||
apiClientProps: { serverId },
|
apiClientProps: { serverId },
|
||||||
query: { id, type: itemType },
|
query: { id, type: itemType },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[createFavoriteMutation, deleteFavoriteMutation],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return setFavorite;
|
return setFavorite;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
|
|
||||||
import { useSetRatingMutation } from '/@/renderer/features/shared/mutations/set-rating-mutation';
|
import { useSetRatingMutation } from '/@/renderer/features/shared/mutations/set-rating-mutation';
|
||||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||||
@@ -6,14 +6,17 @@ import { LibraryItem } from '/@/shared/types/domain-types';
|
|||||||
export const useSetRating = () => {
|
export const useSetRating = () => {
|
||||||
const setRatingMutation = useSetRatingMutation({});
|
const setRatingMutation = useSetRatingMutation({});
|
||||||
|
|
||||||
|
const setRatingMutationRef = useRef(setRatingMutation);
|
||||||
|
setRatingMutationRef.current = setRatingMutation;
|
||||||
|
|
||||||
const setRating = useCallback(
|
const setRating = useCallback(
|
||||||
(serverId: string, id: string[], itemType: LibraryItem, rating: number) => {
|
(serverId: string, id: string[], itemType: LibraryItem, rating: number) => {
|
||||||
setRatingMutation.mutate({
|
setRatingMutationRef.current.mutate({
|
||||||
apiClientProps: { serverId },
|
apiClientProps: { serverId },
|
||||||
query: { id, rating, type: itemType },
|
query: { id, rating, type: itemType },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setRatingMutation],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return setRating;
|
return setRating;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useListFilterPersistence } from '/@/renderer/features/shared/hooks/use-
|
|||||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
import { parseStringParam, setSearchParam } from '/@/renderer/utils/query-params';
|
import { parseStringParam, setSearchParam } from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
export const useSortByFilter = <TSortBy>(defaultValue: null | string, listKey: ItemListKey) => {
|
export const useSortByFilter = <TSortBy>(defaultValue: null | string, listKey: ItemListKey) => {
|
||||||
@@ -20,14 +21,16 @@ export const useSortByFilter = <TSortBy>(defaultValue: null | string, listKey: I
|
|||||||
}, [searchParams, persisted, defaultValue]);
|
}, [searchParams, persisted, defaultValue]);
|
||||||
|
|
||||||
const handleSetSortBy = (sortBy: string) => {
|
const handleSetSortBy = (sortBy: string) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const newParams = setSearchParam(prev, FILTER_KEYS.SHARED.SORT_BY, sortBy);
|
(prev) => {
|
||||||
return newParams;
|
const newParams = setSearchParam(prev, FILTER_KEYS.SHARED.SORT_BY, sortBy);
|
||||||
},
|
return newParams;
|
||||||
{ replace: true },
|
},
|
||||||
);
|
{ replace: true },
|
||||||
setFilter(FILTER_KEYS.SHARED.SORT_BY, sortBy);
|
);
|
||||||
|
setFilter(FILTER_KEYS.SHARED.SORT_BY, sortBy);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useListFilterPersistence } from '/@/renderer/features/shared/hooks/use-
|
|||||||
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
import { FILTER_KEYS } from '/@/renderer/features/shared/utils';
|
||||||
import { useCurrentServer } from '/@/renderer/store';
|
import { useCurrentServer } from '/@/renderer/store';
|
||||||
import { parseStringParam, setSearchParam } from '/@/renderer/utils/query-params';
|
import { parseStringParam, setSearchParam } from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { SortOrder } from '/@/shared/types/domain-types';
|
import { SortOrder } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -21,14 +22,20 @@ export const useSortOrderFilter = (defaultValue: null | string, listKey: ItemLis
|
|||||||
}, [searchParams, persisted, defaultValue]);
|
}, [searchParams, persisted, defaultValue]);
|
||||||
|
|
||||||
const handleSetSortOrder = (sortOrder: SortOrder) => {
|
const handleSetSortOrder = (sortOrder: SortOrder) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const newParams = setSearchParam(prev, FILTER_KEYS.SHARED.SORT_ORDER, sortOrder);
|
(prev) => {
|
||||||
return newParams;
|
const newParams = setSearchParam(
|
||||||
},
|
prev,
|
||||||
{ replace: true },
|
FILTER_KEYS.SHARED.SORT_ORDER,
|
||||||
);
|
sortOrder,
|
||||||
setFilter(FILTER_KEYS.SHARED.SORT_ORDER, sortOrder);
|
);
|
||||||
|
return newParams;
|
||||||
|
},
|
||||||
|
{ replace: true },
|
||||||
|
);
|
||||||
|
setFilter(FILTER_KEYS.SHARED.SORT_ORDER, sortOrder);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
setMultipleSearchParams,
|
setMultipleSearchParams,
|
||||||
setSearchParam,
|
setSearchParam,
|
||||||
} from '/@/renderer/utils/query-params';
|
} from '/@/renderer/utils/query-params';
|
||||||
|
import { runInUrlTransition } from '/@/renderer/utils/url-transition';
|
||||||
import { SongListSort, SortOrder } from '/@/shared/types/domain-types';
|
import { SongListSort, SortOrder } from '/@/shared/types/domain-types';
|
||||||
import { ItemListKey } from '/@/shared/types/types';
|
import { ItemListKey } from '/@/shared/types/types';
|
||||||
|
|
||||||
@@ -65,8 +66,10 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setGenreId = useCallback(
|
const setGenreId = useCallback(
|
||||||
(value: null | string[]) => {
|
(value: null | string[]) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.GENRE_ID, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.GENRE_ID, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -74,8 +77,13 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setArtistIds = useCallback(
|
const setArtistIds = useCallback(
|
||||||
(value: null | string[]) => {
|
(value: null | string[]) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.ARTIST_IDS, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams(
|
||||||
|
(prev) => setSearchParam(prev, FILTER_KEYS.SONG.ARTIST_IDS, value),
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -83,8 +91,10 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setMinYear = useCallback(
|
const setMinYear = useCallback(
|
||||||
(value: null | number) => {
|
(value: null | number) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MIN_YEAR, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MIN_YEAR, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -92,8 +102,10 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setMaxYear = useCallback(
|
const setMaxYear = useCallback(
|
||||||
(value: null | number) => {
|
(value: null | number) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MAX_YEAR, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.MAX_YEAR, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -101,8 +113,10 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setFavorite = useCallback(
|
const setFavorite = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.FAVORITE, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.FAVORITE, value), {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -110,8 +124,13 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
|||||||
|
|
||||||
const setHasRating = useCallback(
|
const setHasRating = useCallback(
|
||||||
(value: boolean | null) => {
|
(value: boolean | null) => {
|
||||||
setSearchParams((prev) => setSearchParam(prev, FILTER_KEYS.SONG.HAS_RATING, value), {
|
runInUrlTransition(() => {
|
||||||
replace: true,
|
setSearchParams(
|
||||||
|
(prev) => setSearchParam(prev, FILTER_KEYS.SONG.HAS_RATING, value),
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
@@ -124,46 +143,53 @@ export const useSongListFilters = (listKey?: ItemListKey) => {
|
|||||||
| null
|
| null
|
||||||
| Record<string, any>,
|
| Record<string, any>,
|
||||||
) => {
|
) => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) => {
|
setSearchParams(
|
||||||
const currentCustom = parseCustomFiltersParam(prev, FILTER_KEYS.SONG._CUSTOM);
|
(prev) => {
|
||||||
let newValue =
|
const currentCustom = parseCustomFiltersParam(
|
||||||
typeof value === 'function' ? value(currentCustom ?? null) : value;
|
prev,
|
||||||
// Convert empty objects to null to clear them from URL
|
FILTER_KEYS.SONG._CUSTOM,
|
||||||
if (
|
);
|
||||||
newValue &&
|
let newValue =
|
||||||
typeof newValue === 'object' &&
|
typeof value === 'function' ? value(currentCustom ?? null) : value;
|
||||||
Object.keys(newValue).length === 0
|
// Convert empty objects to null to clear them from URL
|
||||||
) {
|
if (
|
||||||
newValue = null;
|
newValue &&
|
||||||
}
|
typeof newValue === 'object' &&
|
||||||
return setJsonSearchParam(prev, FILTER_KEYS.SONG._CUSTOM, newValue);
|
Object.keys(newValue).length === 0
|
||||||
},
|
) {
|
||||||
{ replace: true },
|
newValue = null;
|
||||||
);
|
}
|
||||||
|
return setJsonSearchParam(prev, FILTER_KEYS.SONG._CUSTOM, newValue);
|
||||||
|
},
|
||||||
|
{ replace: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const clear = useCallback(() => {
|
const clear = useCallback(() => {
|
||||||
setSearchParams(
|
runInUrlTransition(() => {
|
||||||
(prev) =>
|
setSearchParams(
|
||||||
setMultipleSearchParams(
|
(prev) =>
|
||||||
prev,
|
setMultipleSearchParams(
|
||||||
{
|
prev,
|
||||||
[FILTER_KEYS.SHARED.SEARCH_TERM]: null,
|
{
|
||||||
[FILTER_KEYS.SONG._CUSTOM]: null,
|
[FILTER_KEYS.SHARED.SEARCH_TERM]: null,
|
||||||
[FILTER_KEYS.SONG.ARTIST_IDS]: null,
|
[FILTER_KEYS.SONG._CUSTOM]: null,
|
||||||
[FILTER_KEYS.SONG.FAVORITE]: null,
|
[FILTER_KEYS.SONG.ARTIST_IDS]: null,
|
||||||
[FILTER_KEYS.SONG.GENRE_ID]: null,
|
[FILTER_KEYS.SONG.FAVORITE]: null,
|
||||||
[FILTER_KEYS.SONG.HAS_RATING]: null,
|
[FILTER_KEYS.SONG.GENRE_ID]: null,
|
||||||
[FILTER_KEYS.SONG.MAX_YEAR]: null,
|
[FILTER_KEYS.SONG.HAS_RATING]: null,
|
||||||
[FILTER_KEYS.SONG.MIN_YEAR]: null,
|
[FILTER_KEYS.SONG.MAX_YEAR]: null,
|
||||||
},
|
[FILTER_KEYS.SONG.MIN_YEAR]: null,
|
||||||
new Set([FILTER_KEYS.SONG._CUSTOM]),
|
},
|
||||||
),
|
new Set([FILTER_KEYS.SONG._CUSTOM]),
|
||||||
{ replace: true },
|
),
|
||||||
);
|
{ replace: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
}, [setSearchParams]);
|
}, [setSearchParams]);
|
||||||
|
|
||||||
const query = useMemo(
|
const query = useMemo(
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ const appRouterModals = {
|
|||||||
|
|
||||||
export const AppRouter = () => {
|
export const AppRouter = () => {
|
||||||
const router = (
|
const router = (
|
||||||
<HashRouter unstable_useTransitions={false}>
|
<HashRouter unstable_useTransitions>
|
||||||
<ModalsProvider modals={appRouterModals}>
|
<ModalsProvider modals={appRouterModals}>
|
||||||
<RouterErrorBoundary>
|
<RouterErrorBoundary>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { startTransition } from 'react';
|
||||||
|
|
||||||
|
export function runInUrlTransition(update: () => void): void {
|
||||||
|
startTransition(update);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user