import { useEffect, useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; import { createSelector } from '@reduxjs/toolkit'; import type { Map as ImmutableMap } from 'immutable'; import { List as ImmutableList } from 'immutable'; import { openModal } from 'mastodon/actions/modal'; import { expandAccountMediaTimeline } from 'mastodon/actions/timelines'; import { ColumnBackButton } from 'mastodon/components/column_back_button'; import { RemoteHint } from 'mastodon/components/remote_hint'; import ScrollableList from 'mastodon/components/scrollable_list'; import { AccountHeader } from 'mastodon/features/account_timeline/components/account_header'; import { LimitedAccountHint } from 'mastodon/features/account_timeline/components/limited_account_hint'; import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error'; import Column from 'mastodon/features/ui/components/column'; import { useAccountId } from 'mastodon/hooks/useAccountId'; import { useAccountVisibility } from 'mastodon/hooks/useAccountVisibility'; import type { MediaAttachment } from 'mastodon/models/media_attachment'; import type { RootState } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { MediaItem } from './components/media_item'; const getAccountGallery = createSelector( [ (state: RootState, accountId: string) => (state.timelines as ImmutableMap).getIn( [`account:${accountId}:media`, 'items'], ImmutableList(), ) as ImmutableList, (state: RootState) => state.statuses, ], (statusIds, statuses) => { let items = ImmutableList(); statusIds.forEach((statusId) => { const status = statuses.get(statusId) as | ImmutableMap | undefined; if (status) { items = items.concat( ( status.get('media_attachments') as ImmutableList ).map((media) => media.set('status', status)), ); } }); return items; }, ); export const AccountGallery: React.FC<{ multiColumn: boolean; }> = ({ multiColumn }) => { const dispatch = useAppDispatch(); const accountId = useAccountId(); const attachments = useAppSelector((state) => accountId ? getAccountGallery(state, accountId) : ImmutableList(), ); const isLoading = useAppSelector((state) => (state.timelines as ImmutableMap).getIn([ `account:${accountId}:media`, 'isLoading', ]), ); const hasMore = useAppSelector((state) => (state.timelines as ImmutableMap).getIn([ `account:${accountId}:media`, 'hasMore', ]), ); const account = useAppSelector((state) => accountId ? state.accounts.get(accountId) : undefined, ); const isAccount = !!account; const { suspended, blockedBy, hidden } = useAccountVisibility(accountId); const maxId = attachments.last()?.getIn(['status', 'id']) as | string | undefined; useEffect(() => { if (accountId && isAccount) { void dispatch(expandAccountMediaTimeline(accountId)); } }, [dispatch, accountId, isAccount]); const handleLoadMore = useCallback(() => { if (maxId) { void dispatch(expandAccountMediaTimeline(accountId, { maxId })); } }, [dispatch, accountId, maxId]); const handleOpenMedia = useCallback( (attachment: MediaAttachment) => { const statusId = attachment.getIn(['status', 'id']); const lang = attachment.getIn(['status', 'language']); if (attachment.get('type') === 'video') { dispatch( openModal({ modalType: 'VIDEO', modalProps: { media: attachment, statusId, lang, options: { autoPlay: true }, }, }), ); } else if (attachment.get('type') === 'audio') { dispatch( openModal({ modalType: 'AUDIO', modalProps: { media: attachment, statusId, lang, options: { autoPlay: true }, }, }), ); } else { const media = attachment.getIn([ 'status', 'media_attachments', ]) as ImmutableList; const index = media.findIndex( (x) => x.get('id') === attachment.get('id'), ); dispatch( openModal({ modalType: 'MEDIA', modalProps: { media, index, statusId, lang }, }), ); } }, [dispatch], ); if (accountId && !isAccount) { return ; } let emptyMessage; if (accountId) { if (suspended) { emptyMessage = ( ); } else if (hidden) { emptyMessage = ; } else if (blockedBy) { emptyMessage = ( ); } else if (attachments.isEmpty()) { emptyMessage = ; } else { emptyMessage = ( ); } } const forceEmptyState = suspended || blockedBy || hidden; return ( ) } alwaysPrepend append={accountId && } scrollKey='account_gallery' isLoading={isLoading} hasMore={!forceEmptyState && hasMore} onLoadMore={handleLoadMore} emptyMessage={emptyMessage} bindToDocument={!multiColumn} > {attachments.map((attachment) => ( ))} ); }; // eslint-disable-next-line import/no-default-export export default AccountGallery;