diondiondion b7c5e88186 [Glitch] Refactor BundleColumnError to TS
Port b5879fd61fe6c6e570920fca0501ba7fa2568a72 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2026-05-27 20:11:12 +02:00

243 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useCallback, useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
import { useHistory } from 'react-router';
import { List as ImmutableList } from 'immutable';
import { fetchEndorsedAccounts } from '@/flavours/glitch/actions/accounts';
import { AccountHeader } from '@/flavours/glitch/components/account_header';
import { AccountListItem } from '@/flavours/glitch/components/account_list_item';
import { ColumnBackButton } from '@/flavours/glitch/components/column_back_button';
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
import { RemoteHint } from '@/flavours/glitch/components/remote_hint';
import {
Article,
ItemList,
Scrollable,
} from '@/flavours/glitch/components/scrollable_list/components';
import type { TruncatedListItemInfo } from '@/flavours/glitch/components/truncated_list';
import { TruncatedListItems } from '@/flavours/glitch/components/truncated_list';
import { BundleColumnError } from '@/flavours/glitch/features/ui/components/bundle_column_error';
import Column from '@/flavours/glitch/features/ui/components/column';
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import { useAccountId } from '@/flavours/glitch/hooks/useAccountId';
import { useAccountVisibility } from '@/flavours/glitch/hooks/useAccountVisibility';
import { me } from '@/flavours/glitch/initial_state';
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
import AddIcon from '@/material-icons/400-24px/add.svg?react';
import { CollectionListItem } from '../collections/components/collection_list_item';
import { useCollectionsCreatedBy } from '../collections/overview/created_by_you';
import { areCollectionsEnabled } from '../collections/utils';
import { EmptyMessage } from './components/empty_message';
import { Subheading, SubheadingLink } from './components/subheading';
const collectionsEnabled = areCollectionsEnabled();
const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
multiColumn,
}) => {
const accountId = useAccountId();
const account = useAccount(accountId);
const { suspended, blockedBy, hidden } = useAccountVisibility(accountId);
const forceEmptyState = suspended || blockedBy || hidden;
const dispatch = useAppDispatch();
const history = useHistory();
useEffect(() => {
if (account && !account.show_featured) {
history.push(`/@${account.acct}`);
}
}, [account, history]);
useEffect(() => {
if (accountId) {
void dispatch(fetchEndorsedAccounts({ accountId }));
}
}, [accountId, dispatch]);
const featuredAccountIds = useAppSelector(
(state) =>
state.user_lists.getIn(
['featured_accounts', accountId, 'items'],
ImmutableList(),
) as ImmutableList<string>,
);
const { collections, status: collectionsLoadStatus } =
useCollectionsCreatedBy(accountId);
const { listedCollections = [], unlistedCollections = [] } = Object.groupBy(
collections,
(item) =>
item.discoverable && !!item.item_count
? 'listedCollections'
: 'unlistedCollections',
);
const renderListItem = useCallback(
({
item,
index,
totalListLength,
isLastElement,
}: TruncatedListItemInfo<(typeof listedCollections)[number]>) => (
<CollectionListItem
key={item.id}
collection={item}
withoutBorder={isLastElement}
withAuthorHandle={false}
positionInList={index}
listSize={totalListLength}
/>
),
[],
);
const hasCollections =
collectionsEnabled &&
collectionsLoadStatus === 'idle' &&
listedCollections.length > 0;
const hasFeaturedAccounts = !featuredAccountIds.isEmpty();
const isLoading =
!accountId || (collectionsEnabled && collectionsLoadStatus !== 'idle');
if (accountId === null) {
return <BundleColumnError multiColumn={multiColumn} errorType='routing' />;
}
if (isLoading) {
return (
<AccountFeaturedWrapper accountId={accountId}>
<div className='scrollable__append'>
<LoadingIndicator />
</div>
</AccountFeaturedWrapper>
);
}
if (!hasFeaturedAccounts && !hasCollections) {
return (
<AccountFeaturedWrapper accountId={accountId}>
<EmptyMessage
blockedBy={blockedBy}
hidden={hidden}
suspended={suspended}
accountId={accountId}
/>
<RemoteHint accountId={accountId} />
</AccountFeaturedWrapper>
);
}
return (
<Column>
<ColumnBackButton />
<Scrollable>
{accountId && (
<AccountHeader accountId={accountId} hideTabs={forceEmptyState} />
)}
{!featuredAccountIds.isEmpty() && (
<>
<Subheading as='h2'>
<FormattedMessage
id='account.featured.accounts'
defaultMessage='Profiles'
/>
</Subheading>
<ItemList>
{featuredAccountIds.map((featuredAccountId, index) => (
<Article
focusable
key={featuredAccountId}
aria-posinset={index + 1}
aria-setsize={featuredAccountIds.size}
>
<AccountListItem accountId={featuredAccountId} />
</Article>
))}
</ItemList>
</>
)}
{collectionsEnabled && (
<>
<Subheading as='header'>
<h2>
<FormattedMessage
id='account.featured.collections'
defaultMessage='Collections'
/>
</h2>
{accountId === me && (
<SubheadingLink to='/collections/new' icon={AddIcon}>
<FormattedMessage
id='account.featured.new_collection'
defaultMessage='New collection'
/>
</SubheadingLink>
)}
</Subheading>
{hasCollections ? (
<ItemList>
<TruncatedListItems
visibleItems={listedCollections}
truncatedItems={unlistedCollections}
toggleButton={{
title: (
<FormattedMessage
id='collections.unlisted_collections_with_count'
defaultMessage='Unlisted collections ({count})'
values={{ count: unlistedCollections.length }}
/>
),
subtitle: (
<FormattedMessage
id='collections.unlisted_collections_description'
defaultMessage='These dont appear on your profile to others. Anyone with the link can discover them.'
/>
),
}}
renderListItem={renderListItem}
/>
</ItemList>
) : (
<EmptyMessage
withoutAddCollectionButton
blockedBy={blockedBy}
hidden={hidden}
suspended={suspended}
accountId={accountId}
/>
)}
</>
)}
<RemoteHint accountId={accountId} />
</Scrollable>
</Column>
);
};
const AccountFeaturedWrapper = ({
children,
accountId,
}: React.PropsWithChildren<{ accountId?: string }>) => {
return (
<Column>
<ColumnBackButton />
<div className='scrollable scrollable--flex'>
{accountId && <AccountHeader accountId={accountId} />}
{children}
</div>
</Column>
);
};
// eslint-disable-next-line import/no-default-export
export default AccountFeatured;