DEV: introduces waitForClosedKeyboard (#26363)

This lib will allow us to wait for a keyboard state change. Not waiting for the keyboard to be closed could cause issues when showing a modal at the same time on iOS for example.

Example usage:
- blurSomeInput()
- await waitForClosedKeyboard(this)
- showSomeModal()

Note that this actual behavior has been baked in modals when we call show so you don't have to call the lib yourself.
This commit is contained in:
Joffrey JAFFEUX 2024-03-26 13:09:59 +01:00 committed by GitHub
parent 01c11dff91
commit f350cea5d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 77 additions and 2 deletions

View File

@ -66,7 +66,8 @@ export default {
} else {
let viewportWindowDiff =
this.windowInnerHeight - window.visualViewport.height;
if (viewportWindowDiff > 0) {
const IPAD_HARDWARE_KEYBOARD_TOOLBAR_HEIGHT = 71.5;
if (viewportWindowDiff > IPAD_HARDWARE_KEYBOARD_TOOLBAR_HEIGHT) {
keyboardVisible = true;
}

View File

@ -0,0 +1,71 @@
import { getOwner } from "@ember/application";
export async function waitForClosedKeyboard(context) {
if (!window.visualViewport) {
return;
}
const owner = getOwner(context);
const site = owner.lookup("service:site");
const capabilities = owner.lookup("service:capabilities");
if (!capabilities.isIpadOS && site.desktopView) {
return;
}
if (!document.documentElement.classList.contains("keyboard-visible")) {
return;
}
let timeout;
let viewportListener;
const initialWindowHeight = window.innerHeight;
await Promise.race([
new Promise((resolve) => {
timeout = setTimeout(() => {
// eslint-disable-next-line no-console
console.warn("Keyboard visibility didnt change after 1s.");
resolve();
}, 1000);
}),
new Promise((resolve) =>
window.visualViewport.addEventListener(
"resize",
(viewportListener = resolve),
{ once: true, passive: true }
)
),
]);
clearTimeout(timeout);
window.visualViewport.removeEventListener("resize", viewportListener);
if ("virtualKeyboard" in navigator) {
if (navigator.virtualKeyboard.boundingRect.height > 0) {
// eslint-disable-next-line no-console
console.warn("Expected virtual keyboard to be closed but it wasn't.");
return;
}
} else if (capabilities.isFirefox && capabilities.isAndroid) {
const KEYBOARD_DETECT_THRESHOLD = 150;
if (
Math.abs(
initialWindowHeight -
Math.min(window.innerHeight, window.visualViewport.height)
) > KEYBOARD_DETECT_THRESHOLD
) {
// eslint-disable-next-line no-console
console.warn("Expected virtual keyboard to be closed but it wasn't.");
return;
}
} else {
let viewportWindowDiff = initialWindowHeight - window.visualViewport.height;
if (viewportWindowDiff > 0) {
// eslint-disable-next-line no-console
console.warn("Expected virtual keyboard to be closed but it wasn't.");
return;
}
}
}

View File

@ -4,6 +4,7 @@ import Service, { service } from "@ember/service";
import { CLOSE_INITIATED_BY_MODAL_SHOW } from "discourse/components/d-modal";
import { clearAllBodyScrollLocks } from "discourse/lib/body-scroll-lock";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
import { waitForClosedKeyboard } from "discourse/lib/wait-for-keyboard";
import deprecated from "discourse-common/lib/deprecated";
const LEGACY_OPTS = new Set([
@ -39,7 +40,7 @@ export default class ModalService extends Service {
*
* @returns {Promise} A promise that resolves when the modal is closed, with any data passed to closeModal
*/
show(modal, opts) {
async show(modal, opts) {
if (typeof modal === "string") {
this.dialog.alert(
`Error: the '${modal}' modal needs updating to work with the latest version of Discourse. See https://meta.discourse.org/t/268057.`
@ -59,6 +60,8 @@ export default class ModalService extends Service {
this.close({ initiatedBy: CLOSE_INITIATED_BY_MODAL_SHOW });
await waitForClosedKeyboard(this);
let resolveShowPromise;
const promise = new Promise((resolve) => {
resolveShowPromise = resolve;