Feature/improve restoring grid snapshots (#713)
* Update local storage functions * Update local storage hooks * Add session storage * Improve storing virtuoso grid snapshot * Use "useSyncExternalStore" for browser storage access * Get rid of location replacements in "AppbarSearch" Use session storage instead to prevent reloading the page all the time due to changing the location * Get rid of location replacements in "SourceMangas" Use session storage instead to prevent reloading the page all the time due to changing the location
This commit is contained in:
@@ -17,7 +17,7 @@ import { useTheme } from '@mui/material/styles';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { extensionDefaultLangs, DefaultLanguage, langSortCmp } from '@/util/language';
|
||||
import { useLocalStorage } from '@/util/useLocalStorage';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import {
|
||||
ExtensionState,
|
||||
GroupedExtensions,
|
||||
|
||||
@@ -18,7 +18,7 @@ import { EmptyView } from '@/components/util/EmptyView.tsx';
|
||||
import { MangaGrid } from '@/components/MangaGrid.tsx';
|
||||
import { TPartialManga } from '@/typings.ts';
|
||||
import { GridLayouts } from '@/components/source/GridLayouts.tsx';
|
||||
import { useLocalStorage } from '@/util/useLocalStorage.tsx';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import { GridLayout } from '@/components/context/LibraryOptionsContext.tsx';
|
||||
|
||||
export const Migrate = () => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { StringParam, useQueryParam } from 'use-query-params';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ISource } from '@/typings';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { useLocalStorage } from '@/util/useLocalStorage';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import { langSortCmp, sourceDefualtLangs, sourceForcedDefaultLangs } from '@/util/language';
|
||||
import { translateExtensionLanguage } from '@/screens/util/Extensions';
|
||||
import { AppbarSearch } from '@/components/util/AppbarSearch';
|
||||
@@ -214,7 +214,7 @@ export const SearchAll: React.FC = () => {
|
||||
setTitle(t(isMigrateMode ? 'migrate.search.title' : 'search.title.global_search', { title: mangaTitle }));
|
||||
setAction(
|
||||
<>
|
||||
<AppbarSearch autoOpen />
|
||||
<AppbarSearch isClosable={false} />
|
||||
<LangSelect
|
||||
shownLangs={shownLangs}
|
||||
setShownLangs={setShownLangs}
|
||||
|
||||
@@ -30,7 +30,7 @@ import ExploreOutlinedIcon from '@mui/icons-material/ExploreOutlined';
|
||||
import DevicesIcon from '@mui/icons-material/Devices';
|
||||
import SyncIcon from '@mui/icons-material/Sync';
|
||||
import { langCodeToName } from '@/util/language';
|
||||
import { useLocalStorage } from '@/util/useLocalStorage';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import { ListItemLink } from '@/components/util/ListItemLink';
|
||||
import { DarkTheme } from '@/components/context/DarkTheme';
|
||||
import { NavBarContext } from '@/components/context/NavbarContext.tsx';
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
AbortableApolloUseMutationPaginatedResponse,
|
||||
SPECIAL_ED_SOURCES,
|
||||
} from '@/lib/requests/RequestManager.ts';
|
||||
import { useDebounce } from '@/util/useDebounce.ts';
|
||||
import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext';
|
||||
import { SourceGridLayout } from '@/components/source/SourceGridLayout';
|
||||
import { AppbarSearch } from '@/components/util/AppbarSearch';
|
||||
@@ -35,6 +34,7 @@ import {
|
||||
} from '@/lib/graphql/generated/graphql.ts';
|
||||
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
||||
import { useMetadataServerSettings } from '@/lib/metadata/metadataServerSettings.ts';
|
||||
import { useSessionStorage } from '@/util/useStorage.tsx';
|
||||
|
||||
const ContentTypeMenu = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@@ -215,17 +215,12 @@ export function SourceMangas() {
|
||||
const { sourceId } = useParams<{ sourceId: string }>();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { search } = useLocation();
|
||||
const {
|
||||
contentType: currentLocationContentType = SourceContentType.POPULAR,
|
||||
filtersToApply: currentLocationFiltersToApply = [],
|
||||
clearCache = false,
|
||||
} = useLocation<{
|
||||
contentType: SourceContentType;
|
||||
filtersToApply: IPos[];
|
||||
clearCache: boolean;
|
||||
search: string;
|
||||
}>().state ?? {};
|
||||
const { key: locationKey, state: locationState } = useLocation();
|
||||
const { contentType: initialContentType = SourceContentType.POPULAR, clearCache = false } =
|
||||
useLocation<{
|
||||
contentType: SourceContentType;
|
||||
clearCache: boolean;
|
||||
}>().state ?? {};
|
||||
|
||||
useSetDefaultBackTo('sources');
|
||||
|
||||
@@ -241,13 +236,19 @@ export function SourceMangas() {
|
||||
|
||||
const { options } = useLibraryOptionsContext();
|
||||
const [query] = useQueryParam('query', StringParam);
|
||||
const [dialogFiltersToApply, setDialogFiltersToApply] = useState<IPos[]>(currentLocationFiltersToApply);
|
||||
const [filtersToApply, setFiltersToApply] = useState<IPos[]>(currentLocationFiltersToApply);
|
||||
const searchTerm = useDebounce(query, 1000);
|
||||
const [filtersToApply, setFiltersToApply] = useSessionStorage<IPos[]>(
|
||||
`source-mangas-location-${locationKey}-${sourceId}-filters`,
|
||||
[],
|
||||
);
|
||||
const [dialogFiltersToApply, setDialogFiltersToApply] = useState<IPos[]>(filtersToApply);
|
||||
const [resetScrollPosition, setResetScrollPosition] = useState(false);
|
||||
const [contentType, setContentType] = useState(currentLocationContentType);
|
||||
const [contentType, setContentType] = useSessionStorage(
|
||||
`source-mangas-location-${locationKey}-${sourceId}-content-type`,
|
||||
query ? SourceContentType.SEARCH : initialContentType,
|
||||
);
|
||||
|
||||
const [loadPage, { data, isLoading: loading, size: lastPageNum, abortRequest, filteredOutAllItemsOfFetchedPage }] =
|
||||
useSourceManga(sourceId, contentType, searchTerm, filtersToApply, 1, hideLibraryEntries);
|
||||
useSourceManga(sourceId, contentType, query, filtersToApply, 1, hideLibraryEntries);
|
||||
const isLoading = loading || filteredOutAllItemsOfFetchedPage;
|
||||
const mangas = data?.fetchSourceManga.mangas ?? [];
|
||||
const hasNextPage = data?.fetchSourceManga.hasNextPage ?? false;
|
||||
@@ -268,50 +269,36 @@ export function SourceMangas() {
|
||||
) : undefined;
|
||||
|
||||
const updateContentType = useCallback(
|
||||
(
|
||||
newContentType: SourceContentType,
|
||||
{ updateLocationState = true, search: newSearch }: { updateLocationState?: boolean; search?: string } = {},
|
||||
) => {
|
||||
if (updateLocationState) {
|
||||
(newContentType: SourceContentType, newSearch?: string | null) => {
|
||||
setContentType(newContentType);
|
||||
setResetScrollPosition(true);
|
||||
|
||||
if (query && !newSearch) {
|
||||
navigate(
|
||||
{ pathname: '', search: newSearch },
|
||||
{
|
||||
replace: true,
|
||||
state: {
|
||||
contentType: newContentType,
|
||||
},
|
||||
pathname: '',
|
||||
},
|
||||
{
|
||||
state: { ...locationState, contentType: newContentType },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setContentType(newContentType);
|
||||
setResetScrollPosition(true);
|
||||
},
|
||||
[setContentType],
|
||||
[setContentType, query],
|
||||
);
|
||||
|
||||
const updateLocationFilters = useCallback(
|
||||
(updatedFilters: IPos[]) => {
|
||||
if (contentType === SourceContentType.SEARCH) {
|
||||
navigate(
|
||||
{ pathname: '', search },
|
||||
{
|
||||
replace: true,
|
||||
state: {
|
||||
contentType,
|
||||
filtersToApply: updatedFilters,
|
||||
},
|
||||
},
|
||||
);
|
||||
setFiltersToApply(updatedFilters);
|
||||
}
|
||||
},
|
||||
[contentType, search],
|
||||
[contentType, query],
|
||||
);
|
||||
|
||||
const isSearchTermAvailable = searchTerm && query?.length;
|
||||
const setSearchContentType = isSearchTermAvailable && contentType !== SourceContentType.SEARCH;
|
||||
const setSearchContentType = !!query && contentType !== SourceContentType.SEARCH;
|
||||
if (setSearchContentType) {
|
||||
updateContentType(SourceContentType.SEARCH, { search });
|
||||
updateContentType(SourceContentType.SEARCH, query);
|
||||
}
|
||||
|
||||
const loadMore = useCallback(() => {
|
||||
@@ -346,10 +333,6 @@ export function SourceMangas() {
|
||||
}
|
||||
|
||||
requestManager.clearBrowseCacheFor(sourceId);
|
||||
navigate('', {
|
||||
replace: true,
|
||||
state: { contentType: currentLocationContentType, filters: currentLocationFiltersToApply },
|
||||
});
|
||||
}, [clearCache]);
|
||||
|
||||
useEffect(
|
||||
@@ -363,7 +346,7 @@ export function SourceMangas() {
|
||||
abortRequest(new Error(`SourceMangas(${sourceId}): search string changed`));
|
||||
setResetScrollPosition(true);
|
||||
},
|
||||
[searchTerm],
|
||||
[query],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -426,7 +409,7 @@ export function SourceMangas() {
|
||||
<ContentTypeButton
|
||||
variant={contentType === SourceContentType.SEARCH ? 'contained' : 'outlined'}
|
||||
startIcon={<FilterListIcon />}
|
||||
onClick={() => updateContentType(SourceContentType.SEARCH)}
|
||||
onClick={() => updateContentType(SourceContentType.SEARCH, query)}
|
||||
>
|
||||
{t('global.button.filter')}
|
||||
</ContentTypeButton>
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ISource, TPartialSource } from '@/typings';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { useLocalStorage } from '@/util/useLocalStorage';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import { sourceDefualtLangs, sourceForcedDefaultLangs, langSortCmp } from '@/util/language';
|
||||
import { translateExtensionLanguage } from '@/screens/util/Extensions';
|
||||
import { LoadingPlaceholder } from '@/components/util/LoadingPlaceholder';
|
||||
|
||||
@@ -18,7 +18,7 @@ import { NumberSetting } from '@/components/settings/NumberSetting.tsx';
|
||||
import { MutableListSetting } from '@/components/settings/MutableListSetting.tsx';
|
||||
import { MetadataBrowseSettings, ServerSettings as GqlServerSettings } from '@/typings.ts';
|
||||
import { TextSetting } from '@/components/settings/text/TextSetting.tsx';
|
||||
import { useLocalStorage } from '@/util/useLocalStorage.tsx';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import {
|
||||
createUpdateMetadataServerSettings,
|
||||
useMetadataServerSettings,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Link, List, ListItem, ListItemText, Switch } from '@mui/material';
|
||||
import ListSubheader from '@mui/material/ListSubheader';
|
||||
import { NavBarContext, useSetDefaultBackTo } from '@/components/context/NavbarContext.tsx';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { useLocalStorage } from '@/util/useLocalStorage.tsx';
|
||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||
import { TextSetting } from '@/components/settings/text/TextSetting.tsx';
|
||||
import { ServerSettings as GqlServerSettings } from '@/typings.ts';
|
||||
import { NumberSetting } from '@/components/settings/NumberSetting.tsx';
|
||||
|
||||
Reference in New Issue
Block a user