pleroma/packages/pl-fe/src/features/account-gallery/index.tsx

144 lines
4.3 KiB
TypeScript
Raw Normal View History

import { List as ImmutableList } from 'immutable';
2023-01-30 08:49:34 -08:00
import React, { useEffect, useRef } from 'react';
import { FormattedMessage } from 'react-intl';
import { useParams } from 'react-router-dom';
import { openModal } from 'pl-fe/actions/modals';
import { fetchAccountTimeline } from 'pl-fe/actions/timelines';
import { useAccountLookup } from 'pl-fe/api/hooks';
import LoadMore from 'pl-fe/components/load-more';
import MissingIndicator from 'pl-fe/components/missing-indicator';
import { Column, Spinner } from 'pl-fe/components/ui';
import { useAppDispatch, useAppSelector } from 'pl-fe/hooks';
import { type AccountGalleryAttachment, getAccountGallery } from 'pl-fe/selectors';
2022-11-15 09:23:36 -08:00
import MediaItem from './components/media-item';
interface ILoadMoreMedia {
maxId: string | null;
onLoadMore: (value: string | null) => void;
}
const LoadMoreMedia: React.FC<ILoadMoreMedia> = ({ maxId, onLoadMore }) => {
const handleLoadMore = () => {
onLoadMore(maxId);
};
return (
<LoadMore onClick={handleLoadMore} />
);
};
const AccountGallery = () => {
const dispatch = useAppDispatch();
const { username } = useParams<{ username: string }>();
2023-06-25 10:24:21 -07:00
const {
account,
isLoading: accountLoading,
isUnavailable,
2023-06-25 10:24:21 -07:00
} = useAccountLookup(username, { withRelationship: true });
const attachments: ImmutableList<AccountGalleryAttachment> = useAppSelector((state) => account ? getAccountGallery(state, account.id) : ImmutableList());
const isLoading = useAppSelector((state) => state.timelines.get(`account:${account?.id}:with_replies:media`)?.isLoading);
const hasMore = useAppSelector((state) => state.timelines.get(`account:${account?.id}:with_replies:media`)?.hasMore);
const node = useRef<HTMLDivElement>(null);
const handleScrollToBottom = () => {
if (hasMore) {
handleLoadMore();
}
};
const handleLoadMore = () => {
2023-06-25 10:24:21 -07:00
if (account) {
dispatch(fetchAccountTimeline(account.id, { only_media: true }, true));
}
};
const handleLoadOlder: React.MouseEventHandler = e => {
e.preventDefault();
handleScrollToBottom();
};
const handleOpenMedia = (attachment: AccountGalleryAttachment) => {
if (attachment.type === 'video') {
dispatch(openModal('VIDEO', { media: attachment, statusId: attachment.status.id, account: attachment.account }));
} else {
const media = attachment.status.media_attachments;
const index = media.findIndex((x) => x.id === attachment.id);
dispatch(openModal('MEDIA', { media, index, statusId: attachment.status.id }));
}
};
useEffect(() => {
2023-06-25 10:24:21 -07:00
if (account) {
dispatch(fetchAccountTimeline(account.id, { only_media: true, limit: 40 }));
}
2023-06-25 10:24:21 -07:00
}, [account?.id]);
2023-06-25 10:24:21 -07:00
if (accountLoading || (!attachments && isLoading)) {
return (
2023-06-25 10:24:21 -07:00
<Column>
<Spinner />
</Column>
);
}
2023-06-25 10:24:21 -07:00
if (!account) {
return (
2023-06-25 10:24:21 -07:00
<MissingIndicator />
);
}
let loadOlder = null;
if (hasMore && !(isLoading && attachments.size === 0)) {
loadOlder = <LoadMore className='my-auto' visible={!isLoading} onClick={handleLoadOlder} />;
}
if (isUnavailable) {
return (
<Column>
<div className='empty-column-indicator'>
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
</div>
</Column>
);
}
return (
2023-06-25 10:24:21 -07:00
<Column label={`@${account.acct}`} transparent withHeader={false}>
2023-01-30 08:49:34 -08:00
<div role='feed' className='grid grid-cols-2 gap-2 sm:grid-cols-3' ref={node}>
{attachments.map((attachment, index) => attachment === null ? (
<LoadMoreMedia key={'more:' + attachments.get(index + 1)?.id} maxId={index > 0 ? (attachments.get(index - 1)?.id || null) : null} onLoadMore={handleLoadMore} />
) : (
<MediaItem
key={`${attachment.status.id}+${attachment.id}`}
attachment={attachment}
onOpenMedia={handleOpenMedia}
/>
))}
{!isLoading && attachments.size === 0 && (
<div className='empty-column-indicator col-span-2 sm:col-span-3'>
<FormattedMessage id='account_gallery.none' defaultMessage='No media to show.' />
</div>
)}
{loadOlder}
</div>
{isLoading && attachments.size === 0 && (
<div className='relative flex-auto px-8 py-4'>
<Spinner />
</div>
)}
</Column>
);
};
export { AccountGallery as default };