From 3a66f2a8dafd81ee4ba560577ae5614973e7cef8 Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 9 Nov 2026 10:51:20 +0100 Subject: [PATCH] fix(native): the keyboard on the claim screen closes again On "Almost there" nothing put the keyboard away: the web view keeps it open on a tap beside the fields, and "Next" sent the half-filled form, whose check focused a field again. A tap beside the fields or a drag of the screen now closes it, as in an iOS app, on every first screen. "Next" walks from the address to the email to the password, "Go" buys, and the keyboard goes as the purchase starts. --- client/native-shell/main.ts | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/client/native-shell/main.ts b/client/native-shell/main.ts index 7e7d8e112..dd1b264ed 100644 --- a/client/native-shell/main.ts +++ b/client/native-shell/main.ts @@ -767,6 +767,7 @@ function showClaim( slugInput.setAttribute('autocomplete', 'off'); slugInput.setAttribute('maxlength', '40'); slugInput.setAttribute('aria-label', t('native.claim.addressLabel')); + slugInput.setAttribute('enterkeyhint', 'next'); // "name.liketrek.com" is one address, left to right in every language. const slugField = el( 'div', @@ -831,6 +832,17 @@ function showClaim( error ); + // "Next" moves on to the next field instead of sending the form half filled; + // "Go" on the password sends it. + const nextOnEnter = (from: HTMLInputElement, to: HTMLInputElement) => + from.addEventListener('keydown', (e) => { + if (e.key !== 'Enter' || e.isComposing) return; + e.preventDefault(); + to.focus(); + }); + nextOnEnter(slugInput, emailInput); + nextOnEnter(emailInput, passwordInput); + let status: SlugStatus | null = null; let timer: ReturnType | undefined; const noteFor: Partial> = { @@ -888,6 +900,8 @@ function showClaim( passwordHint.className = 'hint'; void tap(ImpactStyle.Medium); + // The purchase sheet and the progress want the whole screen, not the keyboard. + passwordInput.blur(); const lock = (on: boolean) => { slugInput.disabled = on; emailInput.disabled = on; @@ -1133,9 +1147,38 @@ function followBackGesture(): void { } } +/** + * The keyboard closes the ways it does in an iOS app: a tap beside the fields, + * or a drag of the screen. A web view keeps it open for both, and on the claim + * screen the keyboard covered the hints and the terms with no way to put it away. + */ +function letKeyboardClose(): void { + const isField = (target: EventTarget | null) => + target instanceof Element && !!target.closest('input, textarea, select, [contenteditable="true"], .field'); + const close = () => { + const active = document.activeElement; + if (active instanceof HTMLElement && isField(active)) active.blur(); + }; + document.addEventListener('pointerdown', (e) => { + if (!isField(e.target)) close(); + }, true); + let startY: number | null = null; + document.addEventListener('touchstart', (e) => { + startY = isField(e.target) ? null : (e.touches[0]?.clientY ?? null); + }, { passive: true }); + document.addEventListener('touchmove', (e) => { + const y = e.touches[0]?.clientY; + if (startY !== null && y !== undefined && Math.abs(y - startY) > 10) { + startY = null; + close(); + } + }, { passive: true }); +} + async function start(): Promise { void paintStatusBar(); followBackGesture(); + letKeyboardClose(); let url: string | null; try { ({ url } = await TrekShell.getServer());