From ab58b0cffe5d8479037e9de346b3e323bf2e6b54 Mon Sep 17 00:00:00 2001 From: Rafael dos Santos Silva Date: Mon, 26 Sep 2022 17:35:58 -0300 Subject: [PATCH] FIX: Better virtual keyboard detect on Android (#18298) * FIX: Better virtual keyboard detect on Android Firefox has a bug where *sometimes* the visualViewport.height won't be updated when the keyboard pops up until you scroll, making our composer stay hidden behind the keyboard. This commit uses both window.innerHeight and window.visualViewport.height using the minimum of both to check for height changes. For Chrome/Edge we feature detect the new VirtualKeyboard API and opt-into it when the composer opens and use it to detect if a keyboard is being draw. Opting into the API changes how the viewport is calculated so we have to also change how the full height composer is calculated. To minimize breakage we opt-out when the composer component is destroyed. This commit also moves the `--composer-ipad-padding` to only happen on iPads. Bug report at https://meta.discourse.org/t/-/228382 --- .../app/initializers/mobile-keyboard.js | 64 +++++++++++++++---- .../stylesheets/common/base/compose.scss | 1 + app/assets/stylesheets/mobile/compose.scss | 1 + 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/discourse/app/initializers/mobile-keyboard.js b/app/assets/javascripts/discourse/app/initializers/mobile-keyboard.js index 72c343511d1..816f8e15b5b 100644 --- a/app/assets/javascripts/discourse/app/initializers/mobile-keyboard.js +++ b/app/assets/javascripts/discourse/app/initializers/mobile-keyboard.js @@ -6,9 +6,9 @@ export default { initialize(container) { const site = container.lookup("service:site"); - const capabilities = container.lookup("capabilities:main"); + this.capabilities = container.lookup("capabilities:main"); - if (!capabilities.isIpadOS && !site.mobileView) { + if (!this.capabilities.isIpadOS && !site.mobileView) { return; } @@ -21,27 +21,67 @@ export default { this.onViewportResize(); window.visualViewport.addEventListener("resize", this.onViewportResize); + if ("virtualKeyboard" in navigator) { + navigator.virtualKeyboard.overlaysContent = true; + navigator.virtualKeyboard.addEventListener( + "geometrychange", + this.onViewportResize + ); + } }, teardown() { window.visualViewport.removeEventListener("resize", this.onViewportResize); + if ("virtualKeyboard" in navigator) { + navigator.virtualKeyboard.overlaysContent = false; + navigator.virtualKeyboard.removeEventListener( + "geometrychange", + this.onViewportResize + ); + } }, @bind onViewportResize() { - const composerVH = window.visualViewport.height * 0.01; - const doc = document.documentElement; + const composerVH = window.visualViewport.height * 0.01, + doc = document.documentElement, + KEYBOARD_DETECT_THRESHOLD = 150; doc.style.setProperty("--composer-vh", `${composerVH}px`); - const heightDiff = this.windowInnerHeight - window.visualViewport.height; - doc.classList.toggle("keyboard-visible", heightDiff > 0); + let keyboardVisible = false; + if ("virtualKeyboard" in navigator) { + if (navigator.virtualKeyboard.boundingRect.height > 0) { + keyboardVisible = true; + } + } else if (this.capabilities.isFirefox && this.capabilities.isAndroid) { + if ( + Math.abs( + this.windowInnerHeight - + Math.min(window.innerHeight, window.visualViewport.height) + ) > KEYBOARD_DETECT_THRESHOLD + ) { + keyboardVisible = true; + } + } else { + let viewportWindowDiff = + this.windowInnerHeight - window.visualViewport.height; + if (viewportWindowDiff > 0) { + keyboardVisible = true; + } - // Add bottom padding when using a hardware keyboard and the accessory bar - // is visible accessory bar height is 55px, using 75 allows a small buffer - doc.style.setProperty( - "--composer-ipad-padding", - `${heightDiff < 75 ? heightDiff : 0}px` - ); + // adds bottom padding when using a hardware keyboard and the accessory bar is visible + // accessory bar height is 55px, using 75 allows a small buffer + if (this.capabilities.isIpadOS) { + doc.style.setProperty( + "--composer-ipad-padding", + `${viewportWindowDiff < 75 ? viewportWindowDiff : 0}px` + ); + } + } + + keyboardVisible + ? doc.classList.add("keyboard-visible") + : doc.classList.remove("keyboard-visible"); }, }; diff --git a/app/assets/stylesheets/common/base/compose.scss b/app/assets/stylesheets/common/base/compose.scss index 6aaa9b4632b..9966197e261 100644 --- a/app/assets/stylesheets/common/base/compose.scss +++ b/app/assets/stylesheets/common/base/compose.scss @@ -539,6 +539,7 @@ body:not(.ios-safari-composer-hacks) { min-height: calc(var(--min-height) - 4em); } padding-bottom: var(--composer-ipad-padding); + padding-bottom: calc(10px + env(keyboard-inset-height)); } } diff --git a/app/assets/stylesheets/mobile/compose.scss b/app/assets/stylesheets/mobile/compose.scss index 3e610a7c065..31b9b75f568 100644 --- a/app/assets/stylesheets/mobile/compose.scss +++ b/app/assets/stylesheets/mobile/compose.scss @@ -24,6 +24,7 @@ .keyboard-visible &.open { height: 100%; // Android: Reduces composer jumpiness when the keyboard toggles + height: calc(100% - (10px + env(keyboard-inset-height))); } .keyboard-visible body.ios-safari-composer-hacks &.open {