Include chapter info in continue reading button tooltip

This commit is contained in:
schroda
2025-09-18 20:37:51 +02:00
parent 5456428916
commit 80a576db63
10 changed files with 252 additions and 58 deletions

View File

@@ -47,3 +47,5 @@ export type ChapterSourceOrderInfo = Pick<ChapterType, 'sourceOrder'>;
export type ChapterScanlatorInfo = Pick<ChapterType, 'scanlator'>;
export type ChapterRealUrlInfo = Pick<ChapterType, 'realUrl'>;
export type ChapterNameInfo = Pick<ChapterType, 'name'>;

View File

@@ -28,7 +28,7 @@ export type MangaCardMode = 'default' | 'source' | 'migrate.search' | 'migrate.s
type MangaCardBaseProps = Pick<MangaTypeGql, 'id' | 'title' | 'sourceId'> &
Omit<SingleModeProps['manga'], 'downloadCount' | 'unreadCount' | 'chapters'> &
Partial<Pick<MangaTypeGql, 'inLibrary' | 'downloadCount' | 'unreadCount'>> & {
firstUnreadChapter?: Pick<ChapterType, 'id' | 'sourceOrder' | 'isRead'> | null;
firstUnreadChapter?: Pick<ChapterType, 'id' | 'sourceOrder' | 'isRead' | 'chapterNumber' | 'name'> | null;
};
export type MangaIdInfo = Pick<MangaTypeGql, 'id'>;

View File

