2023-09-12 06:27:54 +03:00
|
|
|
import './generic-accounts.css';
|
|
|
|
|
2023-10-29 06:47:20 +03:00
|
|
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
2023-09-12 06:27:54 +03:00
|
|
|
import { InView } from 'react-intersection-observer';
|
|
|
|
import { useSnapshot } from 'valtio';
|
|
|
|
|
2023-12-20 08:55:56 +03:00
|
|
|
import { api } from '../utils/api';
|
|
|
|
import { fetchRelationships } from '../utils/relationships';
|
2023-09-12 06:27:54 +03:00
|
|
|
import states from '../utils/states';
|
2023-10-19 11:06:55 +03:00
|
|
|
import useLocationChange from '../utils/useLocationChange';
|
2023-09-12 06:27:54 +03:00
|
|
|
|
|
|
|
import AccountBlock from './account-block';
|
|
|
|
import Icon from './icon';
|
2024-05-08 05:29:00 +03:00
|
|
|
import Link from './link';
|
2023-09-12 06:27:54 +03:00
|
|
|
import Loader from './loader';
|
2024-03-24 12:24:47 +03:00
|
|
|
import Status from './status';
|
2023-09-12 06:27:54 +03:00
|
|
|
|
2023-12-20 08:55:56 +03:00
|
|
|
export default function GenericAccounts({
|
|
|
|
instance,
|
|
|
|
excludeRelationshipAttrs = [],
|
2024-03-24 12:24:47 +03:00
|
|
|
postID,
|
2023-12-20 08:55:56 +03:00
|
|
|
onClose = () => {},
|
2024-05-16 16:11:51 +03:00
|
|
|
blankCopy = 'Nothing to show',
|
2023-12-20 08:55:56 +03:00
|
|
|
}) {
|
|
|
|
const { masto, instance: currentInstance } = api();
|
|
|
|
const isCurrentInstance = instance ? instance === currentInstance : true;
|
2023-09-12 06:27:54 +03:00
|
|
|
const snapStates = useSnapshot(states);
|
2023-12-20 08:55:56 +03:00
|
|
|
``;
|
2023-09-12 06:27:54 +03:00
|
|
|
const [uiState, setUIState] = useState('default');
|
|
|
|
const [accounts, setAccounts] = useState([]);
|
|
|
|
const [showMore, setShowMore] = useState(false);
|
|
|
|
|
2023-10-19 11:06:55 +03:00
|
|
|
useLocationChange(onClose);
|
|
|
|
|
2023-09-12 06:27:54 +03:00
|
|
|
if (!snapStates.showGenericAccounts) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const {
|
2023-10-02 12:51:36 +03:00
|
|
|
id,
|
2023-09-12 06:27:54 +03:00
|
|
|
heading,
|
|
|
|
fetchAccounts,
|
|
|
|
accounts: staticAccounts,
|
|
|
|
showReactions,
|
|
|
|
} = snapStates.showGenericAccounts;
|
|
|
|
|
2023-12-20 08:55:56 +03:00
|
|
|
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,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-09-12 06:27:54 +03:00
|
|
|
const loadAccounts = (firstLoad) => {
|
|
|
|
if (!fetchAccounts) return;
|
2023-09-12 14:20:22 +03:00
|
|
|
if (firstLoad) setAccounts([]);
|
2023-09-12 06:27:54 +03:00
|
|
|
setUIState('loading');
|
|
|
|
(async () => {
|
|
|
|
try {
|
|
|
|
const { done, value } = await fetchAccounts(firstLoad);
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
if (firstLoad) {
|
2023-12-20 08:55:56 +03:00
|
|
|
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);
|
2023-09-12 06:27:54 +03:00
|
|
|
} else {
|
2023-12-20 08:55:56 +03:00
|
|
|
// 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;
|
|
|
|
});
|
2023-09-12 06:27:54 +03:00
|
|
|
}
|
|
|
|
setShowMore(!done);
|
2023-12-20 08:55:56 +03:00
|
|
|
|
|
|
|
loadRelationships(value);
|
2023-09-12 06:27:54 +03:00
|
|
|
} else {
|
|
|
|
setShowMore(false);
|
|
|
|
}
|
|
|
|
setUIState('default');
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
setUIState('error');
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
};
|
|
|
|
|
2023-10-29 06:47:20 +03:00
|
|
|
const firstLoad = useRef(true);
|
2023-09-12 06:27:54 +03:00
|
|
|
useEffect(() => {
|
|
|
|
if (staticAccounts?.length > 0) {
|
|
|
|
setAccounts(staticAccounts);
|
2023-12-20 08:55:56 +03:00
|
|
|
loadRelationships(staticAccounts);
|
2023-09-12 06:27:54 +03:00
|
|
|
} else {
|
|
|
|
loadAccounts(true);
|
2023-10-29 06:47:20 +03:00
|
|
|
firstLoad.current = false;
|
2023-09-12 06:27:54 +03:00
|
|
|
}
|
2023-09-12 13:00:19 +03:00
|
|
|
}, [staticAccounts, fetchAccounts]);
|
2023-09-12 06:27:54 +03:00
|
|
|
|
2023-10-02 12:51:36 +03:00
|
|
|
useEffect(() => {
|
2023-10-29 06:47:20 +03:00
|
|
|
if (firstLoad.current) return;
|
2023-10-02 12:51:36 +03:00
|
|
|
// 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]);
|
|
|
|
|
2024-03-24 12:24:47 +03:00
|
|
|
const post = states.statuses[postID];
|
|
|
|
|
2023-09-12 06:27:54 +03:00
|
|
|
return (
|
|
|
|
<div id="generic-accounts-container" class="sheet" tabindex="-1">
|
|
|
|
<button type="button" class="sheet-close" onClick={onClose}>
|
|
|
|
<Icon icon="x" />
|
|
|
|
</button>
|
|
|
|
<header>
|
|
|
|
<h2>{heading || 'Accounts'}</h2>
|
|
|
|
</header>
|
|
|
|
<main>
|
2024-03-24 12:24:47 +03:00
|
|
|
{post && (
|
2024-05-08 05:29:00 +03:00
|
|
|
<Link
|
|
|
|
to={`/${instance || currentInstance}/s/${post.id}`}
|
|
|
|
class="post-preview"
|
|
|
|
>
|
2024-03-24 12:24:47 +03:00
|
|
|
<Status status={post} size="s" readOnly />
|
2024-05-08 05:29:00 +03:00
|
|
|
</Link>
|
2024-03-24 12:24:47 +03:00
|
|
|
)}
|
2023-09-12 06:27:54 +03:00
|
|
|
{accounts.length > 0 ? (
|
|
|
|
<>
|
|
|
|
<ul class="accounts-list">
|
2023-12-20 08:55:56 +03:00
|
|
|
{accounts.map((account) => {
|
|
|
|
const relationship = relationshipsMap[account.id];
|
|
|
|
const key = `${account.id}-${account._types?.length || ''}`;
|
|
|
|
return (
|
|
|
|
<li key={key}>
|
|
|
|
{showReactions && account._types?.length > 0 && (
|
|
|
|
<div class="reactions-block">
|
|
|
|
{account._types.map((type) => (
|
|
|
|
<Icon
|
|
|
|
icon={
|
|
|
|
{
|
|
|
|
reblog: 'rocket',
|
|
|
|
favourite: 'heart',
|
|
|
|
}[type]
|
|
|
|
}
|
|
|
|
class={`${type}-icon`}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
<div class="account-relationships">
|
|
|
|
<AccountBlock
|
|
|
|
account={account}
|
|
|
|
showStats
|
|
|
|
relationship={relationship}
|
|
|
|
excludeRelationshipAttrs={excludeRelationshipAttrs}
|
|
|
|
/>
|
2023-09-12 06:27:54 +03:00
|
|
|
</div>
|
2023-12-20 08:55:56 +03:00
|
|
|
</li>
|
|
|
|
);
|
|
|
|
})}
|
2023-09-12 06:27:54 +03:00
|
|
|
</ul>
|
|
|
|
{uiState === 'default' ? (
|
|
|
|
showMore ? (
|
|
|
|
<InView
|
|
|
|
onChange={(inView) => {
|
|
|
|
if (inView) {
|
|
|
|
loadAccounts();
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
class="plain block"
|
|
|
|
onClick={() => loadAccounts()}
|
|
|
|
>
|
|
|
|
Show more…
|
|
|
|
</button>
|
|
|
|
</InView>
|
|
|
|
) : (
|
|
|
|
<p class="ui-state insignificant">The end.</p>
|
|
|
|
)
|
|
|
|
) : (
|
|
|
|
uiState === 'loading' && (
|
|
|
|
<p class="ui-state">
|
|
|
|
<Loader abrupt />
|
|
|
|
</p>
|
|
|
|
)
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
) : uiState === 'loading' ? (
|
|
|
|
<p class="ui-state">
|
|
|
|
<Loader abrupt />
|
|
|
|
</p>
|
|
|
|
) : uiState === 'error' ? (
|
|
|
|
<p class="ui-state">Error loading accounts</p>
|
|
|
|
) : (
|
2024-05-16 16:11:51 +03:00
|
|
|
<p class="ui-state insignificant">{blankCopy}</p>
|
2023-09-12 06:27:54 +03:00
|
|
|
)}
|
|
|
|
</main>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|