import './generic-accounts.css'; import { useEffect, useRef, useState } from 'preact/hooks'; import { InView } from 'react-intersection-observer'; import { useSnapshot } from 'valtio'; import { api } from '../utils/api'; import { fetchRelationships } from '../utils/relationships'; import states from '../utils/states'; import useLocationChange from '../utils/useLocationChange'; import AccountBlock from './account-block'; import Icon from './icon'; import Link from './link'; import Loader from './loader'; import Status from './status'; export default function GenericAccounts({ instance, excludeRelationshipAttrs = [], postID, onClose = () => {}, blankCopy = 'Nothing to show', }) { const { masto, instance: currentInstance } = api(); const isCurrentInstance = instance ? instance === currentInstance : true; const snapStates = useSnapshot(states); ``; const [uiState, setUIState] = useState('default'); const [accounts, setAccounts] = useState([]); const [showMore, setShowMore] = useState(false); useLocationChange(onClose); if (!snapStates.showGenericAccounts) { return null; } const { id, heading, fetchAccounts, accounts: staticAccounts, showReactions, } = snapStates.showGenericAccounts; const [relationshipsMap, setRelationshipsMap] = useState({}); const loadRelationships = async (accounts) => { if (!accounts?.length) return; if (!isCurrentInstance) return; const relationships = await fetchRelationships(accounts, relationshipsMap); if (relationships) { setRelationshipsMap({ ...relationshipsMap, ...relationships, }); } }; const loadAccounts = (firstLoad) => { if (!fetchAccounts) return; if (firstLoad) setAccounts([]); setUIState('loading'); (async () => { try { const { done, value } = await fetchAccounts(firstLoad); if (Array.isArray(value)) { if (firstLoad) { const accounts = []; for (let i = 0; i < value.length; i++) { const account = value[i]; const theAccount = accounts.find( (a, j) => a.id === account.id && i !== j, ); if (!theAccount) { accounts.push({ _types: [], ...account, }); } else { theAccount._types.push(...account._types); } } setAccounts(accounts); } else { // setAccounts((prev) => [...prev, ...value]); // Merge accounts by id and _types setAccounts((prev) => { const newAccounts = prev; for (const account of value) { const theAccount = newAccounts.find((a) => a.id === account.id); if (!theAccount) { newAccounts.push(account); } else { theAccount._types.push(...account._types); } } return newAccounts; }); } setShowMore(!done); loadRelationships(value); } else { setShowMore(false); } setUIState('default'); } catch (e) { console.error(e); setUIState('error'); } })(); }; const firstLoad = useRef(true); useEffect(() => { if (staticAccounts?.length > 0) { setAccounts(staticAccounts); loadRelationships(staticAccounts); } else { loadAccounts(true); firstLoad.current = false; } }, [staticAccounts, fetchAccounts]); useEffect(() => { if (firstLoad.current) return; // reloadGenericAccounts contains value like {id: 'mute', counter: 1} // We only need to reload if the id matches if (snapStates.reloadGenericAccounts?.id === id) { loadAccounts(true); } }, [snapStates.reloadGenericAccounts.counter]); const post = states.statuses[postID]; return (

{heading || 'Accounts'}

{post && ( )} {accounts.length > 0 ? ( <>
    {accounts.map((account) => { const relationship = relationshipsMap[account.id]; const key = `${account.id}-${account._types?.length || ''}`; return (
  • {showReactions && account._types?.length > 0 && (
    {account._types.map((type) => ( ))}
    )}
  • ); })}
{uiState === 'default' ? ( showMore ? ( { if (inView) { loadAccounts(); } }} > ) : (

The end.

) ) : ( uiState === 'loading' && (

) )} ) : uiState === 'loading' ? (

) : uiState === 'error' ? (

Error loading accounts

) : (

{blankCopy}

)}
); }