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 && (
- {
- if (
- e.target.classList.contains('carousel-item') ||
- e.target.classList.contains('media')
- ) {
- setShowMediaModal(false);
- }
+ {
+ setShowMediaModal(false);
}}
- tabindex="0"
- >
- {mediaAttachments?.map((media, i) => {
- const { blurhash } = media;
- const rgbAverageColor = blurhash
- ? getBlurHashAverageColor(blurhash)
- : null;
- return (
- {
- if (inView) {
- setShowMediaModal(i);
- }
- }}
- >
-
-
- );
- })}
-
-
-
-
-
- {mediaAttachments?.length > 1 && (
-
-
-
- {mediaAttachments?.map((media, i) => (
-
- ))}
-
-
-
- )}
+ />
)}
{!!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 (
+ <>
+
{
+ if (
+ e.target.classList.contains('carousel-item') ||
+ e.target.classList.contains('media')
+ ) {
+ onClose();
+ }
+ }}
+ tabindex="0"
+ >
+ {mediaAttachments?.map((media, i) => {
+ const { blurhash } = media;
+ const rgbAverageColor = blurhash
+ ? getBlurHashAverageColor(blurhash)
+ : null;
+ return (
+ onSnap(inView, i)}
+ >
+
+
+ );
+ })}
+
+
+
+
+
+ {mediaAttachments?.length > 1 && (
+
+
+
+ {mediaAttachments?.map((media, i) => (
+
+ ))}
+
+
+
+ )}
+ >
+ );
+}
+
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);
+}