Files
readest/apps/readest-app/src/app/layout.tsx
T
Huang Xin dc58d985e7 fix(layout): center fixed modals above the on-screen keyboard on Android (#4091)
Adds `interactive-widget=resizes-content` to the viewport meta. iOS
Safari already shrinks the layout viewport when the keyboard opens,
so existing `fixed inset-0` flex-centered modals (PassphrasePrompt,
GroupingModal, etc.) auto-center in the visible space. Android
Chrome defaults to `resizes-visual` — only the visual viewport
shrinks, layout stays full-height — leaving those modals rendered
under the keyboard. Switching to `resizes-content` makes Android
match iOS without any per-modal JS.

Updates both the App Router viewport export (src/app/layout.tsx)
and the Pages Router fallback meta (src/pages/_app.tsx).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 14:31:37 +02:00

97 lines
2.8 KiB
TypeScript

import * as React from 'react';
import type { Metadata, Viewport } from 'next';
import { ViewTransitions } from 'next-view-transitions';
import { EnvProvider } from '@/context/EnvContext';
import Providers from '@/components/Providers';
import '../styles/globals.css';
const url = 'https://web.readest.com/';
const title = 'Readest — Where You Read, Digest and Get Insight';
const description =
'Discover Readest, the ultimate online ebook reader for immersive and organized reading. ' +
'Enjoy seamless access to your digital library, powerful tools for highlighting, bookmarking, ' +
'and note-taking, and support for multiple book views. ' +
'Perfect for deep reading, analysis, and understanding. Explore now!';
const previewImage = 'https://cdn.readest.com/images/open_graph_preview_read_now.png';
export const metadata: Metadata = {
metadataBase: new URL(url),
title: {
default: title,
template: '%s | Readest',
},
description,
generator: 'Next.js',
manifest: '/manifest.json',
keywords: ['epub', 'pdf', 'ebook', 'reader', 'readest', 'pwa'],
authors: [
{
name: 'readest',
url: 'https://github.com/readest/readest',
},
],
icons: {
icon: [{ url: '/icon.png' }, { url: '/favicon.ico' }],
apple: [{ url: '/apple-touch-icon.png', sizes: '180x180' }],
},
appleWebApp: {
capable: true,
title: 'Readest',
statusBarStyle: 'default',
},
openGraph: {
type: 'website',
url,
title,
description,
images: [previewImage],
},
twitter: {
card: 'summary_large_image',
title,
description,
images: [previewImage],
},
other: {
'apple-mobile-web-app-capable': 'yes',
'twitter:domain': 'web.readest.com',
'twitter:url': url,
},
};
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
minimumScale: 1,
maximumScale: 1,
userScalable: false,
viewportFit: 'cover',
// Make Android Chrome's layout viewport shrink when the on-screen
// keyboard opens (matches iOS behavior). Without this, Android's
// default `interactive-widget=resizes-visual` keeps the layout
// viewport at full screen height while only the visual viewport
// shrinks — causing fixed `inset-0`-centered modals (passphrase
// prompt, group picker, etc.) to render under the keyboard. With
// `resizes-content`, `100vh` / flex centering naturally targets
// the available space above the keyboard.
interactiveWidget: 'resizes-content',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html
lang='en'
className={process.env['NEXT_PUBLIC_APP_PLATFORM'] === 'tauri' ? 'edge-to-edge' : ''}
>
<body>
<ViewTransitions>
<EnvProvider>
<Providers>{children}</Providers>
</EnvProvider>
</ViewTransitions>
</body>
</html>
);
}