113 lines
No EOL
3.3 KiB
TypeScript
113 lines
No EOL
3.3 KiB
TypeScript
import { OrderedSet as ImmutableOrderedSet } from 'immutable';
|
|
import debounce from 'lodash/debounce';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
|
|
import {
|
|
fetchAccount,
|
|
fetchFollowing,
|
|
expandFollowing,
|
|
fetchAccountByUsername,
|
|
} from 'soapbox/actions/accounts';
|
|
import MissingIndicator from 'soapbox/components/missing-indicator';
|
|
import ScrollableList from 'soapbox/components/scrollable-list';
|
|
import { Column, Spinner } from 'soapbox/components/ui';
|
|
import AccountContainer from 'soapbox/containers/account-container';
|
|
import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'soapbox/hooks';
|
|
import { findAccountByUsername } from 'soapbox/selectors';
|
|
|
|
const messages = defineMessages({
|
|
heading: { id: 'column.following', defaultMessage: 'Following' },
|
|
});
|
|
|
|
interface IFollowing {
|
|
params?: {
|
|
username?: string,
|
|
}
|
|
}
|
|
|
|
/** Displays a list of accounts the given user is following. */
|
|
const Following: React.FC<IFollowing> = (props) => {
|
|
const intl = useIntl();
|
|
const dispatch = useAppDispatch();
|
|
const features = useFeatures();
|
|
const ownAccount = useOwnAccount();
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
const username = props.params?.username || '';
|
|
const account = useAppSelector(state => findAccountByUsername(state, username));
|
|
const isOwnAccount = username.toLowerCase() === ownAccount?.username?.toLowerCase();
|
|
|
|
const accountIds = useAppSelector(state => state.user_lists.following.get(account!?.id)?.items || ImmutableOrderedSet<string>());
|
|
const hasMore = useAppSelector(state => !!state.user_lists.following.get(account!?.id)?.next);
|
|
|
|
const isUnavailable = useAppSelector(state => {
|
|
const blockedBy = state.relationships.getIn([account?.id, 'blocked_by']) === true;
|
|
return isOwnAccount ? false : (blockedBy && !features.blockersVisible);
|
|
});
|
|
|
|
const handleLoadMore = useCallback(debounce(() => {
|
|
if (account) {
|
|
dispatch(expandFollowing(account.id));
|
|
}
|
|
}, 300, { leading: true }), [account?.id]);
|
|
|
|
useEffect(() => {
|
|
let promises = [];
|
|
|
|
if (account) {
|
|
promises = [
|
|
dispatch(fetchAccount(account.id)),
|
|
dispatch(fetchFollowing(account.id)),
|
|
];
|
|
} else {
|
|
promises = [
|
|
dispatch(fetchAccountByUsername(username)),
|
|
];
|
|
}
|
|
|
|
Promise.all(promises)
|
|
.then(() => setLoading(false))
|
|
.catch(() => setLoading(false));
|
|
|
|
}, [account?.id, username]);
|
|
|
|
if (loading && accountIds.isEmpty()) {
|
|
return (
|
|
<Spinner />
|
|
);
|
|
}
|
|
|
|
if (!account) {
|
|
return (
|
|
<MissingIndicator />
|
|
);
|
|
}
|
|
|
|
if (isUnavailable) {
|
|
return (
|
|
<div className='empty-column-indicator'>
|
|
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Column label={intl.formatMessage(messages.heading)} transparent>
|
|
<ScrollableList
|
|
scrollKey='following'
|
|
hasMore={hasMore}
|
|
onLoadMore={handleLoadMore}
|
|
emptyMessage={<FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />}
|
|
itemClassName='pb-4'
|
|
>
|
|
{accountIds.map(id =>
|
|
<AccountContainer key={id} id={id} />,
|
|
)}
|
|
</ScrollableList>
|
|
</Column>
|
|
);
|
|
};
|
|
|
|
export default Following; |