diff --git a/app/javascript/mastodon/features/alt_text_modal/index.tsx b/app/javascript/mastodon/features/alt_text_modal/index.tsx index 8c5e552eb8..e2d05a99ca 100644 --- a/app/javascript/mastodon/features/alt_text_modal/index.tsx +++ b/app/javascript/mastodon/features/alt_text_modal/index.tsx @@ -2,7 +2,6 @@ import { useState, useCallback, useRef, - useEffect, useImperativeHandle, forwardRef, } from 'react'; @@ -13,6 +12,7 @@ import classNames from 'classnames'; import type { List as ImmutableList, Map as ImmutableMap } from 'immutable'; +import { useSpring, animated } from '@react-spring/web'; import Textarea from 'react-textarea-autosize'; import { length } from 'stringz'; // eslint-disable-next-line import/extensions @@ -31,7 +31,7 @@ import Audio from 'mastodon/features/audio'; import { CharacterCounter } from 'mastodon/features/compose/components/character_counter'; import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components'; import { Video, getPointerPosition } from 'mastodon/features/video'; -import { me } from 'mastodon/initial_state'; +import { me, reduceMotion } from 'mastodon/initial_state'; import type { MediaAttachment } from 'mastodon/models/media_attachment'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { assetHost } from 'mastodon/utils/config'; @@ -105,6 +105,17 @@ const Preview: React.FC<{ position: FocalPoint; onPositionChange: (arg0: FocalPoint) => void; }> = ({ mediaId, position, onPositionChange }) => { + const draggingRef = useRef(false); + const nodeRef = useRef(null); + + const [x, y] = position; + const style = useSpring({ + to: { + left: `${x * 100}%`, + top: `${y * 100}%`, + }, + immediate: reduceMotion || draggingRef.current, + }); const media = useAppSelector((state) => ( (state.compose as ImmutableMap).get( @@ -117,9 +128,6 @@ const Preview: React.FC<{ ); const [dragging, setDragging] = useState(false); - const [x, y] = position; - const nodeRef = useRef(null); - const draggingRef = useRef(false); const setRef = useCallback( (e: HTMLImageElement | HTMLVideoElement | null) => { @@ -134,36 +142,30 @@ const Preview: React.FC<{ return; } + const handleMouseMove = (e: MouseEvent) => { + const { x, y } = getPointerPosition(nodeRef.current, e); + draggingRef.current = true; // This will disable the animation for quicker feedback, only do this if the mouse actually moves + onPositionChange([x, y]); + }; + + const handleMouseUp = () => { + setDragging(false); + draggingRef.current = false; + document.removeEventListener('mouseup', handleMouseUp); + document.removeEventListener('mousemove', handleMouseMove); + }; + const { x, y } = getPointerPosition(nodeRef.current, e.nativeEvent); + setDragging(true); - draggingRef.current = true; onPositionChange([x, y]); + + document.addEventListener('mouseup', handleMouseUp); + document.addEventListener('mousemove', handleMouseMove); }, [setDragging, onPositionChange], ); - useEffect(() => { - const handleMouseUp = () => { - setDragging(false); - draggingRef.current = false; - }; - - const handleMouseMove = (e: MouseEvent) => { - if (draggingRef.current) { - const { x, y } = getPointerPosition(nodeRef.current, e); - onPositionChange([x, y]); - } - }; - - document.addEventListener('mouseup', handleMouseUp); - document.addEventListener('mousemove', handleMouseMove); - - return () => { - document.removeEventListener('mouseup', handleMouseUp); - document.removeEventListener('mousemove', handleMouseMove); - }; - }, [setDragging, onPositionChange]); - if (!media) { return null; } @@ -179,10 +181,7 @@ const Preview: React.FC<{ role='presentation' onMouseDown={handleMouseDown} /> -
+
); } else if (media.get('type') === 'gifv') { @@ -194,10 +193,7 @@ const Preview: React.FC<{ alt='' onMouseDown={handleMouseDown} /> -
+
); } else if (media.get('type') === 'video') {