mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: improves keyboard sizing (#26372)
This commit is making the following changes: - replaces `mobile-keyboard` initializer and `chat-vh` with a new template-less component: `d-vh` - ensures body scroll lock is released when page/tab focus changes - correctly locks body on chat channels and chat threads when composer is focused - removes `bodyScrollFix` as we now use body scroll lock - `onViewportResize` has been debounced to ensure it's not a bad performance vector - adds a reverse option do body scroll lock, this is made to support reversed scroll areas (like chat channels and threads) --------- Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
@@ -27,7 +27,6 @@ import {
|
||||
PAST,
|
||||
READ_INTERVAL_MS,
|
||||
} from "discourse/plugins/chat/discourse/lib/chat-constants";
|
||||
import { bodyScrollFix } from "discourse/plugins/chat/discourse/lib/chat-ios-hacks";
|
||||
import ChatMessagesLoader from "discourse/plugins/chat/discourse/lib/chat-messages-loader";
|
||||
import {
|
||||
checkMessageBottomVisibility,
|
||||
@@ -184,7 +183,6 @@ export default class ChatChannel extends Component {
|
||||
onPresenceChangeCallback(present) {
|
||||
if (present) {
|
||||
this.debouncedUpdateLastReadMessage();
|
||||
bodyScrollFix({ delayed: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +461,6 @@ export default class ChatChannel extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
bodyScrollFix();
|
||||
DatesSeparatorsPositioner.apply(this.scrollable);
|
||||
|
||||
this.needsArrow =
|
||||
@@ -765,6 +762,7 @@ export default class ChatChannel extends Component {
|
||||
@channel={{@channel}}
|
||||
@uploadDropZone={{this.uploadDropZone}}
|
||||
@onSendMessage={{this.onSendMessage}}
|
||||
@scrollable={{this.scrollable}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
@@ -141,6 +141,7 @@ export default class ChatComposer extends Component {
|
||||
|
||||
@action
|
||||
setup() {
|
||||
this.composer.scrollable = this.args.scrollable;
|
||||
this.appEvents.on("chat:modify-selection", this, "modifySelection");
|
||||
this.appEvents.on(
|
||||
"chat:open-insert-link-modal",
|
||||
|
||||
@@ -16,6 +16,7 @@ import DButton from "discourse/components/d-button";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import optionalService from "discourse/lib/optional-service";
|
||||
import { updateUserStatusOnMention } from "discourse/lib/update-user-status-on-mention";
|
||||
import isZoomed from "discourse/lib/zoom-check";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
@@ -30,7 +31,6 @@ import ChatMessageSeparator from "discourse/plugins/chat/discourse/components/ch
|
||||
import ChatMessageText from "discourse/plugins/chat/discourse/components/chat-message-text";
|
||||
import ChatMessageThreadIndicator from "discourse/plugins/chat/discourse/components/chat-message-thread-indicator";
|
||||
import ChatMessageInteractor from "discourse/plugins/chat/discourse/lib/chat-message-interactor";
|
||||
import isZoomed from "discourse/plugins/chat/discourse/lib/zoom-check";
|
||||
import ChatOnLongPress from "discourse/plugins/chat/discourse/modifiers/chat/on-long-press";
|
||||
|
||||
let _chatMessageDecorators = [];
|
||||
|
||||
@@ -19,10 +19,7 @@ import {
|
||||
PAST,
|
||||
READ_INTERVAL_MS,
|
||||
} from "discourse/plugins/chat/discourse/lib/chat-constants";
|
||||
import {
|
||||
bodyScrollFix,
|
||||
stackingContextFix,
|
||||
} from "discourse/plugins/chat/discourse/lib/chat-ios-hacks";
|
||||
import { stackingContextFix } from "discourse/plugins/chat/discourse/lib/chat-ios-hacks";
|
||||
import ChatMessagesLoader from "discourse/plugins/chat/discourse/lib/chat-messages-loader";
|
||||
import DatesSeparatorsPositioner from "discourse/plugins/chat/discourse/lib/dates-separators-positioner";
|
||||
import { extractCurrentTopicInfo } from "discourse/plugins/chat/discourse/lib/extract-current-topic-info";
|
||||
@@ -119,7 +116,6 @@ export default class ChatThread extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
bodyScrollFix();
|
||||
DatesSeparatorsPositioner.apply(this.scrollable);
|
||||
|
||||
this.needsArrow =
|
||||
@@ -578,6 +574,7 @@ export default class ChatThread extends Component {
|
||||
@thread={{@thread}}
|
||||
@onSendMessage={{this.onSendMessage}}
|
||||
@uploadDropZone={{this.uploadDropZone}}
|
||||
@scrollable={{this.scrollable}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import Component from "@ember/component";
|
||||
import { service } from "@ember/service";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import isZoomed from "discourse/plugins/chat/discourse/lib/zoom-check";
|
||||
|
||||
const CSS_VAR = "--chat-vh";
|
||||
let lastVH;
|
||||
|
||||
export default class ChatVh extends Component {
|
||||
@service capabilities;
|
||||
|
||||
tagName = "";
|
||||
|
||||
didInsertElement() {
|
||||
super.didInsertElement(...arguments);
|
||||
|
||||
if ("virtualKeyboard" in navigator) {
|
||||
navigator.virtualKeyboard.overlaysContent = true;
|
||||
navigator.virtualKeyboard.addEventListener("geometrychange", this.setVH);
|
||||
}
|
||||
|
||||
this.activeWindow = window.visualViewport || window;
|
||||
this.activeWindow.addEventListener("resize", this.setVH);
|
||||
this.setVH();
|
||||
}
|
||||
|
||||
willDestroyElement() {
|
||||
super.willDestroyElement(...arguments);
|
||||
|
||||
this.activeWindow?.removeEventListener("resize", this.setVH);
|
||||
lastVH = null;
|
||||
|
||||
if ("virtualKeyboard" in navigator) {
|
||||
navigator.virtualKeyboard.removeEventListener(
|
||||
"geometrychange",
|
||||
this.setVH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
setVH() {
|
||||
if (isZoomed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let height;
|
||||
if ("virtualKeyboard" in navigator) {
|
||||
height =
|
||||
window.visualViewport.height -
|
||||
navigator.virtualKeyboard.boundingRect.height;
|
||||
} else {
|
||||
height = this.activeWindow?.height || window.innerHeight;
|
||||
}
|
||||
|
||||
const vh = height * 0.01;
|
||||
|
||||
if (lastVH === vh) {
|
||||
return;
|
||||
}
|
||||
lastVH = vh;
|
||||
|
||||
document.documentElement.style.setProperty(CSS_VAR, `${vh}px`);
|
||||
}
|
||||
|
||||
#blurActiveElement() {
|
||||
if (document.activeElement?.blur) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { next, schedule } from "@ember/runloop";
|
||||
import { capabilities } from "discourse/services/capabilities";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
import isZoomed from "discourse/plugins/chat/discourse/lib/zoom-check";
|
||||
|
||||
// since -webkit-overflow-scrolling: touch can't be used anymore to disable momentum scrolling
|
||||
// we use different hacks to work around this
|
||||
@@ -33,21 +32,3 @@ export function stackingContextFix(scrollable, callback) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function bodyScrollFix(options = {}) {
|
||||
// when keyboard is visible this will ensure body
|
||||
// doesn’t scroll out of viewport
|
||||
if (
|
||||
capabilities.isIOS &&
|
||||
document.documentElement.classList.contains("keyboard-visible") &&
|
||||
!isZoomed()
|
||||
) {
|
||||
if (options.delayed) {
|
||||
setTimeout(() => {
|
||||
document.documentElement.scrollTo(0, 0);
|
||||
}, 200);
|
||||
} else {
|
||||
document.documentElement.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
|
||||
// return true when the browser viewport is zoomed
|
||||
export default function isZoomed() {
|
||||
return (
|
||||
!isTesting() &&
|
||||
visualViewport?.scale !== 1 &&
|
||||
document.documentElement.clientWidth / window.innerWidth !== 1
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Service, { service } from "@ember/service";
|
||||
import { disableBodyScroll } from "discourse/lib/body-scroll-lock";
|
||||
|
||||
export default class ChatChannelComposer extends Service {
|
||||
@service chat;
|
||||
@@ -9,12 +11,31 @@ export default class ChatChannelComposer extends Service {
|
||||
@service router;
|
||||
@service("chat-thread-composer") threadComposer;
|
||||
@service loadingSlider;
|
||||
@service capabilities;
|
||||
@service appEvents;
|
||||
|
||||
@tracked textarea;
|
||||
@tracked scrollable;
|
||||
|
||||
init() {
|
||||
super.init(...arguments);
|
||||
this.appEvents.on("discourse:focus-changed", this, this.blur);
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
this.appEvents.off("discourse:focus-changed", this, this.blur);
|
||||
}
|
||||
|
||||
@action
|
||||
focus(options = {}) {
|
||||
this.textarea?.focus(options);
|
||||
|
||||
schedule("afterRender", () => {
|
||||
if (this.capabilities.isIOS && !this.capabilities.isIpadOS) {
|
||||
disableBodyScroll(this.scrollable, { reverse: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Service, { service } from "@ember/service";
|
||||
import { disableBodyScroll } from "discourse/lib/body-scroll-lock";
|
||||
|
||||
export default class ChatThreadComposer extends Service {
|
||||
@service chat;
|
||||
@service capabilities;
|
||||
@service appEvents;
|
||||
|
||||
@tracked textarea;
|
||||
@tracked scrollable;
|
||||
|
||||
init() {
|
||||
super.init(...arguments);
|
||||
this.appEvents.on("discourse:focus-changed", this, this.blur);
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
this.appEvents.off("discourse:focus-changed", this, this.blur);
|
||||
}
|
||||
|
||||
@action
|
||||
focus(options = {}) {
|
||||
this.textarea?.focus(options);
|
||||
|
||||
schedule("afterRender", () => {
|
||||
if (this.capabilities.isIOS && !this.capabilities.isIpadOS) {
|
||||
disableBodyScroll(this.scrollable, { reverse: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<div id="chat-progress-bar-container"></div>
|
||||
|
||||
<ChatVh />
|
||||
|
||||
{{#if this.chat.sidebarActive}}
|
||||
<div class="full-page-chat teams-sidebar-on">
|
||||
{{outlet}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// desktop and mobile
|
||||
// -1px is for the bottom border of the chat navbar
|
||||
$base-height: calc(
|
||||
var(--chat-vh, 1vh) * 100 - var(--header-offset, 0px) - 1px - $inset
|
||||
var(--composer-vh, 1vh) * 100 - var(--header-offset, 0px) - 1px - $inset
|
||||
);
|
||||
|
||||
height: calc($base-height - var(--composer-height, 0px));
|
||||
|
||||
Reference in New Issue
Block a user