Files
mastodon/app/javascript/flavours/glitch/features/account_featured/index.tsx
diondiondion ffac9e53c6 [Glitch] Update collection list item design
Port 2124be8a81 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
2026-03-26 18:30:16 +01:00

232 lines
7.1 KiB
TypeScript

import { useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
import { useHistory } from 'react-router';
import { List as ImmutableList } from 'immutable';
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
import { isServerFeatureEnabled } from '@/flavours/glitch/utils/environment';
import { fetchEndorsedAccounts } from 'flavours/glitch/actions/accounts';
import { fetchFeaturedTags } from 'flavours/glitch/actions/featured_tags';
import { Account } from 'flavours/glitch/components/account';
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 { AccountHeader } from 'flavours/glitch/features/account_timeline/components/account_header';
import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error';
import Column from 'flavours/glitch/features/ui/components/column';
import { useAccountId } from 'flavours/glitch/hooks/useAccountId';
import { useAccountVisibility } from 'flavours/glitch/hooks/useAccountVisibility';
import {
fetchAccountCollections,
selectAccountCollections,
} from 'flavours/glitch/reducers/slices/collections';
import { useAppDispatch, useAppSelector } from 'flavours/glitch/store';
import { CollectionListItem } from '../collections/detail/collection_list_item';
import { areCollectionsEnabled } from '../collections/utils';
import { EmptyMessage } from './components/empty_message';
import { FeaturedTag } from './components/featured_tag';
import type { TagMap } from './components/featured_tag';
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 &&
isServerFeatureEnabled('profile_redesign')
) {
history.push(`/@${account.acct}`);
}
}, [account, history]);
useEffect(() => {
if (accountId) {
void dispatch(fetchFeaturedTags({ accountId }));
void dispatch(fetchEndorsedAccounts({ accountId }));
if (areCollectionsEnabled()) {
void dispatch(fetchAccountCollections({ accountId }));
}
}
}, [accountId, dispatch]);
const isLoading = useAppSelector(
(state) =>
!accountId ||
!!state.user_lists.getIn(['featured_tags', accountId, 'isLoading']),
);
const featuredTags = useAppSelector(
(state) =>
state.user_lists.getIn(
['featured_tags', accountId, 'items'],
ImmutableList(),
) as ImmutableList<TagMap>,
);
const featuredAccountIds = useAppSelector(
(state) =>
state.user_lists.getIn(
['featured_accounts', accountId, 'items'],
ImmutableList(),
) as ImmutableList<string>,
);
const { collections, status } = useAppSelector((state) =>
selectAccountCollections(state, accountId ?? null),
);
const listedCollections = collections.filter(
// Hide unlisted and empty collections to avoid confusion
// (Unlisted collections will only be part of the payload
// when viewing your own profile.)
(item) => item.discoverable && !!item.item_count,
);
if (accountId === null) {
return <BundleColumnError multiColumn={multiColumn} errorType='routing' />;
}
if (isLoading) {
return (
<AccountFeaturedWrapper accountId={accountId}>
<div className='scrollable__append'>
<LoadingIndicator />
</div>
</AccountFeaturedWrapper>
);
}
const noTags =
featuredTags.isEmpty() || isServerFeatureEnabled('profile_redesign');
if (
noTags &&
featuredAccountIds.isEmpty() &&
listedCollections.length === 0
) {
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} />
)}
{listedCollections.length > 0 && status === 'idle' && (
<>
<h4 className='column-subheading'>
<FormattedMessage
id='account.featured.collections'
defaultMessage='Collections'
/>
</h4>
<ItemList>
{listedCollections.map((item, index) => (
<CollectionListItem
key={item.id}
collection={item}
withoutBorder={index === listedCollections.length - 1}
withAuthorHandle={false}
positionInList={index + 1}
listSize={listedCollections.length}
/>
))}
</ItemList>
</>
)}
{!noTags && (
<>
<h4 className='column-subheading'>
<FormattedMessage
id='account.featured.hashtags'
defaultMessage='Hashtags'
/>
</h4>
<ItemList>
{featuredTags.map((tag, index) => (
<Article
focusable
key={tag.get('id')}
aria-posinset={index + 1}
aria-setsize={featuredTags.size}
>
<FeaturedTag tag={tag} account={account?.acct ?? ''} />
</Article>
))}
</ItemList>
</>
)}
{!featuredAccountIds.isEmpty() && (
<>
<h4 className='column-subheading'>
<FormattedMessage
id='account.featured.accounts'
defaultMessage='Profiles'
/>
</h4>
<ItemList>
{featuredAccountIds.map((featuredAccountId, index) => (
<Article
focusable
key={featuredAccountId}
aria-posinset={index + 1}
aria-setsize={featuredAccountIds.size}
>
<Account id={featuredAccountId} />
</Article>
))}
</ItemList>
</>
)}
<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;