From 5e801b899d562436be9c8ef988852c4a856dc57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Wed, 7 Feb 2024 17:33:22 +0100 Subject: [PATCH] Use media query to detect touchscreens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- src/components/dropdown-menu/dropdown-menu.tsx | 8 +++----- src/components/status-reaction-wrapper.tsx | 8 ++++---- .../compose/components/privacy-dropdown.tsx | 4 ++-- .../ui/components/modals/media-modal.tsx | 4 ++-- src/is-mobile.ts | 17 +---------------- 5 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/components/dropdown-menu/dropdown-menu.tsx b/src/components/dropdown-menu/dropdown-menu.tsx index 5af959cd7..ab40f164e 100644 --- a/src/components/dropdown-menu/dropdown-menu.tsx +++ b/src/components/dropdown-menu/dropdown-menu.tsx @@ -7,7 +7,7 @@ import { useHistory } from 'react-router-dom'; import { closeDropdownMenu as closeDropdownMenuRedux, openDropdownMenu } from 'soapbox/actions/dropdown-menu'; import { closeModal, openModal } from 'soapbox/actions/modals'; import { useAppDispatch } from 'soapbox/hooks'; -import { isUserTouching } from 'soapbox/is-mobile'; +import { userTouching } from 'soapbox/is-mobile'; import { IconButton, Portal } from '../ui'; @@ -53,8 +53,6 @@ const DropdownMenu = (props: IDropdownMenu) => { const arrowRef = useRef(null); - const isOnMobile = isUserTouching(); - const { x, y, strategy, refs, middlewareData, placement } = useFloating({ placement: initialPlacement, middleware: [ @@ -92,7 +90,7 @@ const DropdownMenu = (props: IDropdownMenu) => { * On mobile screens, let's replace the Popper dropdown with a Modal. */ const handleOpen = () => { - if (isOnMobile) { + if (userTouching.matches) { dispatch( openModal('ACTIONS', { status: filteredProps.status, @@ -113,7 +111,7 @@ const DropdownMenu = (props: IDropdownMenu) => { const handleClose = () => { (refs.reference.current as HTMLButtonElement)?.focus(); - if (isOnMobile) { + if (userTouching.matches) { dispatch(closeModal('ACTIONS')); } else { closeDropdownMenu(); diff --git a/src/components/status-reaction-wrapper.tsx b/src/components/status-reaction-wrapper.tsx index c1d1224de..4bac8c878 100644 --- a/src/components/status-reaction-wrapper.tsx +++ b/src/components/status-reaction-wrapper.tsx @@ -4,7 +4,7 @@ import { simpleEmojiReact } from 'soapbox/actions/emoji-reacts'; import { openModal } from 'soapbox/actions/modals'; import { EmojiSelector, Portal } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig } from 'soapbox/hooks'; -import { isUserTouching } from 'soapbox/is-mobile'; +import { userTouching } from 'soapbox/is-mobile'; import { getReactForStatus } from 'soapbox/utils/emoji-reacts'; interface IStatusReactionWrapper { @@ -39,7 +39,7 @@ const StatusReactionWrapper: React.FC = ({ statusId, chi clearTimeout(timeout.current); } - if (!isUserTouching()) { + if (!userTouching.matches) { setVisible(true); } }; @@ -51,7 +51,7 @@ const StatusReactionWrapper: React.FC = ({ statusId, chi // Unless the user is touching, delay closing the emoji selector briefly // so the user can move the mouse diagonally to make a selection. - if (isUserTouching()) { + if (userTouching.matches) { setVisible(false); } else { timeout.current = setTimeout(() => { @@ -73,7 +73,7 @@ const StatusReactionWrapper: React.FC = ({ statusId, chi const handleClick: React.EventHandler = e => { const meEmojiReact = getReactForStatus(status, soapboxConfig.allowedEmoji)?.name || '👍'; - if (isUserTouching()) { + if (userTouching.matches) { if (ownAccount) { if (visible) { handleReact(meEmojiReact); diff --git a/src/features/compose/components/privacy-dropdown.tsx b/src/features/compose/components/privacy-dropdown.tsx index e26c8c6e4..1d46c3c0a 100644 --- a/src/features/compose/components/privacy-dropdown.tsx +++ b/src/features/compose/components/privacy-dropdown.tsx @@ -11,7 +11,7 @@ import { closeModal, openModal } from 'soapbox/actions/modals'; import Icon from 'soapbox/components/icon'; import { IconButton } from 'soapbox/components/ui'; import { useAppDispatch, useCompose } from 'soapbox/hooks'; -import { isUserTouching } from 'soapbox/is-mobile'; +import { userTouching } from 'soapbox/is-mobile'; import Motion from '../../ui/util/optional-motion'; @@ -173,7 +173,7 @@ const PrivacyDropdown: React.FC = ({ const onModalClose = () => dispatch(closeModal('ACTIONS')); const handleToggle: React.MouseEventHandler = (e) => { - if (isUserTouching()) { + if (userTouching.matches) { if (open) { onModalClose(); } else { diff --git a/src/features/ui/components/modals/media-modal.tsx b/src/features/ui/components/modals/media-modal.tsx index 1f406cbbd..6f78a8ed5 100644 --- a/src/features/ui/components/modals/media-modal.tsx +++ b/src/features/ui/components/modals/media-modal.tsx @@ -15,7 +15,7 @@ import PlaceholderStatus from 'soapbox/features/placeholder/components/placehold import Thread from 'soapbox/features/status/components/thread'; import Video from 'soapbox/features/video'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; -import { isUserTouching } from 'soapbox/is-mobile'; +import { userTouching } from 'soapbox/is-mobile'; import { makeGetStatus } from 'soapbox/selectors'; import ImageLoader from '../image-loader'; @@ -104,7 +104,7 @@ const MediaModal: React.FC = (props) => { const getIndex = () => index !== null ? index : props.index; const toggleNavigation = () => { - setNavigationHidden(value => !value && isUserTouching()); + setNavigationHidden(value => !value && userTouching.matches); }; const handleStatusClick: React.MouseEventHandler = e => { diff --git a/src/is-mobile.ts b/src/is-mobile.ts index bbe38a123..cada4d479 100644 --- a/src/is-mobile.ts +++ b/src/is-mobile.ts @@ -1,5 +1,3 @@ -import { supportsPassiveEvents } from 'detect-passive-events'; - /** Breakpoint at which the application is considered "mobile". */ const LAYOUT_BREAKPOINT = 630; @@ -11,20 +9,7 @@ export function isMobile(width: number) { /** Whether the device is iOS (best guess). */ const iOS: boolean = /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream; -let userTouching = false; -const listenerOptions = supportsPassiveEvents ? { passive: true } as EventListenerOptions : false; - -function touchListener(): void { - userTouching = true; - window.removeEventListener('touchstart', touchListener, listenerOptions); -} - -window.addEventListener('touchstart', touchListener, listenerOptions); - -/** Whether the user has touched the screen since the page loaded. */ -export function isUserTouching(): boolean { - return userTouching; -} +export const userTouching = window.matchMedia('(pointer: coarse)'); /** Whether the device is iOS (best guess). */ export function isIOS(): boolean {