import { lazy, Suspense, useCallback } from 'react';
import { openModal } from '@/mastodon/actions/modal';
import type { DeployPictureInPictureCallback } from '@/mastodon/actions/picture_in_picture';
import { deployPictureInPicture } from '@/mastodon/actions/picture_in_picture';
import { CollectionPreviewCard } from '@/mastodon/features/collections/components/collection_preview_card';
import Card from '@/mastodon/features/status/components/card';
import { useExpandedStatus } from '@/mastodon/hooks/useStatus';
import { useToggle } from '@/mastodon/hooks/useToggle';
import { displayMedia } from '@/mastodon/initial_state';
import type {
MediaAttachment,
MediaAttachmentShape,
} from '@/mastodon/models/status';
import { isMediaAttachmentOfType } from '@/mastodon/models/status';
import { selectMediaFilters } from '@/mastodon/selectors/filters';
import { selectPictureInPicture } from '@/mastodon/selectors/statuses';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import { compareUrls } from '@/mastodon/utils/compare_urls';
import { PictureInPicturePlaceholder } from '../picture_in_picture_placeholder';
export const StatusAttachments: React.FC<{
statusId: string;
contextType?: string;
}> = ({ statusId, contextType }) => {
const status = useExpandedStatus(statusId);
if (!status) {
return null;
}
const attachment = status.media_attachments[0];
if (attachment) {
return (
);
}
// Don't display the card or collection if this is a quote.
if (status.quote) {
return null;
}
const card = status.card;
const collection = card?.url
? status.tagged_collections.find(({ url }) => compareUrls(url, card.url))
: status.tagged_collections[0];
if (card && !collection) {
return (
);
}
if (collection) {
return ;
}
return null;
};
type TMediaGallery = React.ComponentClass<
{
media: Immutable.List;
height: number;
onOpenMedia: (index: number) => void;
onToggleVisibility?: () => void;
sensitive?: boolean;
lang?: string;
visible?: boolean;
autoplay?: boolean;
matchedFilters?: (string | null | undefined)[];
cacheWidth?: () => void;
defaultWidth?: number;
},
{ visible: boolean; width?: number }
>;
const MediaGallery = lazy(
() => import('@/mastodon/components/media_gallery'),
);
const Audio = lazy(() => import('@/mastodon/features/audio'));
const Video = lazy(() => import('@/mastodon/features/video'));
const MediaAttachments: React.FC<{
statusId: string;
accountId: string;
contextType?: string;
sensitive: boolean;
language: string;
attachment: MediaAttachmentShape;
restAttachments: MediaAttachmentShape[];
defaultPosterUrl: string;
}> = ({
statusId,
accountId,
contextType,
sensitive,
language,
attachment,
defaultPosterUrl,
}) => {
const description =
attachment.translation?.description ?? attachment.description;
const immutableAttachments = useAppSelector((state) => {
return state.statuses.getIn([
statusId,
'media_attachments',
]) as Immutable.List;
});
const mediaFilters = useAppSelector((state) =>
selectMediaFilters(state, { statusId, contextType }),
);
const pictureInPicture = useAppSelector((state) =>
selectPictureInPicture(state, statusId),
);
const [showMedia, { onToggle: handleToggleMediaVisibility }] = useToggle(
() =>
mediaFilters.length === 0 &&
((displayMedia !== 'hide_all' && !sensitive) ||
displayMedia === 'show_all'),
);
const dispatch = useAppDispatch();
const handleOpenMedia = useCallback(
(index: number) => {
dispatch(
openModal({
modalType: 'MEDIA',
modalProps: { statusId, media: attachment, index, lang: language },
}),
);
},
[attachment, dispatch, language, statusId],
);
const handleOpenVideo = useCallback(
(options: {
startTime: number;
autoPlay: boolean;
defaultVolume: number;
}) => {
dispatch(
openModal({
modalType: 'VIDEO',
modalProps: {
statusId,
options,
media: attachment,
lang: language,
},
}),
);
},
[attachment, dispatch, language, statusId],
);
const handleDeployPictureInPicture: DeployPictureInPictureCallback =
useCallback(
(type, props) => {
if (!accountId || !pictureInPicture.available) {
return;
}
void dispatch(
deployPictureInPicture({
statusId,
accountId,
playerType: type,
props,
}),
);
},
[dispatch, pictureInPicture.available, accountId, statusId],
);
let aspectRatio = '3 / 2';
if (
isMediaAttachmentOfType(attachment, 'image') ||
isMediaAttachmentOfType(attachment, 'video') ||
isMediaAttachmentOfType(attachment, 'gifv')
) {
aspectRatio = `${attachment.meta.original.width} / ${attachment.meta.original.height}`;
} else if (isMediaAttachmentOfType(attachment, 'audio')) {
aspectRatio = '16 / 9';
}
if (pictureInPicture.inUse) {
return ;
}
if (isMediaAttachmentOfType(attachment, 'audio')) {
const { colors, original } = attachment.meta;
return (
}
>
);
}
if (isMediaAttachmentOfType(attachment, 'video')) {
const { original } = attachment.meta;
return (
}
>
);
}
return (
}
>
);
};