@@ -6,14 +6,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useTranslation } from 'react-i18next';
import Button from '@mui/material/Button';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import { Link } from 'react-router-dom';
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
import { Chapters } from '@/features/chapter/services/Chapters.ts';
import { MUIUtil } from '@/lib/mui/MUI.util.ts';
import { ChapterReadInfo, ChapterSourceOrderInfo } from '@/features/chapter/Chapter.types.ts';
import {
ChapterNameInfo,
ChapterNumberInfo,
ChapterReadInfo,
ChapterScanlatorInfo,
ChapterSourceOrderInfo,
} from '@/features/chapter/Chapter.types.ts';
import { ContinueReadingTooltip } from '@/features/manga/components/ContinueReadingTooltip.tsx';
export const ContinueReadingButton = ({
showContinueReadingButton,
@@ -21,32 +26,36 @@ export const ContinueReadingButton = ({
mangaLinkTo,
}: {
showContinueReadingButton: boolean;
chapter?: (ChapterSourceOrderInfo & ChapterReadInfo) | null;
chapter?:
| (ChapterSourceOrderInfo & ChapterReadInfo & ChapterNumberInfo & ChapterNameInfo & ChapterScanlatorInfo)
| null;
mangaLinkTo: string;
}) => {
const { t } = useTranslation();
if (!showContinueReadingButton || !chapter) {
return null;
}
const { sourceOrder } = chapter;
const isFirstChapter = sourceOrder === 1;
const { sourceOrder, chapterNumber, name, scanlator } = chapter;
return (
<CustomTooltip title={t(isFirstChapter ? 'global.button.start' : 'global.button.resume')}>
<ContinueReadingTooltip
chapterNumber={chapterNumber}
name={name}
sourceOrder={sourceOrder}
scanlator={scanlator}
>
<Button
{...MUIUtil.preventRippleProp()}
variant="contained"
size="small"
sx={{ minWidth: 'unset', py: 0.5, px: 0.75 }}
component={Link}
to={`${mangaLinkTo}/chapter/${chapter.sourceOrder}`}
to={`${mangaLinkTo}/chapter/${sourceOrder}`}
state={Chapters.getReaderOpenChapterLocationState(chapter)}
onClick={(e) => e.stopPropagation()}
>
<PlayArrowIcon />
</Button>
</CustomTooltip>
</ContinueReadingTooltip>
);
};

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import { CustomTooltip } from '@/base/components/CustomTooltip';
import {
ChapterNameInfo,
ChapterNumberInfo,
ChapterScanlatorInfo,
ChapterSourceOrderInfo,
} from '@/features/chapter/Chapter.types.ts';
export const ContinueReadingTooltip = ({
children,
chapterNumber,
name,
sourceOrder,
scanlator,
}: ChapterNumberInfo &
ChapterSourceOrderInfo &
ChapterNameInfo &
ChapterScanlatorInfo & {
children: ReactElement;
}) => {
const { t } = useTranslation();
const isFirstChapter = sourceOrder === 1;
return (
<CustomTooltip
slotProps={{
tooltip: {
sx: {
whiteSpace: 'pre-line',
},
},
}}
title={t(isFirstChapter ? 'chapter.action.read.start' : 'chapter.action.read.resume', {
chapterNumber,
title: name,
scanlator,
})}
>
{children}
</CustomTooltip>
);
};

View File

@@ -11,22 +11,48 @@ import PlayArrow from '@mui/icons-material/PlayArrow';
import { useTranslation } from 'react-i18next';
import { StyledFab } from '@/base/components/buttons/StyledFab.tsx';
import { Chapters } from '@/features/chapter/services/Chapters.ts';
import { ChapterMangaInfo, ChapterReadInfo, ChapterSourceOrderInfo } from '@/features/chapter/Chapter.types.ts';
import {
ChapterMangaInfo,
ChapterNameInfo,
ChapterNumberInfo,
ChapterReadInfo,
ChapterScanlatorInfo,
ChapterSourceOrderInfo,
} from '@/features/chapter/Chapter.types.ts';
import { ContinueReadingTooltip } from '@/features/manga/components/ContinueReadingTooltip.tsx';
export function ResumeFab({ chapter }: { chapter: ChapterMangaInfo & ChapterSourceOrderInfo & ChapterReadInfo }) {
export function ResumeFab({
chapter,
}: {
chapter: ChapterMangaInfo &
ChapterSourceOrderInfo &
ChapterReadInfo &
ChapterNumberInfo &
ChapterNameInfo &
ChapterScanlatorInfo;
}) {
const { t } = useTranslation();
const { sourceOrder } = chapter;
const { sourceOrder, name, chapterNumber, scanlator } = chapter;
const isFirstChapter = sourceOrder === 1;
return (
<StyledFab
component={Link}
variant="extended"
color="primary"
to={Chapters.getReaderUrl(chapter)}
state={Chapters.getReaderOpenChapterLocationState(chapter)}
<ContinueReadingTooltip
chapterNumber={chapterNumber}
name={name}
sourceOrder={sourceOrder}
scanlator={scanlator}
>
<PlayArrow />
{sourceOrder === 1 ? t('global.button.start') : t('global.button.resume')}
</StyledFab>
<StyledFab
component={Link}
variant="extended"
color="primary"
to={Chapters.getReaderUrl(chapter)}
state={Chapters.getReaderOpenChapterLocationState(chapter)}
>
<PlayArrow />
{isFirstChapter ? t('global.button.start') : t('global.button.resume')}
</StyledFab>
</ContinueReadingTooltip>
);
}

View File

@@ -35,7 +35,7 @@ import {
AuthMode,
DatabaseType,
KoreaderSyncChecksumMethod,
KoreaderSyncStrategy,
KoreaderSyncLegacyStrategy,
SortOrder,
} from '@/lib/graphql/generated/graphql';
import {
@@ -515,15 +515,27 @@ export const ServerSettings = () => {
]}
handleChange={(value) => updateSetting('koreaderSyncChecksumMethod', value)}
/>
<SelectSetting<KoreaderSyncStrategy>
<SelectSetting<KoreaderSyncLegacyStrategy>
settingName={t('settings.server.koreader.sync.strategy.title')}
value={serverSettings.koreaderSyncStrategy}
values={[
[KoreaderSyncStrategy.Disabled, { text: t('settings.server.koreader.sync.strategy.disabled') }],
[KoreaderSyncStrategy.Prompt, { text: t('settings.server.koreader.sync.strategy.prompt') }],
[KoreaderSyncStrategy.Silent, { text: t('settings.server.koreader.sync.strategy.silent') }],
[KoreaderSyncStrategy.Send, { text: t('settings.server.koreader.sync.strategy.send') }],
[KoreaderSyncStrategy.Receive, { text: t('settings.server.koreader.sync.strategy.receive') }],
[
KoreaderSyncLegacyStrategy.Disabled,
{ text: t('settings.server.koreader.sync.strategy.disabled') },
],
[
KoreaderSyncLegacyStrategy.Prompt,
{ text: t('settings.server.koreader.sync.strategy.prompt') },
],
[
KoreaderSyncLegacyStrategy.Silent,
{ text: t('settings.server.koreader.sync.strategy.silent') },
],
[KoreaderSyncLegacyStrategy.Send, { text: t('settings.server.koreader.sync.strategy.send') }],
[
KoreaderSyncLegacyStrategy.Receive,
{ text: t('settings.server.koreader.sync.strategy.receive') },
],
]}
handleChange={(value) => updateSetting('koreaderSyncStrategy', value)}
/>