FIX: simplify body scroll lock on iOS (#30696)

This will have the following advantages:
- removes a very annoying bug which was making text selection super hard on iOS
- removes the flashing of header when transitioning from disable to enable body scroll lock
This commit is contained in:
Joffrey JAFFEUX 2025-01-10 17:13:48 +01:00 committed by GitHub
parent 07e5f8907e
commit b96a9b9896
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 16 additions and 78 deletions

View File

@ -97,37 +97,16 @@ const restoreOverflowSetting = () => {
}; };
const setPositionFixed = () => const setPositionFixed = () =>
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
const $html = document.documentElement;
const $body = document.body; const $body = document.body;
if (bodyStyle === void 0) { if (bodyStyle === void 0) {
htmlStyle = { ...$html.style };
bodyStyle = { ...$body.style }; bodyStyle = { ...$body.style };
const { scrollY, scrollX, innerHeight } = window; $body.style.touchAction = "none";
$html.style.height = "100%";
$html.style.overflow = "hidden";
$body.style.position = "fixed";
$body.style.top = `${-scrollY}px`;
$body.style.left = `${-scrollX}px`;
$body.style.width = "100%";
$body.style.height = "auto";
} }
}); });
const restorePositionSetting = () => { const restorePositionSetting = () => {
if (bodyStyle !== void 0) { if (bodyStyle !== void 0) {
const y = -parseInt(document.body.style.top, 10);
const x = -parseInt(document.body.style.left, 10);
const $html = document.documentElement;
const $body = document.body; const $body = document.body;
$html.style.height = (htmlStyle == null ? void 0 : htmlStyle.height) || ""; $body.style.touchAction = bodyStyle.touchAction || "";
$html.style.overflow =
(htmlStyle == null ? void 0 : htmlStyle.overflow) || "";
$body.style.position = bodyStyle.position || "";
$body.style.top = bodyStyle.top || "";
$body.style.left = bodyStyle.left || "";
$body.style.width = bodyStyle.width || "";
$body.style.height = bodyStyle.height || "";
$body.style.overflow = bodyStyle.overflow || "";
window.scrollTo(x, y);
bodyStyle = void 0; bodyStyle = void 0;
} }
}; };
@ -199,43 +178,8 @@ const disableBodyScroll = (targetElement, options) => {
} else { } else {
setOverflowHidden(options); setOverflowHidden(options);
} }
if (isIosDevice) {
targetElement.ontouchstart = (event) => {
if (event.targetTouches.length === 1) {
initialClientY = event.targetTouches[0].clientY;
}
};
targetElement.ontouchmove = (event) => {
if (event.targetTouches.length === 1) {
handleScroll(event, targetElement, options);
}
};
if (!documentListenerAdded) {
document.addEventListener(
"touchmove",
preventDefault,
hasPassiveEvents ? { passive: false } : void 0
);
documentListenerAdded = true;
}
}
}; };
const clearAllBodyScrollLocks = () => { const clearAllBodyScrollLocks = () => {
if (isIosDevice) {
locks.forEach((lock) => {
lock.targetElement.ontouchstart = null;
lock.targetElement.ontouchmove = null;
});
if (documentListenerAdded) {
document.removeEventListener(
"touchmove",
preventDefault,
hasPassiveEvents ? { passive: false } : void 0
);
documentListenerAdded = false;
}
initialClientY = -1;
}
if (isIosDevice) { if (isIosDevice) {
restorePositionSetting(); restorePositionSetting();
} else { } else {
@ -261,18 +205,6 @@ const enableBodyScroll = (targetElement) => {
locks = locks.filter((lock) => lock.targetElement !== targetElement); locks = locks.filter((lock) => lock.targetElement !== targetElement);
locksIndex == null ? void 0 : locksIndex.delete(targetElement); locksIndex == null ? void 0 : locksIndex.delete(targetElement);
} }
if (isIosDevice) {
targetElement.ontouchstart = null;
targetElement.ontouchmove = null;
if (documentListenerAdded && locks.length === 0) {
document.removeEventListener(
"touchmove",
preventDefault,
hasPassiveEvents ? { passive: false } : void 0
);
documentListenerAdded = false;
}
}
if (locks.length === 0) { if (locks.length === 0) {
if (isIosDevice) { if (isIosDevice) {
restorePositionSetting(); restorePositionSetting();

View File

@ -88,6 +88,7 @@ export default class SwipeModifier extends Modifier {
this.element.addEventListener("swipeend", this.onDidEndSwipe); this.element.addEventListener("swipeend", this.onDidEndSwipe);
this.element.addEventListener("swipecancel", this.onDidCancelSwipe); this.element.addEventListener("swipecancel", this.onDidCancelSwipe);
this.element.addEventListener("swipe", this.onDidSwipe); this.element.addEventListener("swipe", this.onDidSwipe);
this.element.addEventListener("scroll", this.onScroll);
} }
/** /**
@ -138,6 +139,14 @@ export default class SwipeModifier extends Modifier {
this.onDidCancelSwipeCallback?.(event.detail); this.onDidCancelSwipeCallback?.(event.detail);
} }
/**
* Handler for scroll event. Prevents scrolling while swiping.
*/
@bind
onScroll(event) {
event.preventDefault();
}
/** /**
* Cleans up the swipe modifier. * Cleans up the swipe modifier.
*/ */
@ -150,6 +159,7 @@ export default class SwipeModifier extends Modifier {
this.element.removeEventListener("swipeend", this.onDidEndSwipe); this.element.removeEventListener("swipeend", this.onDidEndSwipe);
this.element.removeEventListener("swipecancel", this.onDidCancelSwipe); this.element.removeEventListener("swipecancel", this.onDidCancelSwipe);
this.element.removeEventListener("swipe", this.onDidSwipe); this.element.removeEventListener("swipe", this.onDidSwipe);
this.element.removeEventListener("scroll", this.onScroll);
this._swipeEvents.removeTouchListeners(); this._swipeEvents.removeTouchListeners();
if (this.lockBody) { if (this.lockBody) {

View File

@ -12,10 +12,6 @@ import { Promise } from "rsvp";
import EmojiPickerDetached from "discourse/components/emoji-picker/detached"; import EmojiPickerDetached from "discourse/components/emoji-picker/detached";
import InsertHyperlink from "discourse/components/modal/insert-hyperlink"; import InsertHyperlink from "discourse/components/modal/insert-hyperlink";
import { SKIP } from "discourse/lib/autocomplete"; import { SKIP } from "discourse/lib/autocomplete";
import {
disableBodyScroll,
enableBodyScroll,
} from "discourse/lib/body-scroll-lock";
import { setupHashtagAutocomplete } from "discourse/lib/hashtag-autocomplete"; import { setupHashtagAutocomplete } from "discourse/lib/hashtag-autocomplete";
import { emojiUrlFor } from "discourse/lib/text"; import { emojiUrlFor } from "discourse/lib/text";
import userSearch from "discourse/lib/user-search"; import userSearch from "discourse/lib/user-search";
@ -285,22 +281,20 @@ export default class ChatComposer extends Component {
} }
@action @action
onTextareaFocusOut(event) { onTextareaFocusOut() {
this.isFocused = false; this.isFocused = false;
enableBodyScroll(event.target);
} }
@action @action
onTextareaFocusIn(event) { onTextareaFocusIn(event) {
this.isFocused = true; this.isFocused = true;
const textarea = event.target;
disableBodyScroll(textarea);
if (!this.capabilities.isIOS) { if (!this.capabilities.isIOS) {
return; return;
} }
// hack to prevent the whole viewport to move on focus input // hack to prevent the whole viewport to move on focus input
const textarea = event.target;
textarea.style.transform = "translateY(-99999px)"; textarea.style.transform = "translateY(-99999px)";
textarea.focus(); textarea.focus();
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {

View File

@ -22,5 +22,7 @@
color: var(--primary-medium); color: var(--primary-medium);
font-size: var(--font-down-1); font-size: var(--font-down-1);
padding: 0.5em 0.25em 0.25em; padding: 0.5em 0.25em 0.25em;
touch-action: none;
height: 100%;
} }
} }