Add WebView support
This commit is contained in:
@@ -104,7 +104,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"label": {
|
"label": {
|
||||||
"open_on_source": "Open on source",
|
|
||||||
"select": "Select"
|
"select": "Select"
|
||||||
},
|
},
|
||||||
"mark_as_read": {
|
"mark_as_read": {
|
||||||
@@ -349,7 +348,8 @@
|
|||||||
"migrate": "Migrate",
|
"migrate": "Migrate",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"open": "Open",
|
"open": "Open",
|
||||||
"open_site": "Open Site",
|
"open_browser": "Open in browser",
|
||||||
|
"open_webview": "Open in WebView",
|
||||||
"options": "Options",
|
"options": "Options",
|
||||||
"popular": "Popular",
|
"popular": "Popular",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
|
|||||||
11
src/assets/icons/IconBrowser.tsx
Normal file
11
src/assets/icons/IconBrowser.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* 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 LaunchIcon from '@mui/icons-material/Launch';
|
||||||
|
|
||||||
|
export const IconBrowser = LaunchIcon;
|
||||||
11
src/assets/icons/IconWebView.tsx
Normal file
11
src/assets/icons/IconWebView.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* 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 OpenInBrowserIcon from '@mui/icons-material/OpenInBrowser';
|
||||||
|
|
||||||
|
export const IconWebView = OpenInBrowserIcon;
|
||||||
@@ -466,6 +466,10 @@ export class RequestManager {
|
|||||||
return `${this.getBaseUrl()}${apiVersion}${endpoint}`;
|
return `${this.getBaseUrl()}${apiVersion}${endpoint}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getWebviewUrl(url: string): string {
|
||||||
|
return `${this.getValidUrlFor('webview')}#${url}`;
|
||||||
|
}
|
||||||
|
|
||||||
public clearBrowseCacheFor(sourceId: string) {
|
public clearBrowseCacheFor(sourceId: string) {
|
||||||
const cacheKeys = this.cache.getMatchingKeys(
|
const cacheKeys = this.cache.getMatchingKeys(
|
||||||
new RegExp(`${CACHE_INITIAL_PAGES_FETCHING_KEY}|${CACHE_PAGES_KEY}|${CACHE_RESULTS_KEY}.*${sourceId}`),
|
new RegExp(`${CACHE_INITIAL_PAGES_FETCHING_KEY}|${CACHE_PAGES_KEY}|${CACHE_RESULTS_KEY}.*${sourceId}`),
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import BookmarkRemove from '@mui/icons-material/BookmarkRemove';
|
|||||||
import BookmarkAdd from '@mui/icons-material/BookmarkAdd';
|
import BookmarkAdd from '@mui/icons-material/BookmarkAdd';
|
||||||
import DoneAll from '@mui/icons-material/DoneAll';
|
import DoneAll from '@mui/icons-material/DoneAll';
|
||||||
import { ComponentProps, useMemo } from 'react';
|
import { ComponentProps, useMemo } from 'react';
|
||||||
import LaunchIcon from '@mui/icons-material/Launch';
|
|
||||||
import { SelectableCollectionReturnType } from '@/modules/collection/hooks/useSelectableCollection.ts';
|
import { SelectableCollectionReturnType } from '@/modules/collection/hooks/useSelectableCollection.ts';
|
||||||
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
import { Chapters } from '@/modules/chapter/services/Chapters.ts';
|
||||||
import { MenuItem } from '@/modules/core/components/menu/MenuItem.tsx';
|
import { MenuItem } from '@/modules/core/components/menu/MenuItem.tsx';
|
||||||
@@ -41,6 +40,8 @@ import {
|
|||||||
ChapterReadInfo,
|
ChapterReadInfo,
|
||||||
ChapterRealUrlInfo,
|
ChapterRealUrlInfo,
|
||||||
} from '@/modules/chapter/Chapter.types.ts';
|
} from '@/modules/chapter/Chapter.types.ts';
|
||||||
|
import { IconWebView } from '@/assets/icons/IconWebView.tsx';
|
||||||
|
import { IconBrowser } from '@/assets/icons/IconBrowser.tsx';
|
||||||
|
|
||||||
type BaseProps = { onClose: () => void; selectable?: boolean };
|
type BaseProps = { onClose: () => void; selectable?: boolean };
|
||||||
|
|
||||||
@@ -173,15 +174,30 @@ export const ChapterActionMenuItems = ({
|
|||||||
<MenuItem onClick={handleSelect} Icon={CheckBoxOutlineBlank} title={t('chapter.action.label.select')} />
|
<MenuItem onClick={handleSelect} Icon={CheckBoxOutlineBlank} title={t('chapter.action.label.select')} />
|
||||||
)}
|
)}
|
||||||
{isSingleMode && (
|
{isSingleMode && (
|
||||||
<MenuItem
|
<>
|
||||||
Icon={LaunchIcon}
|
<MenuItem
|
||||||
disabled={!chapter!.realUrl}
|
Icon={IconBrowser}
|
||||||
onClick={() => {
|
disabled={!chapter!.realUrl}
|
||||||
window.open(chapter!.realUrl!, '_blank', 'noopener,noreferrer');
|
onClick={() => {
|
||||||
onClose();
|
window.open(chapter!.realUrl!, '_blank', 'noopener,noreferrer');
|
||||||
}}
|
onClose();
|
||||||
title={t('chapter.action.label.open_on_source')}
|
}}
|
||||||
/>
|
title={t('global.button.open_browser')}
|
||||||
|
/>
|
||||||
|
<MenuItem
|
||||||
|
Icon={IconWebView}
|
||||||
|
disabled={!chapter!.realUrl}
|
||||||
|
onClick={() => {
|
||||||
|
window.open(
|
||||||
|
requestManager.getWebviewUrl(chapter!.realUrl!),
|
||||||
|
'_blank',
|
||||||
|
'noopener,noreferrer',
|
||||||
|
);
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
title={t('global.button.open_webview')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{shouldShowMenuItem(canBeDownloaded) && (
|
{shouldShowMenuItem(canBeDownloaded) && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { t as translate } from 'i18next';
|
import { t as translate } from 'i18next';
|
||||||
import Link from '@mui/material/Link';
|
import Link from '@mui/material/Link';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import LaunchIcon from '@mui/icons-material/Launch';
|
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||||
|
import ButtonGroup from '@mui/material/ButtonGroup';
|
||||||
import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx';
|
import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx';
|
||||||
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
||||||
import { Mangas } from '@/modules/manga/services/Mangas.ts';
|
import { Mangas } from '@/modules/manga/services/Mangas.ts';
|
||||||
@@ -51,6 +51,9 @@ import { SourceIdInfo } from '@/modules/source/Source.types.ts';
|
|||||||
import { Thumbnail } from '@/modules/manga/components/details/Thumbnail.tsx';
|
import { Thumbnail } from '@/modules/manga/components/details/Thumbnail.tsx';
|
||||||
import { DescriptionGenre } from '@/modules/manga/components/details/DescriptionGenre.tsx';
|
import { DescriptionGenre } from '@/modules/manga/components/details/DescriptionGenre.tsx';
|
||||||
import { SearchLink } from '@/modules/manga/components/details/SearchLink.tsx';
|
import { SearchLink } from '@/modules/manga/components/details/SearchLink.tsx';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
import { IconBrowser } from '@/assets/icons/IconBrowser.tsx';
|
||||||
|
import { IconWebView } from '@/assets/icons/IconWebView.tsx';
|
||||||
|
|
||||||
const DetailsWrapper = styled('div')(({ theme }) => ({
|
const DetailsWrapper = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -136,19 +139,34 @@ const OpenSourceButton = ({ url }: { url?: string | null }) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomTooltip title={t('global.button.open_site')} disabled={!url}>
|
<ButtonGroup>
|
||||||
<CustomButtonIcon
|
<CustomTooltip title={t('global.button.open_browser')} disabled={!url}>
|
||||||
size="medium"
|
<CustomButtonIcon
|
||||||
disabled={!url}
|
size="medium"
|
||||||
component={Link}
|
disabled={!url}
|
||||||
href={url ?? undefined}
|
component={Link}
|
||||||
target="_blank"
|
href={url ?? undefined}
|
||||||
rel="noreferrer"
|
target="_blank"
|
||||||
variant="outlined"
|
rel="noreferrer"
|
||||||
>
|
variant="outlined"
|
||||||
<LaunchIcon />
|
>
|
||||||
</CustomButtonIcon>
|
<IconBrowser />
|
||||||
</CustomTooltip>
|
</CustomButtonIcon>
|
||||||
|
</CustomTooltip>
|
||||||
|
<CustomTooltip title={t('global.button.open_webview')} disabled={!url}>
|
||||||
|
<CustomButtonIcon
|
||||||
|
size="medium"
|
||||||
|
disabled={!url}
|
||||||
|
component={Link}
|
||||||
|
href={url ? requestManager.getWebviewUrl(url) : undefined}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
<IconWebView />
|
||||||
|
</CustomButtonIcon>
|
||||||
|
</CustomTooltip>
|
||||||
|
</ButtonGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import { ReaderBookmarkButton } from '@/modules/reader/components/overlay/naviga
|
|||||||
import { FALLBACK_CHAPTER } from '@/modules/chapter/Chapter.constants.ts';
|
import { FALLBACK_CHAPTER } from '@/modules/chapter/Chapter.constants.ts';
|
||||||
import { FALLBACK_MANGA } from '@/modules/manga/Manga.constants.ts';
|
import { FALLBACK_MANGA } from '@/modules/manga/Manga.constants.ts';
|
||||||
import { ReaderExitButton } from '@/modules/reader/components/overlay/navigation/ReaderExitButton.tsx';
|
import { ReaderExitButton } from '@/modules/reader/components/overlay/navigation/ReaderExitButton.tsx';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
|
||||||
const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' };
|
const DEFAULT_MANGA = { ...FALLBACK_MANGA, title: '' };
|
||||||
|
|
||||||
@@ -107,7 +108,16 @@ const BaseReaderOverlayHeaderMobile = forwardRef<
|
|||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
{t('chapter.action.label.open_on_source')}
|
{t('global.button.open_browser')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
component={Link}
|
||||||
|
disabled={!realUrl}
|
||||||
|
href={realUrl ? requestManager.getWebviewUrl(realUrl) : ''}
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{t('global.button.open_webview')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
disabled={!realUrl}
|
disabled={!realUrl}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
||||||
import DownloadIcon from '@mui/icons-material/Download';
|
import DownloadIcon from '@mui/icons-material/Download';
|
||||||
import ReplayIcon from '@mui/icons-material/Replay';
|
import ReplayIcon from '@mui/icons-material/Replay';
|
||||||
import { memo, useMemo, useRef } from 'react';
|
import { memo, useMemo, useRef } from 'react';
|
||||||
@@ -25,6 +24,9 @@ import { userReaderStatePagesContext } from '@/modules/reader/contexts/state/Rea
|
|||||||
import { ReaderLibraryButton } from '@/modules/reader/components/overlay/navigation/ReaderLibraryButton.tsx';
|
import { ReaderLibraryButton } from '@/modules/reader/components/overlay/navigation/ReaderLibraryButton.tsx';
|
||||||
import { ReaderBookmarkButton } from '@/modules/reader/components/overlay/navigation/ReaderBookmarkButton.tsx';
|
import { ReaderBookmarkButton } from '@/modules/reader/components/overlay/navigation/ReaderBookmarkButton.tsx';
|
||||||
import { CHAPTER_ACTION_TO_TRANSLATION, FALLBACK_CHAPTER } from '@/modules/chapter/Chapter.constants.ts';
|
import { CHAPTER_ACTION_TO_TRANSLATION, FALLBACK_CHAPTER } from '@/modules/chapter/Chapter.constants.ts';
|
||||||
|
import { IconBrowser } from '@/assets/icons/IconBrowser.tsx';
|
||||||
|
import { IconWebView } from '@/assets/icons/IconWebView.tsx';
|
||||||
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
|
|
||||||
const DownloadButton = ({ currentChapter }: Required<Pick<ReaderStateChapters, 'currentChapter'>>) => {
|
const DownloadButton = ({ currentChapter }: Required<Pick<ReaderStateChapters, 'currentChapter'>>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -100,7 +102,7 @@ const BaseReaderNavBarDesktopActions = memo(
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomTooltip>
|
</CustomTooltip>
|
||||||
<DownloadButton currentChapter={currentChapter} />
|
<DownloadButton currentChapter={currentChapter} />
|
||||||
<CustomTooltip title={t('chapter.action.label.open_on_source')} disabled={!realUrl}>
|
<CustomTooltip title={t('global.button.open_browser')} disabled={!realUrl}>
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={!realUrl}
|
disabled={!realUrl}
|
||||||
href={realUrl ?? ''}
|
href={realUrl ?? ''}
|
||||||
@@ -108,7 +110,18 @@ const BaseReaderNavBarDesktopActions = memo(
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
>
|
>
|
||||||
<OpenInNewIcon />
|
<IconBrowser />
|
||||||
|
</IconButton>
|
||||||
|
</CustomTooltip>
|
||||||
|
<CustomTooltip title={t('global.button.open_webview')} disabled={!realUrl}>
|
||||||
|
<IconButton
|
||||||
|
disabled={!realUrl}
|
||||||
|
href={realUrl ? requestManager.getWebviewUrl(realUrl) : ''}
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
color="inherit"
|
||||||
|
>
|
||||||
|
<IconWebView />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</CustomTooltip>
|
</CustomTooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
Reference in New Issue
Block a user