fix(mobile): pin the chat composer above the keyboard on Android browsers
The chat screen relied on the browser's focused-field reveal, which holds on iOS Safari but not on Android, where interactive-widget is also widely ignored — the composer just stayed behind the keyboard. The draft screen's visual-viewport pin now covers the chat screen on Android; iOS chat keeps the native reveal.
This commit is contained in:
@@ -17,6 +17,15 @@ import React from 'react';
|
|||||||
import { isCapacitorApp } from '@/lib/platform';
|
import { isCapacitorApp } from '@/lib/platform';
|
||||||
import type { ComposerEditorHandle } from '../editor/ComposerEditor';
|
import type { ComposerEditorHandle } from '../editor/ComposerEditor';
|
||||||
|
|
||||||
|
// Android mobile browsers are the pan-mode holdouts this pin exists for on
|
||||||
|
// the CHAT screen too: interactive-widget=resizes-content is ignored by a
|
||||||
|
// fair share of Android WebView/Chrome builds, and unlike iOS Safari they do
|
||||||
|
// not reliably reveal the focused field either — the composer just stays
|
||||||
|
// behind the keyboard. iOS keeps its browser-native reveal on the chat
|
||||||
|
// screen, so this stays Android-only there.
|
||||||
|
// Callers are browser-only React effects, so navigator always exists here.
|
||||||
|
const isAndroidBrowser = (): boolean => /Android/i.test(navigator.userAgent);
|
||||||
|
|
||||||
export interface MobileViewportPinOptions {
|
export interface MobileViewportPinOptions {
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
/** Composer expanded to fullscreen on mobile. */
|
/** Composer expanded to fullscreen on mobile. */
|
||||||
@@ -96,12 +105,14 @@ export function useMobileViewportPin(options: MobileViewportPinOptions): void {
|
|||||||
};
|
};
|
||||||
}, [editorRef, formRef, isFullscreen, isMobile]);
|
}, [editorRef, formRef, isFullscreen, isMobile]);
|
||||||
|
|
||||||
// Draft screen with the keyboard up: anchor the normal-height composer to
|
// Keyboard up: anchor the normal-height composer to the visible bottom.
|
||||||
// the visible bottom. The chat screen does not need this — its own
|
// Draft screen on every mobile browser; chat screen only on Android,
|
||||||
// focused-field reveal works there.
|
// where neither viewport resizing nor the focused-field reveal can be
|
||||||
|
// relied on (iOS chat keeps the browser's own reveal).
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
if (!isMobile || isCapacitorApp()) return;
|
if (!isMobile || isCapacitorApp()) return;
|
||||||
if (!isDraftScreen || isFullscreen || !isFocused) return;
|
if (isFullscreen || !isFocused) return;
|
||||||
|
if (!isDraftScreen && !isAndroidBrowser()) return;
|
||||||
const vv = window.visualViewport;
|
const vv = window.visualViewport;
|
||||||
const form = formRef.current;
|
const form = formRef.current;
|
||||||
if (!vv || !form) return;
|
if (!vv || !form) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user