diff --git a/app/assets/javascripts/discourse/app/components/d-composer-position.gjs b/app/assets/javascripts/discourse/app/components/d-composer-position.gjs
index f3ab33d601e..2c0a5ffc783 100644
--- a/app/assets/javascripts/discourse/app/components/d-composer-position.gjs
+++ b/app/assets/javascripts/discourse/app/components/d-composer-position.gjs
@@ -8,19 +8,14 @@ export default class DComposerPosition extends Component {
constructor() {
super(...arguments);
- if (!window.visualViewport) {
- return;
- }
-
const html = document.documentElement;
if (
- html.classList.contains("ios-device") ||
+ html.classList.contains("mobile-device") ||
html.classList.contains("ipados-device")
) {
window.addEventListener("scroll", this._correctScrollPosition);
this._correctScrollPosition();
-
const editor = document.querySelector(".d-editor-input");
editor?.addEventListener("touchmove", this._textareaTouchMove);
}
@@ -29,10 +24,6 @@ export default class DComposerPosition extends Component {
willDestroy() {
super.willDestroy(...arguments);
- if (!window.visualViewport) {
- return;
- }
-
const html = document.documentElement;
if (
@@ -69,7 +60,7 @@ export default class DComposerPosition extends Component {
_textareaTouchMove(event) {
// This is an alternative to locking up the body
- // It stops scrolls from bubbling up to the body
+ // It stops scrolling in the given element from bubbling up to the body
// when the textarea does not have any content to scroll
if (event.target) {
const notScrollable =
diff --git a/app/assets/javascripts/discourse/app/components/d-virtual-height.gjs b/app/assets/javascripts/discourse/app/components/d-virtual-height.gjs
index 1b35197f209..06dcd36f184 100644
--- a/app/assets/javascripts/discourse/app/components/d-virtual-height.gjs
+++ b/app/assets/javascripts/discourse/app/components/d-virtual-height.gjs
@@ -5,13 +5,13 @@ import isZoomed from "discourse/lib/zoom-check";
import discourseDebounce from "discourse-common/lib/debounce";
import { bind } from "discourse-common/utils/decorators";
-const FF_KEYBOARD_DETECT_THRESHOLD = 150;
-
export default class DVirtualHeight extends Component {
@service site;
@service capabilities;
@service appEvents;
+ MIN_THRESHOLD = 120;
+
constructor() {
super(...arguments);
@@ -23,7 +23,6 @@ export default class DVirtualHeight extends Component {
return;
}
- // TODO: Handle device rotation
this.windowInnerHeight = window.innerHeight;
scheduleOnce("afterRender", this, this.debouncedOnViewportResize);
@@ -32,13 +31,6 @@ export default class DVirtualHeight extends Component {
"resize",
this.debouncedOnViewportResize
);
- if ("virtualKeyboard" in navigator) {
- navigator.virtualKeyboard.overlaysContent = true;
- navigator.virtualKeyboard.addEventListener(
- "geometrychange",
- this.debouncedOnViewportResize
- );
- }
}
willDestroy() {
@@ -50,13 +42,6 @@ export default class DVirtualHeight extends Component {
"resize",
this.debouncedOnViewportResize
);
- if ("virtualKeyboard" in navigator) {
- navigator.virtualKeyboard.overlaysContent = false;
- navigator.virtualKeyboard.removeEventListener(
- "geometrychange",
- this.debouncedOnViewportResize
- );
- }
}
setVH() {
@@ -64,15 +49,7 @@ export default class DVirtualHeight extends Component {
return;
}
- let height;
- if ("virtualKeyboard" in navigator) {
- height =
- window.visualViewport.height -
- navigator.virtualKeyboard.boundingRect.height;
- } else {
- const activeWindow = window.visualViewport || window;
- height = activeWindow?.height || window.innerHeight;
- }
+ const height = Math.round(window.visualViewport.height);
if (this.previousHeight && Math.abs(this.previousHeight - height) <= 1) {
return false;
@@ -84,8 +61,6 @@ export default class DVirtualHeight extends Component {
"--composer-vh",
`${height / 100}px`
);
-
- document.documentElement.style.setProperty("--vvh", `${height}px`);
}
@bind
@@ -104,26 +79,12 @@ export default class DVirtualHeight extends Component {
const docEl = document.documentElement;
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)
- ) > FF_KEYBOARD_DETECT_THRESHOLD
- ) {
- keyboardVisible = true;
- }
- } else {
- let viewportWindowDiff =
- this.windowInnerHeight - window.visualViewport.height;
- const MIN_THRESHOLD = 20;
- if (viewportWindowDiff > MIN_THRESHOLD) {
- keyboardVisible = true;
- }
+
+ let viewportWindowDiff =
+ this.windowInnerHeight - window.visualViewport.height;
+
+ if (viewportWindowDiff > this.MIN_THRESHOLD) {
+ keyboardVisible = true;
}
this.appEvents.trigger("keyboard-visibility-change", keyboardVisible);
diff --git a/app/assets/stylesheets/common/base/compose.scss b/app/assets/stylesheets/common/base/compose.scss
index aa5ab336061..83b4af9cc1c 100644
--- a/app/assets/stylesheets/common/base/compose.scss
+++ b/app/assets/stylesheets/common/base/compose.scss
@@ -1,14 +1,10 @@
-html.composer-open.not-mobile-device {
+html.composer-open {
#main-outlet {
padding-bottom: var(--composer-height);
transition: padding-bottom 250ms ease;
}
}
-html.composer-open {
- --d-min-composer-reply-height: 35vh;
-}
-
#reply-control {
position: fixed;
display: flex;
@@ -44,7 +40,7 @@ html.composer-open {
}
z-index: z("composer", "content");
transition: height 0.2s, max-width 0.2s, padding-bottom 0.2s, top 0.2s,
- min-height 0.2s;
+ transform 0.2s, min-height 0.2s;
background-color: var(--secondary);
box-shadow: var(--shadow-composer);
@@ -71,7 +67,6 @@ html.composer-open {
&.open {
box-sizing: border-box;
height: var(--composer-height);
- min-height: var(--d-min-composer-reply-height);
max-height: calc(100vh - var(--header-offset, 4em));
padding-bottom: var(--composer-ipad-padding);
}
@@ -632,28 +627,17 @@ div.ac-wrap {
}
}
+// The composer on mobile is fixed-positioned, same as on desktop because
+// we are using interactive-widget=resizes-content in the viewport meta tag
.ipados-device,
.mobile-device {
- // Let's have the composer be top-anchored for mobile and iPad.
- // Safari in iOS/iPad and Firefox/Android do not handle well a bottom:0 fixed-positioned element,
- // especially while the software keyboard is visible.
#reply-control {
- bottom: unset;
- height: 0;
z-index: -1;
- // these two properties below are equivalent to bottom: 0
- top: calc(var(--composer-vh, 1vh) * 100);
- transform: translateY(-100%);
&.open {
z-index: z("mobile-composer");
}
- &.composer-action-edit,
- &.edit-title {
- height: calc(var(--composer-vh, 1vh) * 100);
- }
-
&.draft,
&.saving {
z-index: z("ipad-header-nav") + 1;
@@ -671,19 +655,6 @@ div.ac-wrap {
// this prevents touch events bubbling up to the browser, i.e. accidental scrolls
touch-action: none;
}
-
- // When an element (input, textearea) gets focus, iOS Safari tries to put it in the center
- // by scrolling and zooming. We handle zooming with a meta tag. We used to handle scrolling
- // using a complicated JS hack.
- //
- // However, iOS Safari doesn't scroll when the input has opacity of 0 or is clipped.
- // We use this quirk and temporarily hide the element on scroll and quickly show it again
- //
- // Source https://gist.github.com/kiding/72721a0553fa93198ae2bb6eefaa3299
- input:focus,
- textarea:focus {
- animation: blink_input_opacity_to_prevent_scrolling_when_focus 0.01s;
- }
}
&.keyboard-visible #reply-control.open {
@@ -691,6 +662,32 @@ div.ac-wrap {
}
&.composer-open .with-topic-progress {
- bottom: var(--d-min-composer-reply-height);
+ bottom: calc(var(--composer-height));
+ }
+}
+
+// Safari in iOS/iPad does not handle well a bottom:0 fixed-positioned element,
+// especially while the software keyboard is visible, so we top-anchor it here
+// and shift it using transform
+.ipados-device,
+.ios-device {
+ #reply-control {
+ // the two properties below are equivalent to bottom: 0
+ top: calc(var(--composer-vh, 1vh) * 100);
+ transform: translateY(-100%);
+ bottom: unset;
+ }
+
+ // When an element (input, textearea) gets focus, iOS Safari tries to put it in the center
+ // by scrolling and zooming. We handle zooming with a meta tag. We used to handle scrolling
+ // using a complicated JS hack.
+ //
+ // However, iOS Safari doesn't scroll when the input has opacity of 0 or is clipped.
+ // We use this quirk and temporarily hide the element on scroll and quickly show it again
+ //
+ // Source https://gist.github.com/kiding/72721a0553fa93198ae2bb6eefaa3299
+ input:focus,
+ textarea:focus {
+ animation: blink_input_opacity_to_prevent_scrolling_when_focus 0.01s;
}
}
diff --git a/app/views/layouts/_head.html.erb b/app/views/layouts/_head.html.erb
index 177feecfecf..abd3add7373 100644
--- a/app/views/layouts/_head.html.erb
+++ b/app/views/layouts/_head.html.erb
@@ -8,7 +8,7 @@
<%- end %>
<%= discourse_theme_color_meta_tags %>
<%= discourse_color_scheme_meta_tag %>
-
+
<%- if Discourse.base_path.present? %>
<% end %>