diff --git a/src/components/SourceCard.tsx b/src/components/SourceCard.tsx index 795011d3..a34bbd10 100644 --- a/src/components/SourceCard.tsx +++ b/src/components/SourceCard.tsx @@ -55,7 +55,11 @@ export const SourceCard: React.FC = (props: IProps) => { margin: '10px', }} > - + = (props: IProps) => { variant="outlined" component={Link} to={`/sources/${id}`} - state={{ contentType: SourceContentType.LATEST }} + state={{ contentType: SourceContentType.LATEST, clearCache: true }} > {t('global.button.latest')} @@ -117,7 +121,7 @@ export const SourceCard: React.FC = (props: IProps) => { variant="outlined" component={Link} to={`/sources/${id}`} - state={{ contentType: SourceContentType.LATEST }} + state={{ contentType: SourceContentType.LATEST, clearCache: true }} > {t('global.button.latest')} @@ -126,7 +130,7 @@ export const SourceCard: React.FC = (props: IProps) => { variant="outlined" component={Link} to={`/sources/${id}`} - state={{ contentType: SourceContentType.POPULAR }} + state={{ contentType: SourceContentType.POPULAR, clearCache: true }} > {t('global.button.popular')} diff --git a/src/lib/requests/CustomCache.ts b/src/lib/requests/CustomCache.ts index 4fe4cca1..803dd8e2 100644 --- a/src/lib/requests/CustomCache.ts +++ b/src/lib/requests/CustomCache.ts @@ -43,6 +43,21 @@ export class CustomCache { return this.keyToResponseMap.get(key) as Response; } + public getAllKeys(): string[] { + return [...this.keyToResponseMap.keys()]; + } + + public getMatchingKeys(regex: RegExp): string[] { + return this.getAllKeys().filter((key) => !!regex.exec(key)); + } + + public clearFor(...keys: string[]) { + keys.forEach((key) => { + this.keyToResponseMap.delete(key); + this.keyToFetchTimestampMap.delete(key); + }); + } + public clear(): void { this.keyToResponseMap.clear(); this.keyToFetchTimestampMap.clear(); diff --git a/src/lib/requests/RequestManager.ts b/src/lib/requests/RequestManager.ts index b289ca39..77a48dab 100644 --- a/src/lib/requests/RequestManager.ts +++ b/src/lib/requests/RequestManager.ts @@ -295,6 +295,16 @@ export type AbortableApolloMutationResponse = { response: Promise { @@ -356,6 +374,7 @@ export class RequestManager { } private async revalidatePage( + sourceId: string, cacheResultsKey: string, cachePagesKey: string, getVariablesFor: (page: number) => Variables, @@ -369,6 +388,10 @@ export class RequestManager { maxPage: number, signal: AbortSignal, ): Promise { + if (SPECIAL_ED_SOURCES.REVALIDATION.includes(sourceId)) { + return; + } + const { response: revalidationRequest } = this.doRequest( GQLMethod.MUTATION, GET_SOURCE_MANGAS_FETCH, @@ -406,6 +429,7 @@ export class RequestManager { if (isCachedPageInvalid && pageToRevalidate < maxPage) { await this.revalidatePage( + sourceId, cacheResultsKey, cachePagesKey, getVariablesFor, @@ -965,10 +989,6 @@ export class RequestManager { }, }); - const CACHE_INITIAL_PAGES_FETCHING_KEY = 'GET_SOURCE_MANGAS_FETCH_FETCHING_INITIAL_PAGES'; - const CACHE_PAGES_KEY = 'GET_SOURCE_MANGAS_FETCH_PAGES'; - const CACHE_RESULTS_KEY = 'GET_SOURCE_MANGAS_FETCH'; - const isRevalidationDoneRef = useRef(false); const activeRevalidationRef = useRef< | [ @@ -1019,6 +1039,7 @@ export class RequestManager { const revalidatePage = async (pageToRevalidate: number, maxPage: number, signal: AbortSignal) => this.revalidatePage( + input.source, CACHE_RESULTS_KEY, CACHE_PAGES_KEY, getVariablesFor, diff --git a/src/screens/SourceMangas.tsx b/src/screens/SourceMangas.tsx index 49400e63..3bfb2439 100644 --- a/src/screens/SourceMangas.tsx +++ b/src/screens/SourceMangas.tsx @@ -18,7 +18,11 @@ import FavoriteIcon from '@mui/icons-material/Favorite'; import NewReleasesIcon from '@mui/icons-material/NewReleases'; import FilterListIcon from '@mui/icons-material/FilterList'; import { TPartialManga, TranslationKey } from '@/typings'; -import { requestManager, AbortableApolloUseMutationPaginatedResponse } from '@/lib/requests/RequestManager.ts'; +import { + requestManager, + AbortableApolloUseMutationPaginatedResponse, + SPECIAL_ED_SOURCES, +} from '@/lib/requests/RequestManager.ts'; import { useDebounce } from '@/components/manga/hooks'; import { useLibraryOptionsContext } from '@/components/context/LibraryOptionsContext'; import { SourceGridLayout } from '@/components/source/GridLayouts'; @@ -203,11 +207,12 @@ export function SourceMangas() { const { contentType: currentLocationContentType = SourceContentType.POPULAR, filtersToApply: currentLocationFiltersToApply = [], - } = - useLocation<{ - contentType: SourceContentType; - filtersToApply: IPos[]; - }>().state ?? {}; + clearCache = false, + } = useLocation<{ + contentType: SourceContentType; + filtersToApply: IPos[]; + clearCache: boolean; + }>().state ?? {}; const { options } = useLibraryOptionsContext(); const [query] = useQueryParam('query', StringParam); @@ -288,6 +293,23 @@ export function SourceMangas() { setResetScrollPosition(true); }, [sourceId, contentType]); + useEffect(() => { + if (!clearCache) { + return; + } + + const requiresClear = SPECIAL_ED_SOURCES.REVALIDATION.includes(sourceId); + if (!requiresClear) { + return; + } + + requestManager.clearBrowseCacheFor(sourceId); + navigate('', { + replace: true, + state: { contentType: currentLocationContentType, filters: currentLocationFiltersToApply }, + }); + }, [clearCache]); + useEffect( () => () => { if (contentType !== SourceContentType.SEARCH) {