mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 00:07:01 +00:00
fix: open the keyboard in the body when replying on Android
This commit is contained in:
@@ -137,6 +137,19 @@ const RichTextEditor = React.forwardRef<RichTextEditorHandle, Props>(function Ri
|
||||
// posts its first message, then replay in order.
|
||||
const readyRef = React.useRef(false);
|
||||
const queuedRef = React.useRef<string[]>([]);
|
||||
// Focusing the editor in the page only moves the caret there. Android
|
||||
// opens the keyboard, and takes key focus off the composer's first button
|
||||
// (Close: a hardware Space shut the composer), only for a WebView that holds
|
||||
// native focus. iOS has keyboardDisplayRequiresUserAction for this.
|
||||
const wantsNativeFocusRef = React.useRef(false);
|
||||
const requestNativeFocus = React.useCallback(() => {
|
||||
if (Platform.OS !== 'android') return;
|
||||
if (!readyRef.current) {
|
||||
wantsNativeFocusRef.current = true;
|
||||
return;
|
||||
}
|
||||
webViewRef.current?.requestFocus();
|
||||
}, []);
|
||||
const call = React.useCallback((script: string) => {
|
||||
if (!readyRef.current) {
|
||||
queuedRef.current.push(script);
|
||||
@@ -203,15 +216,17 @@ const RichTextEditor = React.forwardRef<RichTextEditorHandle, Props>(function Ri
|
||||
call(`window.__rne && window.__rne.getHtml(${id})`);
|
||||
}),
|
||||
focusEnd: () => {
|
||||
requestNativeFocus();
|
||||
call(`window.__rne && window.__rne.focusEnd()`);
|
||||
},
|
||||
focus: () => {
|
||||
requestNativeFocus();
|
||||
call(`window.__rne && window.__rne.focus()`);
|
||||
},
|
||||
blur: () => {
|
||||
call(`document.activeElement && document.activeElement.blur()`);
|
||||
},
|
||||
}), [call]);
|
||||
}), [call, requestNativeFocus]);
|
||||
|
||||
const onMessage = (event: WebViewMessageEvent) => {
|
||||
let data: { type: string; payload: unknown };
|
||||
@@ -225,6 +240,12 @@ const RichTextEditor = React.forwardRef<RichTextEditorHandle, Props>(function Ri
|
||||
perfEndAfterPaint('compose-open');
|
||||
const queued = queuedRef.current;
|
||||
queuedRef.current = [];
|
||||
// A focus asked for before the page was up (a reply opening) gets the
|
||||
// view's native focus first, then the caret from the queued script.
|
||||
if (wantsNativeFocusRef.current) {
|
||||
wantsNativeFocusRef.current = false;
|
||||
webViewRef.current?.requestFocus();
|
||||
}
|
||||
for (const script of queued) webViewRef.current?.injectJavaScript(`${script}; true;`);
|
||||
}
|
||||
switch (data.type) {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
// Reply opened with no keyboard on Android and focus on the composer's
|
||||
// Close button (a hardware Space shut the composer). 8bb7b607 focused the
|
||||
// editor inside its page only; an Android WebView takes the keyboard and
|
||||
// key focus only with native view focus, which nothing gave it. The suite
|
||||
// has no React Native renderer, so this checks the editor's wiring.
|
||||
|
||||
const src = fs.readFileSync(path.resolve(__dirname, '..', '..', 'components/RichTextEditor.tsx'), 'utf8');
|
||||
|
||||
describe('rich text editor focus', () => {
|
||||
it('gives the WebView native focus before focusing the editor in the page', () => {
|
||||
const handle = src.slice(src.indexOf('focusEnd: () =>'), src.indexOf('blur: () =>'));
|
||||
expect(handle).toMatch(/focusEnd: \(\) => \{\s+requestNativeFocus\(\);\s+call\(`window\.__rne && window\.__rne\.focusEnd\(\)`\);/);
|
||||
expect(handle).toMatch(/focus: \(\) => \{\s+requestNativeFocus\(\);\s+call\(`window\.__rne && window\.__rne\.focus\(\)`\);/);
|
||||
});
|
||||
|
||||
it('asks for native focus on Android, and holds the request until the page is up', () => {
|
||||
const fn = src.slice(src.indexOf('const requestNativeFocus'), src.indexOf('const call = '));
|
||||
expect(fn).toMatch(/if \(Platform\.OS !== 'android'\) return;/);
|
||||
expect(fn).toMatch(/if \(!readyRef\.current\) \{\s+wantsNativeFocusRef\.current = true;\s+return;\s+\}\s+webViewRef\.current\?\.requestFocus\(\);/);
|
||||
const ready = src.slice(src.indexOf('if (!readyRef.current) {\r\n readyRef.current = true;'.replace(/\r\n/g, src.includes('\r\n') ? '\r\n' : '\n')));
|
||||
expect(ready).toMatch(/if \(wantsNativeFocusRef\.current\) \{\s+wantsNativeFocusRef\.current = false;\s+webViewRef\.current\?\.requestFocus\(\);\s+\}\s+for \(const script of queued\)/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user