Add manga fetch timestamp to thumbnail url (#598)
In case the fetch timestamp of a manga changed, it's possible that the thumbnail has changed as well, thus, the currently cached image should be ignored
This commit is contained in:
@@ -24,6 +24,7 @@ import { MangaOptionButton } from '@/components/manga/MangaOptionButton.tsx';
|
|||||||
import { MangaActionMenuItems, SingleModeProps } from '@/components/manga/MangaActionMenuItems.tsx';
|
import { MangaActionMenuItems, SingleModeProps } from '@/components/manga/MangaActionMenuItems.tsx';
|
||||||
import { Menu } from '@/components/menu/Menu.tsx';
|
import { Menu } from '@/components/menu/Menu.tsx';
|
||||||
import { MigrateDialog } from '@/components/MigrateDialog.tsx';
|
import { MigrateDialog } from '@/components/MigrateDialog.tsx';
|
||||||
|
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||||
|
|
||||||
const BottomGradient = styled('div')({
|
const BottomGradient = styled('div')({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@@ -96,17 +97,8 @@ export const MangaCard = (props: MangaCardProps) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { manga, gridLayout, inLibraryIndicator, selected, handleSelection, mode = 'default' } = props;
|
const { manga, gridLayout, inLibraryIndicator, selected, handleSelection, mode = 'default' } = props;
|
||||||
const {
|
const { id, title, downloadCount, unreadCount: unread, inLibrary, latestReadChapter, chapters } = manga;
|
||||||
id,
|
const thumbnailUrl = Mangas.getThumbnailUrl(manga);
|
||||||
title,
|
|
||||||
thumbnailUrl: tmpThumbnailUrl,
|
|
||||||
downloadCount,
|
|
||||||
unreadCount: unread,
|
|
||||||
inLibrary,
|
|
||||||
latestReadChapter,
|
|
||||||
chapters,
|
|
||||||
} = manga;
|
|
||||||
const thumbnailUrl = tmpThumbnailUrl ?? 'nonExistingMangaUrl';
|
|
||||||
const {
|
const {
|
||||||
options: { showContinueReadingButton, showUnreadBadge, showDownloadBadge },
|
options: { showContinueReadingButton, showUnreadBadge, showDownloadBadge },
|
||||||
} = useLibraryOptionsContext();
|
} = useLibraryOptionsContext();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { requestManager } from '@/lib/requests/RequestManager.ts';
|
|||||||
import { makeToast } from '@/components/util/Toast';
|
import { makeToast } from '@/components/util/Toast';
|
||||||
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
import { useMetadataServerSettings } from '@/util/metadataServerSettings.ts';
|
||||||
import { CategorySelect } from '@/components/navbar/action/CategorySelect.tsx';
|
import { CategorySelect } from '@/components/navbar/action/CategorySelect.tsx';
|
||||||
|
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||||
|
|
||||||
const DetailsWrapper = styled('div')(({ theme }) => ({
|
const DetailsWrapper = styled('div')(({ theme }) => ({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@@ -215,9 +216,7 @@ export const MangaDetails: React.FC<IProps> = ({ manga }) => {
|
|||||||
<TopContentWrapper>
|
<TopContentWrapper>
|
||||||
<ThumbnailMetadataWrapper>
|
<ThumbnailMetadataWrapper>
|
||||||
<Thumbnail>
|
<Thumbnail>
|
||||||
{manga.thumbnailUrl && (
|
{manga.thumbnailUrl && <img src={Mangas.getThumbnailUrl(manga)} alt="Manga Thumbnail" />}
|
||||||
<img src={requestManager.getValidImgUrlFor(manga.thumbnailUrl)} alt="Manga Thumbnail" />
|
|
||||||
)}
|
|
||||||
</Thumbnail>
|
</Thumbnail>
|
||||||
<Metadata>
|
<Metadata>
|
||||||
<h1>{manga.title}</h1>
|
<h1>{manga.title}</h1>
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ export const actionToTranslationKey: {
|
|||||||
export type MangaChapterCountInfo = { chapters: Pick<TManga['chapters'], 'totalCount'> };
|
export type MangaChapterCountInfo = { chapters: Pick<TManga['chapters'], 'totalCount'> };
|
||||||
export type MangaDownloadInfo = Pick<TManga, 'downloadCount'> & MangaChapterCountInfo;
|
export type MangaDownloadInfo = Pick<TManga, 'downloadCount'> & MangaChapterCountInfo;
|
||||||
export type MangaUnreadInfo = Pick<TManga, 'unreadCount'> & MangaChapterCountInfo;
|
export type MangaUnreadInfo = Pick<TManga, 'unreadCount'> & MangaChapterCountInfo;
|
||||||
|
export type MangaLastFetchedInfo = Pick<TManga, 'lastFetchedAt'>;
|
||||||
|
export type MangaThumbnailInfo = Pick<TManga, 'thumbnailUrl'>;
|
||||||
|
|
||||||
export type MigrateMode = 'copy' | 'migrate';
|
export type MigrateMode = 'copy' | 'migrate';
|
||||||
|
|
||||||
@@ -171,6 +173,10 @@ export class Mangas {
|
|||||||
return mangas.filter(Mangas.isPartiallyRead);
|
return mangas.filter(Mangas.isPartiallyRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getThumbnailUrl(manga: MangaLastFetchedInfo & Partial<MangaThumbnailInfo>): string {
|
||||||
|
return manga.thumbnailUrl ? `${manga.thumbnailUrl}?fetchedAt=${manga.lastFetchedAt}` : 'nonExistingMangaUrl';
|
||||||
|
}
|
||||||
|
|
||||||
static async getChapterIdsWithState(
|
static async getChapterIdsWithState(
|
||||||
mangaIds: number[],
|
mangaIds: number[],
|
||||||
state: Pick<ChapterConditionInput, 'isRead' | 'isDownloaded' | 'isBookmarked'>,
|
state: Pick<ChapterConditionInput, 'isRead' | 'isDownloaded' | 'isBookmarked'>,
|
||||||
|
|||||||
@@ -1499,10 +1499,6 @@ export class RequestManager {
|
|||||||
return this.doRequest(GQLMethod.USE_QUERY, GET_MIGRATABLE_SOURCE_MANGAS, { sourceId }, options);
|
return this.doRequest(GQLMethod.USE_QUERY, GET_MIGRATABLE_SOURCE_MANGAS, { sourceId }, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMangaThumbnailUrl(mangaId: number): string {
|
|
||||||
return this.getValidImgUrlFor(`manga/${mangaId}/thumbnail`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public useUpdateMangaCategories(
|
public useUpdateMangaCategories(
|
||||||
options?: MutationHookOptions<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables>,
|
options?: MutationHookOptions<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables>,
|
||||||
): AbortableApolloUseMutationResponse<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables> {
|
): AbortableApolloUseMutationResponse<UpdateMangaCategoriesMutation, UpdateMangaCategoriesMutationVariables> {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { UpdateChecker } from '@/components/library/UpdateChecker.tsx';
|
|||||||
import { StyledGroupedVirtuoso } from '@/components/virtuoso/StyledGroupedVirtuoso.tsx';
|
import { StyledGroupedVirtuoso } from '@/components/virtuoso/StyledGroupedVirtuoso.tsx';
|
||||||
import { StyledGroupHeader } from '@/components/virtuoso/StyledGroupHeader.tsx';
|
import { StyledGroupHeader } from '@/components/virtuoso/StyledGroupHeader.tsx';
|
||||||
import { StyledGroupItemWrapper } from '@/components/virtuoso/StyledGroupItemWrapper.tsx';
|
import { StyledGroupItemWrapper } from '@/components/virtuoso/StyledGroupItemWrapper.tsx';
|
||||||
|
import { Mangas } from '@/lib/data/Mangas.ts';
|
||||||
|
|
||||||
function epochToDate(epoch: number) {
|
function epochToDate(epoch: number) {
|
||||||
const date = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
const date = new Date(0); // The 0 there is the key, which sets the date to the epoch
|
||||||
@@ -200,7 +201,7 @@ export const Updates: React.FC = () => {
|
|||||||
marginRight: 2,
|
marginRight: 2,
|
||||||
imageRendering: 'pixelated',
|
imageRendering: 'pixelated',
|
||||||
}}
|
}}
|
||||||
src={requestManager.getValidImgUrlFor(manga.thumbnailUrl ?? '')}
|
src={Mangas.getThumbnailUrl(manga)}
|
||||||
/>
|
/>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
<Typography variant="h5" component="h2">
|
<Typography variant="h5" component="h2">
|
||||||
|
|||||||
Reference in New Issue
Block a user