v272vjg35: Swipe through a post's media on mobile slides

This commit is contained in:
2026-10-20 08:26:54 -06:00
parent adcd0cfd96
commit b97dddfc4c
3 changed files with 86 additions and 2 deletions
+36 -2
View File
@@ -82,8 +82,42 @@ function SlideMedia({ media, author }: { media: CardMedia; author: string }) {
);
}
/** Several media items in one slide: a sideways scroll-snap track with a dot per item. */
function MediaTrack({ items, author }: { items: CardMedia[]; author: string }) {
const track = useRef<HTMLDivElement>(null);
const [active, setActive] = useState(0);
const onScroll = () => {
const el = track.current;
if (!el) return;
const next = Math.min(items.length - 1, Math.max(0, Math.round(el.scrollLeft / Math.max(el.clientWidth, 1))));
if (next === active) return;
setActive(next);
// A video stops when it is swiped away.
el.querySelectorAll<HTMLElement>(":scope > .slide-pane").forEach((pane, i) => {
if (i !== next) pane.querySelectorAll("video").forEach((v) => v.pause());
});
};
return (
<>
<div className="slide-track" data-testid="slide-track" ref={track} onScroll={onScroll}>
{items.map((m, i) => (
<div className="slide-pane" key={i} aria-label={`Media ${i + 1} of ${items.length}`}>
<SlideMedia media={m} author={author} />
</div>
))}
</div>
<div className="slide-dots" data-testid="slide-dots" aria-hidden="true">
{items.map((_, i) => (
<span key={i} className={`slide-dot${i === active ? " slide-dot-on" : ""}`} />
))}
</div>
</>
);
}
function PostSlide({ post, index, current, p }: { post: CardPost; index: number; current: boolean; p: FeedViewProps }) {
const media = post.media.find((m) => m.kind === "video" || m.src) ?? null;
const items = post.media.filter((m) => m.kind === "video" || m.src);
const media = items[0] ?? null;
const own = canDelete(post, p.feedUserId) && !!p.onDelete;
// While a video plays, the author and text step aside so the native controls stay clear.
const [playing, setPlaying] = useState(false);
@@ -100,7 +134,7 @@ function PostSlide({ post, index, current, p }: { post: CardPost; index: number;
>
{media ? (
<>
<SlideMedia media={media} author={post.authorName} />
{items.length > 1 ? <MediaTrack items={items} author={post.authorName} /> : <SlideMedia media={media} author={post.authorName} />}
<div className="slide-scrim-top" aria-hidden="true" />
<div className="slide-scrim" aria-hidden="true" />
</>