import { useState, useEffect, useCallback } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faXmark } from '@fortawesome/free-solid-svg-icons'; import type { AtUri } from '../api/types'; import { atprotoClient } from '../api/client'; import { usePostLabels } from '../settings/usePostLabels'; import PdsFavicon from './PdsFavicon'; import { useMuteBlock } from './muteBlockContext'; import './LikesRepostsModal.css'; interface RepostsModalProps { uri: AtUri; onClose: () => void; } interface RepostActor { did: string; handle: string; displayName?: string; avatar?: string; } export default function RepostsModal({ uri, onClose }: RepostsModalProps) { const [actors, setActors] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [cursor, setCursor] = useState(); const [loadingMore, setLoadingMore] = useState(false); const { t: label } = usePostLabels(); const { isBlocked, isMuted } = useMuteBlock(); useEffect(() => { document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = ''; }; }, []); useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); }, [onClose]); useEffect(() => { let cancelled = false; (async () => { try { const resp = await atprotoClient.api!.app.bsky.feed.getRepostedBy({ uri, limit: 50 }); if (!cancelled) { const repostedBy = (resp.data as { repostedBy: RepostActor[] }).repostedBy; setActors(repostedBy); setCursor((resp.data as { cursor?: string }).cursor); } } catch (err) { if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load reposts'); } finally { if (!cancelled) setLoading(false); } })(); return () => { cancelled = true; }; }, [uri]); const loadMore = useCallback(async () => { if (!cursor || loadingMore) return; setLoadingMore(true); try { const resp = await atprotoClient.api!.app.bsky.feed.getRepostedBy({ uri, limit: 50, cursor }); const repostedBy = (resp.data as { repostedBy: RepostActor[] }).repostedBy; setActors((prev) => [...prev, ...repostedBy]); setCursor((resp.data as { cursor?: string }).cursor); } catch (err) { console.error('Failed to load more reposts:', err); } finally { setLoadingMore(false); } }, [uri, cursor, loadingMore]); return (
e.stopPropagation()}>
{label('repost', false, true)}ed by
{loading &&
Loading…
} {error &&
{error}
} {!loading && !error && actors.length === 0 && (
No {label('repost', true)} yet
)} {actors.map((actor) => (
{actor.avatar ? ( ) : (
{(actor.displayName || actor.handle)[0].toUpperCase()}
)}
{actor.displayName || actor.handle} @{actor.handle} {isBlocked(actor.did) && Blocked} {isMuted(actor.did) && Muted}
))} {cursor && ( )}
); }