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:
schroda
2024-04-07 15:22:03 +02:00
committed by GitHub
parent ea2f9dc5a4
commit aab3e25031
20 changed files with 220 additions and 190 deletions

View File

@@ -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,

View File

@@ -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 = () => {

View File

@@ -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}

View File

@@ -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';

View File

@@ -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>

View File

@@ -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';

View File

@@ -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,

View File

@@ -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';