diff --git a/src/components/status.jsx b/src/components/status.jsx index aa48edc7..6c593826 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -2,7 +2,13 @@ import './status.css'; import { getBlurHashAverageColor } from 'fast-blurhash'; import mem from 'mem'; -import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; +import { + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} from 'preact/hooks'; import { InView } from 'react-intersection-observer'; import useResizeObserver from 'use-resize-observer'; import { useSnapshot } from 'valtio'; @@ -15,6 +21,7 @@ import htmlContentLength from '../utils/html-content-length'; import shortenNumber from '../utils/shorten-number'; import states from '../utils/states'; import store from '../utils/store'; +import useDebouncedCallback from '../utils/useDebouncedCallback'; import visibilityIconsMap from '../utils/visibility-icons-map'; import Avatar from './avatar'; @@ -132,16 +139,6 @@ function Status({ }; const [showMediaModal, setShowMediaModal] = useState(false); - const carouselFocusItem = useRef(null); - const prevShowMediaModal = useRef(showMediaModal); - useEffect(() => { - if (showMediaModal !== false) { - carouselFocusItem.current?.node?.scrollIntoView({ - behavior: prevShowMediaModal.current === false ? 'auto' : 'smooth', - }); - } - prevShowMediaModal.current = showMediaModal; - }, [showMediaModal]); if (reblog) { return ( @@ -157,7 +154,6 @@ function Status({ const [showEdited, setShowEdited] = useState(false); - const carouselRef = useRef(null); const currentYear = new Date().getFullYear(); const spoilerContentRef = useRef(null); @@ -563,111 +559,13 @@ function Status({ {showMediaModal !== false && ( - - - {mediaAttachments?.length > 1 && ( - - )} + /> )} {!!showEdited && ( @@ -1203,4 +1101,127 @@ function StatusButton({ ); } +function Carousel({ mediaAttachments, index = 0, onClose = () => {} }) { + const carouselRef = useRef(null); + + const [currentIndex, setCurrentIndex] = useState(index); + const carouselFocusItem = useRef(null); + useLayoutEffect(() => { + carouselFocusItem.current?.node?.scrollIntoView(); + }, []); + useLayoutEffect(() => { + carouselFocusItem.current?.node?.scrollIntoView({ + behavior: 'smooth', + }); + }, [currentIndex]); + + const onSnap = useDebouncedCallback((inView, i) => { + if (inView) { + setCurrentIndex(i); + } + }, 100); + + return ( + <> + + + {mediaAttachments?.length > 1 && ( + + )} + + ); +} + export default Status; diff --git a/src/utils/useDebouncedCallback.js b/src/utils/useDebouncedCallback.js new file mode 100644 index 00000000..c40a3f13 --- /dev/null +++ b/src/utils/useDebouncedCallback.js @@ -0,0 +1,23 @@ +import { useCallback, useRef } from 'preact/hooks'; + +export default function useDebouncedCallback( + callback, + delay, + dependencies = [], +) { + const timeout = useRef(); + + const comboDeps = dependencies + ? [callback, delay, ...dependencies] + : [callback, delay]; + + return useCallback((...args) => { + if (timeout.current != null) { + clearTimeout(timeout.current); + } + + timeout.current = setTimeout(() => { + callback(...args); + }, delay); + }, comboDeps); +}