pl-fe: migrate account gallery to react query
Signed-off-by: Nicole Mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -11,7 +11,7 @@ import { setComposeToStatus } from './compose';
|
||||
import { importEntities } from './importer';
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
|
||||
import type { CreateStatusParams, Status as BaseStatus, ScheduledStatus } from 'pl-api';
|
||||
import type { CreateStatusParams, Status as BaseStatus, ScheduledStatus, StatusSource } from 'pl-api';
|
||||
import type { Status } from 'pl-fe/normalizers/status';
|
||||
import type { AppDispatch, RootState } from 'pl-fe/store';
|
||||
import type { IntlShape } from 'react-intl';
|
||||
@ -128,7 +128,7 @@ const deleteStatus = (statusId: string, groupId?: string, withRedraft = false) =
|
||||
dispatch(deleteFromTimelines(statusId));
|
||||
|
||||
if (withRedraft) {
|
||||
dispatch(setComposeToStatus(status, poll, response.text || '', response.spoiler_text, response.content_type, withRedraft));
|
||||
dispatch(setComposeToStatus(status, poll, response.text || '', response.spoiler_text, (response as StatusSource).content_type, withRedraft));
|
||||
useModalsStore.getState().openModal('COMPOSE');
|
||||
}
|
||||
})
|
||||
|
||||
@ -2,13 +2,14 @@ import clsx from 'clsx';
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
|
||||
import Blurhash from 'pl-fe/components/blurhash';
|
||||
import Icon from 'pl-fe/components/icon';
|
||||
import StillImage from 'pl-fe/components/still-image';
|
||||
import { useSettings } from 'pl-fe/hooks/use-settings';
|
||||
import { isIOS } from 'pl-fe/is-mobile';
|
||||
|
||||
import type { AccountGalleryAttachment } from 'pl-fe/selectors';
|
||||
import type { AccountGalleryAttachment } from 'pl-fe/hooks/use-account-gallery';
|
||||
|
||||
interface IMediaItem {
|
||||
attachment: AccountGalleryAttachment;
|
||||
@ -18,7 +19,8 @@ interface IMediaItem {
|
||||
|
||||
const MediaItem: React.FC<IMediaItem> = ({ attachment, onOpenMedia, isLast }) => {
|
||||
const { autoPlayGif, displayMedia } = useSettings();
|
||||
const [visible, setVisible] = useState<boolean>(displayMedia !== 'hide_all' && !attachment.status?.sensitive || displayMedia === 'show_all');
|
||||
const { account } = useAccount(attachment.account_id);
|
||||
const [visible, setVisible] = useState<boolean>(displayMedia !== 'hide_all' && !attachment.sensitive || displayMedia === 'show_all');
|
||||
|
||||
const handleMouseEnter: React.MouseEventHandler<HTMLVideoElement> = e => {
|
||||
const video = e.target as HTMLVideoElement;
|
||||
@ -49,8 +51,7 @@ const MediaItem: React.FC<IMediaItem> = ({ attachment, onOpenMedia, isLast }) =>
|
||||
}
|
||||
};
|
||||
|
||||
const status = attachment.status;
|
||||
const title = status.spoiler_text || attachment.description;
|
||||
const title = attachment.description;
|
||||
|
||||
let thumbnail: React.ReactNode = '';
|
||||
let icon;
|
||||
@ -119,7 +120,7 @@ const MediaItem: React.FC<IMediaItem> = ({ attachment, onOpenMedia, isLast }) =>
|
||||
|
||||
return (
|
||||
<div className='col-span-1'>
|
||||
<Link className='media-gallery__item-thumbnail aspect-1' to={`/@${status.account.acct}/posts/${status.id}`} onClick={handleClick} title={title}>
|
||||
<Link className='media-gallery__item-thumbnail aspect-1' to={`/@${account?.acct}/posts/${attachment.status_id}`} onClick={handleClick} title={title}>
|
||||
<Blurhash
|
||||
hash={attachment.blurhash}
|
||||
className={clsx('media-gallery__preview', {
|
||||
|
||||
@ -1,22 +1,18 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { fetchAccountTimeline } from 'pl-fe/actions/timelines';
|
||||
import { useAccountLookup } from 'pl-fe/api/hooks/accounts/use-account-lookup';
|
||||
import LoadMore from 'pl-fe/components/load-more';
|
||||
import MissingIndicator from 'pl-fe/components/missing-indicator';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { type AccountGalleryAttachment, getAccountGallery } from 'pl-fe/selectors';
|
||||
import { type AccountGalleryAttachment, useAccountGallery } from 'pl-fe/hooks/use-account-gallery';
|
||||
import { useModalsStore } from 'pl-fe/stores/modals';
|
||||
|
||||
import MediaItem from './components/media-item';
|
||||
|
||||
const AccountGallery = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { username } = useParams<{ username: string }>();
|
||||
const { openModal } = useModalsStore();
|
||||
|
||||
@ -26,9 +22,7 @@ const AccountGallery = () => {
|
||||
isUnavailable,
|
||||
} = useAccountLookup(username, { withRelationship: true });
|
||||
|
||||
const attachments: Array<AccountGalleryAttachment> = useAppSelector((state) => account ? getAccountGallery(state, account.id) : []);
|
||||
const isLoading = useAppSelector((state) => state.timelines[`account:${account?.id}:with_replies:media`]?.isLoading);
|
||||
const hasMore = useAppSelector((state) => state.timelines[`account:${account?.id}:with_replies:media`]?.hasMore);
|
||||
const { data: attachments, isFetching, isLoading, hasNextPage: hasMore, fetchNextPage } = useAccountGallery(account?.id!);
|
||||
|
||||
const handleScrollToBottom = () => {
|
||||
if (hasMore) {
|
||||
@ -37,9 +31,7 @@ const AccountGallery = () => {
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
if (account) {
|
||||
dispatch(fetchAccountTimeline(account.id, { only_media: true }, true));
|
||||
}
|
||||
fetchNextPage({ cancelRefetch: false });
|
||||
};
|
||||
|
||||
const handleLoadOlder: React.MouseEventHandler = e => {
|
||||
@ -49,22 +41,13 @@ const AccountGallery = () => {
|
||||
|
||||
const handleOpenMedia = (attachment: AccountGalleryAttachment) => {
|
||||
if (attachment.type === 'video') {
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status.id });
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status_id });
|
||||
} else {
|
||||
const media = attachment.status.media_attachments;
|
||||
const index = media.findIndex((x) => x.id === attachment.id);
|
||||
|
||||
openModal('MEDIA', { media, index, statusId: attachment.status.id });
|
||||
openModal('MEDIA', { index: attachment.index, statusId: attachment.status_id });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (account) {
|
||||
dispatch(fetchAccountTimeline(account.id, { only_media: true, limit: 40 }));
|
||||
}
|
||||
}, [account?.id]);
|
||||
|
||||
if (accountLoading || (!attachments && isLoading)) {
|
||||
if (accountLoading || isLoading) {
|
||||
return (
|
||||
<Column>
|
||||
<Spinner />
|
||||
@ -80,8 +63,8 @@ const AccountGallery = () => {
|
||||
|
||||
let loadOlder = null;
|
||||
|
||||
if (hasMore && !(isLoading && attachments.length === 0)) {
|
||||
loadOlder = <LoadMore className='my-auto mt-4' visible={!isLoading} onClick={handleLoadOlder} />;
|
||||
if (hasMore && !(isFetching && attachments.length === 0)) {
|
||||
loadOlder = <LoadMore className='my-auto mt-4' visible={!isFetching} onClick={handleLoadOlder} />;
|
||||
}
|
||||
|
||||
if (isUnavailable) {
|
||||
@ -99,7 +82,7 @@ const AccountGallery = () => {
|
||||
<div role='feed' className='grid grid-cols-2 gap-1 overflow-hidden rounded-md sm:grid-cols-3'>
|
||||
{attachments.map((attachment, index) => (
|
||||
<MediaItem
|
||||
key={`${attachment.status.id}+${attachment.id}`}
|
||||
key={`${attachment.status_id}+${attachment.id}`}
|
||||
attachment={attachment}
|
||||
onOpenMedia={handleOpenMedia}
|
||||
isLast={index === attachments.length - 1}
|
||||
@ -115,7 +98,7 @@ const AccountGallery = () => {
|
||||
|
||||
{loadOlder}
|
||||
|
||||
{isLoading && attachments.length === 0 && (
|
||||
{isFetching && attachments.length === 0 && (
|
||||
<div className='relative flex-auto px-8 py-4'>
|
||||
<Spinner />
|
||||
</div>
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchGroupTimeline } from 'pl-fe/actions/timelines';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import Widget from 'pl-fe/components/ui/widget';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { type AccountGalleryAttachment, getGroupGallery } from 'pl-fe/selectors';
|
||||
import { type AccountGalleryAttachment, useGroupGallery } from 'pl-fe/hooks/use-account-gallery';
|
||||
import { useModalsStore } from 'pl-fe/stores/modals';
|
||||
|
||||
import MediaItem from '../../../account-gallery/components/media-item';
|
||||
@ -15,42 +12,22 @@ import MediaItem from '../../../account-gallery/components/media-item';
|
||||
import type { Group } from 'pl-fe/normalizers/group';
|
||||
|
||||
interface IGroupMediaPanel {
|
||||
group?: Group;
|
||||
group: Group;
|
||||
}
|
||||
|
||||
const GroupMediaPanel: React.FC<IGroupMediaPanel> = ({ group }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { openModal } = useModalsStore();
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const isMember = !!group?.relationship?.member;
|
||||
const isPrivate = group?.locked;
|
||||
|
||||
const attachments: Array<AccountGalleryAttachment> = useAppSelector((state) => group ? getGroupGallery(state, group?.id) : []);
|
||||
const { data: attachments, isLoading } = useGroupGallery(group.id);
|
||||
|
||||
const handleOpenMedia = (attachment: AccountGalleryAttachment): void => {
|
||||
if (attachment.type === 'video') {
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status.id });
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status_id });
|
||||
} else {
|
||||
const media = attachment.status.media_attachments;
|
||||
const index = media.findIndex(x => x.id === attachment.id);
|
||||
|
||||
openModal('MEDIA', { media, index, statusId: attachment.status.id });
|
||||
openModal('MEDIA', { index: attachment.index, statusId: attachment.status_id });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
|
||||
if (group && (isMember || !isPrivate)) {
|
||||
dispatch(fetchGroupTimeline(group.id, { only_media: true, limit: 40 }))
|
||||
// @ts-ignore
|
||||
.then(() => setLoading(false))
|
||||
.catch(() => {});
|
||||
}
|
||||
}, [group?.id, isMember, isPrivate]);
|
||||
|
||||
const renderAttachments = () => {
|
||||
const nineAttachments = attachments.slice(0, 9);
|
||||
|
||||
@ -59,7 +36,7 @@ const GroupMediaPanel: React.FC<IGroupMediaPanel> = ({ group }) => {
|
||||
<div className='grid grid-cols-3 gap-0.5 overflow-hidden rounded-md'>
|
||||
{nineAttachments.map((attachment, index) => (
|
||||
<MediaItem
|
||||
key={`${attachment.status.id}+${attachment.id}`}
|
||||
key={`${attachment.status_id}+${attachment.id}`}
|
||||
attachment={attachment}
|
||||
onOpenMedia={handleOpenMedia}
|
||||
isLast={index === nineAttachments.length - 1}
|
||||
@ -76,15 +53,11 @@ const GroupMediaPanel: React.FC<IGroupMediaPanel> = ({ group }) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (isPrivate && !isMember) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Widget title={<FormattedMessage id='media_panel.title' defaultMessage='Media' />}>
|
||||
{group && (
|
||||
<div className='w-full'>
|
||||
{loading ? (
|
||||
{isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
renderAttachments()
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { fetchAccountTimeline } from 'pl-fe/actions/timelines';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import Text from 'pl-fe/components/ui/text';
|
||||
import Widget from 'pl-fe/components/ui/widget';
|
||||
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
|
||||
import { useAppSelector } from 'pl-fe/hooks/use-app-selector';
|
||||
import { type AccountGalleryAttachment, getAccountGallery } from 'pl-fe/selectors';
|
||||
import { type AccountGalleryAttachment, useAccountGallery } from 'pl-fe/hooks/use-account-gallery';
|
||||
import { useModalsStore } from 'pl-fe/stores/modals';
|
||||
|
||||
import MediaItem from '../../../account-gallery/components/media-item';
|
||||
@ -19,37 +16,21 @@ interface IProfileMediaPanel {
|
||||
}
|
||||
|
||||
const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { openModal } = useModalsStore();
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const attachments: Array<AccountGalleryAttachment> = useAppSelector((state) => account ? getAccountGallery(state, account?.id) : []);
|
||||
const { data: attachments, isLoading } = useAccountGallery(account?.id!);
|
||||
|
||||
const handleOpenMedia = (attachment: AccountGalleryAttachment): void => {
|
||||
if (attachment.type === 'video') {
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status.id });
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status_id });
|
||||
} else {
|
||||
const media = attachment.status.media_attachments;
|
||||
const index = media.findIndex(x => x.id === attachment.id);
|
||||
|
||||
openModal('MEDIA', { media, index, statusId: attachment.status.id });
|
||||
openModal('MEDIA', { index: attachment.index, statusId: attachment.status_id });
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
|
||||
if (account) {
|
||||
dispatch(fetchAccountTimeline(account.id, { only_media: true, limit: 40 }))
|
||||
// @ts-ignore yes it does
|
||||
.then(() => setLoading(false))
|
||||
.catch(() => {});
|
||||
}
|
||||
}, [account?.id]);
|
||||
|
||||
const renderAttachments = () => {
|
||||
const publicAttachments = attachments.filter(attachment => attachment.status.visibility === 'public');
|
||||
const publicAttachments = attachments.filter(attachment => attachment.visibility === 'public');
|
||||
const nineAttachments = publicAttachments.slice(0, 9);
|
||||
|
||||
if (nineAttachments.length) {
|
||||
@ -57,7 +38,7 @@ const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
|
||||
<div className='grid grid-cols-3 gap-0.5 overflow-hidden rounded-md'>
|
||||
{nineAttachments.map((attachment, index) => (
|
||||
<MediaItem
|
||||
key={`${attachment.status.id}+${attachment.id}`}
|
||||
key={`${attachment.status_id}+${attachment.id}`}
|
||||
attachment={attachment}
|
||||
onOpenMedia={handleOpenMedia}
|
||||
isLast={index === nineAttachments.length - 1}
|
||||
@ -76,15 +57,13 @@ const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
|
||||
|
||||
return (
|
||||
<Widget title={<FormattedMessage id='media_panel.title' defaultMessage='Media' />}>
|
||||
{account && (
|
||||
<div className='w-full'>
|
||||
{loading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
renderAttachments()
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className='w-full'>
|
||||
{isLoading || !account ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
renderAttachments()
|
||||
)}
|
||||
</div>
|
||||
</Widget>
|
||||
);
|
||||
};
|
||||
|
||||
58
packages/pl-fe/src/hooks/use-account-gallery.ts
Normal file
58
packages/pl-fe/src/hooks/use-account-gallery.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { useAccountMediaTimeline, useGroupMediaTimeline } from 'pl-fe/queries/timelines/use-account-media-timeline';
|
||||
|
||||
import { useAppSelector } from './use-app-selector';
|
||||
|
||||
import type { MediaAttachment } from 'pl-api';
|
||||
import type { RootState } from 'pl-fe/store';
|
||||
|
||||
|
||||
type AccountGalleryAttachment = MediaAttachment & {
|
||||
index: number;
|
||||
sensitive: boolean;
|
||||
visibility: string;
|
||||
status_id: string;
|
||||
account_id: string;
|
||||
}
|
||||
|
||||
const getGallery = createSelector([
|
||||
(state: RootState, statusIds: string[]) => statusIds,
|
||||
(state: RootState) => state.statuses,
|
||||
], (statusIds, statuses) =>
|
||||
statusIds.reduce((medias: Array<AccountGalleryAttachment>, statusId: string) => {
|
||||
const status = statuses[statusId];
|
||||
if (!status) return medias;
|
||||
if (status.reblog_id) return medias;
|
||||
|
||||
return medias.concat(
|
||||
status.media_attachments.map((media, index) => ({
|
||||
...media,
|
||||
index,
|
||||
sensitive: status.sensitive,
|
||||
visibility: status.visibility,
|
||||
status_id: statusId,
|
||||
account_id: status.account.id,
|
||||
})));
|
||||
}, []),
|
||||
);
|
||||
|
||||
const useAccountGallery = (accountId: string) => {
|
||||
const result = useAccountMediaTimeline(accountId);
|
||||
|
||||
return {
|
||||
...result,
|
||||
data: useAppSelector((state) => getGallery(state, result.data || [])),
|
||||
};
|
||||
};
|
||||
|
||||
const useGroupGallery = (groupId: string) => {
|
||||
const result = useGroupMediaTimeline(groupId);
|
||||
|
||||
return {
|
||||
...result,
|
||||
data: useAppSelector((state) => getGallery(state, result.data || [])),
|
||||
};
|
||||
};
|
||||
|
||||
export { useAccountGallery, useGroupGallery, type AccountGalleryAttachment };
|
||||
@ -116,7 +116,9 @@ const GroupLayout: React.FC<IGroupLayout> = ({ params, children }) => {
|
||||
{!me && (
|
||||
<SignUpPanel />
|
||||
)}
|
||||
<GroupMediaPanel group={group} />
|
||||
{group && (group.relationship?.member || !group.locked) && (
|
||||
<GroupMediaPanel group={group} />
|
||||
)}
|
||||
<LinkFooter />
|
||||
</Layout.Aside>
|
||||
</>
|
||||
|
||||
@ -47,7 +47,7 @@ const containerStyle: React.CSSProperties = {
|
||||
};
|
||||
|
||||
interface MediaModalProps {
|
||||
media: Array<MediaAttachment>;
|
||||
media?: Array<MediaAttachment>;
|
||||
statusId?: string;
|
||||
index: number;
|
||||
time?: number;
|
||||
@ -55,7 +55,6 @@ interface MediaModalProps {
|
||||
|
||||
const MediaModal: React.FC<MediaModalProps & BaseModalProps> = (props) => {
|
||||
const {
|
||||
media,
|
||||
statusId,
|
||||
onClose,
|
||||
time = 0,
|
||||
@ -66,6 +65,7 @@ const MediaModal: React.FC<MediaModalProps & BaseModalProps> = (props) => {
|
||||
|
||||
const getStatus = useCallback(makeGetStatus(), []);
|
||||
const status = useAppSelector((state) => statusId ? getStatus(state, { id: statusId }) : undefined);
|
||||
const media = status?.media_attachments || props.media || [];
|
||||
|
||||
const [isLoaded, setIsLoaded] = useState<boolean>(!!status);
|
||||
const [index, setIndex] = useState<number | null>(null);
|
||||
|
||||
@ -2,16 +2,14 @@ import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { useGroup } from 'pl-fe/api/hooks/groups/use-group';
|
||||
import { useGroupMedia } from 'pl-fe/api/hooks/groups/use-group-media';
|
||||
import LoadMore from 'pl-fe/components/load-more';
|
||||
import MissingIndicator from 'pl-fe/components/missing-indicator';
|
||||
import Column from 'pl-fe/components/ui/column';
|
||||
import Spinner from 'pl-fe/components/ui/spinner';
|
||||
import MediaItem from 'pl-fe/features/account-gallery/components/media-item';
|
||||
import { type AccountGalleryAttachment, useGroupGallery } from 'pl-fe/hooks/use-account-gallery';
|
||||
import { useModalsStore } from 'pl-fe/stores/modals';
|
||||
|
||||
import type { Status } from 'pl-fe/normalizers/status';
|
||||
import type { AccountGalleryAttachment } from 'pl-fe/selectors';
|
||||
|
||||
interface IGroupGallery {
|
||||
params: { groupId: string };
|
||||
@ -24,27 +22,13 @@ const GroupGallery: React.FC<IGroupGallery> = (props) => {
|
||||
|
||||
const { group, isLoading: groupIsLoading } = useGroup(groupId);
|
||||
|
||||
const {
|
||||
entities: statuses,
|
||||
fetchNextPage,
|
||||
isLoading,
|
||||
isFetching,
|
||||
hasNextPage,
|
||||
} = useGroupMedia(groupId);
|
||||
|
||||
const attachments = statuses.reduce<AccountGalleryAttachment[]>((result, status) => {
|
||||
result.push(...status.media_attachments.map((a) => ({ ...a, status, account: status.account })));
|
||||
return result;
|
||||
}, []);
|
||||
const { data: attachments, isFetching, isLoading, hasNextPage, fetchNextPage } = useGroupGallery(groupId);
|
||||
|
||||
const handleOpenMedia = (attachment: AccountGalleryAttachment) => {
|
||||
if (attachment.type === 'video') {
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status.id });
|
||||
openModal('VIDEO', { media: attachment, statusId: attachment.status_id });
|
||||
} else {
|
||||
const media = (attachment.status as Status).media_attachments;
|
||||
const index = media.findIndex((x) => x.id === attachment.id);
|
||||
|
||||
openModal('MEDIA', { media, index, statusId: attachment.status.id });
|
||||
openModal('MEDIA', { index: attachment.index, statusId: attachment.status_id });
|
||||
}
|
||||
};
|
||||
|
||||
@ -71,7 +55,7 @@ const GroupGallery: React.FC<IGroupGallery> = (props) => {
|
||||
<div role='feed' className='mt-4 grid grid-cols-2 gap-1 overflow-hidden rounded-md sm:grid-cols-3'>
|
||||
{attachments.map((attachment, index) => (
|
||||
<MediaItem
|
||||
key={`${attachment.status.id}+${attachment.id}`}
|
||||
key={`${attachment.status_id}+${attachment.id}`}
|
||||
attachment={attachment}
|
||||
onOpenMedia={handleOpenMedia}
|
||||
isLast={index === attachments.length - 1}
|
||||
@ -86,7 +70,7 @@ const GroupGallery: React.FC<IGroupGallery> = (props) => {
|
||||
</div>
|
||||
|
||||
{hasNextPage && (
|
||||
<LoadMore className='mt-4' disabled={isFetching} onClick={fetchNextPage} />
|
||||
<LoadMore className='mt-4' disabled={isFetching} onClick={() => fetchNextPage({ cancelRefetch: false })} />
|
||||
)}
|
||||
</Column>
|
||||
);
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import { makePaginatedResponseQuery } from '../utils/make-paginated-response-query';
|
||||
import { minifyStatusList } from '../utils/minify-list';
|
||||
|
||||
const useAccountMediaTimeline = makePaginatedResponseQuery(
|
||||
(accountId: string) => ['timelineIds', `account:${accountId}:with_replies:media`],
|
||||
(client, [accountId]) => client.accounts.getAccountStatuses(accountId!, { only_media: true }).then(minifyStatusList),
|
||||
);
|
||||
|
||||
const useGroupMediaTimeline = makePaginatedResponseQuery(
|
||||
(groupId: string) => ['timelineIds', `group:${groupId}:media`],
|
||||
(client, [groupId]) => client.timelines.groupTimeline(groupId!, { only_media: true }).then(minifyStatusList),
|
||||
);
|
||||
|
||||
export { useAccountMediaTimeline, useGroupMediaTimeline };
|
||||
@ -8,7 +8,7 @@ import { validId } from 'pl-fe/utils/auth';
|
||||
import ConfigDB from 'pl-fe/utils/config-db';
|
||||
import { shouldFilter } from 'pl-fe/utils/timelines';
|
||||
|
||||
import type { Account as BaseAccount, Filter, MediaAttachment, NotificationGroup, Relationship } from 'pl-api';
|
||||
import type { Filter, NotificationGroup, Relationship } from 'pl-api';
|
||||
import type { EntityStore } from 'pl-fe/entity-store/types';
|
||||
import type { Account } from 'pl-fe/normalizers/account';
|
||||
import type { Group } from 'pl-fe/normalizers/group';
|
||||
@ -199,39 +199,6 @@ type SelectedNotification = NotificationGroup & {
|
||||
target: Account;
|
||||
})
|
||||
|
||||
type AccountGalleryAttachment = MediaAttachment & {
|
||||
status: MinifiedStatus;
|
||||
account: BaseAccount;
|
||||
}
|
||||
|
||||
const getAccountGallery = createSelector([
|
||||
(state: RootState, id: string) => state.timelines[`account:${id}:with_replies:media`]?.items || [],
|
||||
(state: RootState) => state.statuses,
|
||||
], (statusIds, statuses) =>
|
||||
statusIds.reduce((medias: Array<AccountGalleryAttachment>, statusId: string) => {
|
||||
const status = statuses[statusId];
|
||||
if (!status) return medias;
|
||||
if (status.reblog_id) return medias;
|
||||
|
||||
return medias.concat(
|
||||
status.media_attachments.map(media => ({ ...media, status, account: status.account })));
|
||||
}, []),
|
||||
);
|
||||
|
||||
const getGroupGallery = createSelector([
|
||||
(state: RootState, id: string) => state.timelines[`group:${id}:media`]?.items || [],
|
||||
(state: RootState) => state.statuses,
|
||||
], (statusIds, statuses) =>
|
||||
statusIds.reduce((medias: Array<AccountGalleryAttachment>, statusId: string) => {
|
||||
const status = statuses[statusId];
|
||||
if (!status) return medias;
|
||||
if (status.reblog_id) return medias;
|
||||
|
||||
return medias.concat(
|
||||
status.media_attachments.map(media => ({ ...media, status, account: status.account })));
|
||||
}, []),
|
||||
);
|
||||
|
||||
const makeGetReport = () => {
|
||||
const getStatus = makeGetStatus();
|
||||
|
||||
@ -354,9 +321,6 @@ export {
|
||||
type SelectedStatus,
|
||||
makeGetNotification,
|
||||
type SelectedNotification,
|
||||
type AccountGalleryAttachment,
|
||||
getAccountGallery,
|
||||
getGroupGallery,
|
||||
makeGetReport,
|
||||
makeGetOtherAccounts,
|
||||
makeGetHosts,
|
||||
|
||||
Reference in New Issue
Block a user