import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { MatrixEvent, Room } from 'matrix-js-sdk'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMediaAuthentication } from '../../hooks/useMediaAuthentication'; import { useRoomMediaTimeline } from '../../hooks/useRoomMediaTimeline'; import { useRoomNavigate } from '../../hooks/useRoomNavigate'; import { Lightbox, toLightboxItems } from './MediaGallery'; // How many media pages to walk back looking for the clicked event before // settling for a single-item viewer. const MAX_SEARCH_PAGES = 6; type RoomMediaLightboxProps = { room: Room; eventId: string; onClose: () => void; }; /** * [Gitea #219] The room timeline's image viewer is the gallery lightbox, * opened at the clicked event: same dark backdrop, sender + date, 1/N counter, * ←/→ across the room's media, zoom keys, download and "jump to message". * * Items come from the same detached media timeline the gallery uses (#163). * It starts at the live end, so an older image may not be loaded yet: we page * back a bounded number of times looking for it and meanwhile show the clicked * image on its own, so the viewer never waits on the network to open. */ export function RoomMediaLightbox({ room, eventId, onClose }: RoomMediaLightboxProps) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); const { navigateRoom } = useRoomNavigate(); const { events, loadMore, loading, canLoadMore } = useRoomMediaTimeline(mx, room); const items = useMemo(() => toLightboxItems(room, events), [room, events]); const index = items.findIndex((it) => it.eventId === eventId); const pagesRef = useRef(0); useEffect(() => { if (index !== -1 || loading || !canLoadMore || pagesRef.current >= MAX_SEARCH_PAGES) return; pagesRef.current += 1; loadMore(); }, [index, loading, canLoadMore, loadMore]); // Until the event is in the loaded window, show just the clicked media. const fallbackItems = useMemo(() => { const ev: MatrixEvent | undefined = room.findEventById(eventId) ?? undefined; return ev ? toLightboxItems(room, [ev]) : []; }, [room, eventId]); const handleJump = useCallback( (id: string) => { onClose(); navigateRoom(room.roomId, id); }, [onClose, navigateRoom, room.roomId], ); const found = index !== -1; const shownItems = found ? items : fallbackItems; if (shownItems.length === 0) return null; return ( ); }