forked from akai/readest
fix(reader): suppress Android image callout freezing the image viewer (#4425)
Long-pressing an image in the zoom viewer on Android triggers the WebView's native image callout (context menu / drag / magnifier) at the same time as the viewer's own pinch/pan/zoom touch handlers, locking up the whole app until restart. Same root cause as the book-cover freeze (PR #4345): `-webkit-touch-callout: none` doesn't inherit, so the class must sit on an ancestor of the `<img>`. Apply the existing `.no-context-menu` class to the viewer container so the `.no-context-menu img` rule reaches the zoomed image and disables the native callout. Harmless on desktop (the property is a no-op there and right-click-save still works). Closes #4420 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { render, cleanup } from '@testing-library/react';
|
||||
|
||||
import ImageViewer from '@/app/reader/components/ImageViewer';
|
||||
|
||||
vi.mock('@/hooks/useTranslation', () => ({
|
||||
useTranslation: () => (s: string) => s,
|
||||
}));
|
||||
|
||||
// useKeyDownActions pulls in EnvContext + device store; not under test here.
|
||||
vi.mock('@/hooks/useKeyDownActions', () => ({
|
||||
useKeyDownActions: () => {},
|
||||
}));
|
||||
|
||||
// ZoomControls reaches into the theme store and Tauri window APIs; stub it out.
|
||||
vi.mock('@/app/reader/components/ZoomControls', () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
}));
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const gridInsets = { top: 0, right: 0, bottom: 0, left: 0 };
|
||||
|
||||
describe('ImageViewer', () => {
|
||||
it('suppresses the native image callout on the zoomed image', () => {
|
||||
// The WebView's native long-press image callout collides with the
|
||||
// viewer's own pinch/pan handlers on Android and freezes the app. The
|
||||
// zoomed <img> must live under a `.no-context-menu` ancestor so the
|
||||
// global `.no-context-menu img { -webkit-touch-callout: none }` rule
|
||||
// disables that callout. Mirrors the book-cover fix (PR #4345).
|
||||
const { container } = render(
|
||||
<ImageViewer src='blob:test-image' onClose={vi.fn()} gridInsets={gridInsets} />,
|
||||
);
|
||||
|
||||
const calloutSafeImage = container.querySelector('.no-context-menu img');
|
||||
expect(calloutSafeImage).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -367,12 +367,15 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
||||
if (!src) return null;
|
||||
|
||||
return (
|
||||
// `no-context-menu` suppresses the WebView's native long-press image
|
||||
// callout (via the `.no-context-menu img` rule). On Android it otherwise
|
||||
// collides with the pinch/pan handlers below and freezes the app.
|
||||
<div
|
||||
ref={containerRef}
|
||||
tabIndex={-1}
|
||||
role='button'
|
||||
aria-label={_('Image viewer')}
|
||||
className='fixed inset-0 z-50 flex items-center justify-center outline-none'
|
||||
className='no-context-menu fixed inset-0 z-50 flex items-center justify-center outline-none'
|
||||
onKeyDown={handleKeyDown}
|
||||
onWheel={handleWheel}
|
||||
onTouchMove={onTouchMove}
|
||||
|
||||
Reference in New Issue
Block a user