diff --git a/app/soapbox/components/status-media.tsx b/app/soapbox/components/status-media.tsx new file mode 100644 index 000000000..391d2c865 --- /dev/null +++ b/app/soapbox/components/status-media.tsx @@ -0,0 +1,173 @@ +import React, { useState } from 'react'; + +import { openModal } from 'soapbox/actions/modals'; +import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; +import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder_card'; +import Card from 'soapbox/features/status/components/card'; +import Bundle from 'soapbox/features/ui/components/bundle'; +import { MediaGallery, Video, Audio } from 'soapbox/features/ui/util/async-components'; +import { useAppDispatch } from 'soapbox/hooks'; + +import type { List as ImmutableList } from 'immutable'; +import type { Status, Attachment } from 'soapbox/types/entities'; + +interface IStatusMedia { + /** Status entity to render media for. */ + status: Status, + /** Whether to display compact media. */ + muted?: boolean, + /** Callback when compact media is clicked. */ + onClick?: () => void, + /** Whether or not the media is concealed behind a NSFW banner. */ + showMedia?: boolean, + /** Callback when visibility is toggled (eg clicked through NSFW). */ + onToggleVisibility: () => void, +} + +/** Render media attachments for a status. */ +const StatusMedia: React.FC = ({ + status, + muted = false, + onClick, + showMedia = true, + onToggleVisibility, +}) => { + const dispatch = useAppDispatch(); + const [mediaWrapperWidth, setMediaWrapperWidth] = useState(undefined); + + const size = status.media_attachments.size; + const firstAttachment = status.media_attachments.first(); + + let media = null; + + const setRef = (c: HTMLDivElement): void => { + if (c) { + setMediaWrapperWidth(c.offsetWidth); + } + }; + + const renderLoadingMediaGallery = (): JSX.Element => { + return
; + }; + + const renderLoadingVideoPlayer = (): JSX.Element => { + return
; + }; + + const renderLoadingAudioPlayer = (): JSX.Element => { + return
; + }; + + const openMedia = (media: ImmutableList, index: number) => { + dispatch(openModal('MEDIA', { media, index })); + }; + + const openVideo = (media: Attachment, time: number): void => { + dispatch(openModal('VIDEO', { media, time })); + }; + + if (size > 0 && firstAttachment) { + if (muted) { + media = ( + + ); + } else if (size === 1 && firstAttachment.type === 'video') { + const video = firstAttachment; + + if (video.external_video_id && status.card) { + const getHeight = (): number => { + const width = Number(video.meta.getIn(['original', 'width'])); + const height = Number(video.meta.getIn(['original', 'height'])); + return Number(mediaWrapperWidth) / (width / height); + }; + + const height = getHeight(); + + media = ( +
+
+
+ ); + } else { + media = ( + + {(Component: any) => ( + + )} + + ); + } + } else if (size === 1 && firstAttachment.type === 'audio') { + const attachment = firstAttachment; + + media = ( + + {(Component: any) => ( + + )} + + ); + } else { + media = ( + + {(Component: any) => ( + + )} + + ); + } + } else if (status.spoiler_text.length === 0 && !status.quote && status.card) { + media = ( + + ); + } else if (status.expectsCard) { + media = ( + + ); + } + + return media; +}; + +export default StatusMedia; diff --git a/app/soapbox/components/status.tsx b/app/soapbox/components/status.tsx index 3cea7c280..5a42fdea5 100644 --- a/app/soapbox/components/status.tsx +++ b/app/soapbox/components/status.tsx @@ -14,6 +14,7 @@ import Bundle from 'soapbox/features/ui/components/bundle'; import { MediaGallery, Video, Audio } from 'soapbox/features/ui/util/async-components'; import AttachmentThumbs from './attachment-thumbs'; +import StatusMedia from './status-media'; import StatusReplyMentions from './status-reply-mentions'; import StatusActionBar from './status_action_bar'; import StatusContent from './status_content'; @@ -342,7 +343,6 @@ class Status extends ImmutablePureComponent { } render() { - let media = null; const poll = null; let prepend, rebloggedByText, reblogElement, reblogElementMobile; @@ -450,120 +450,6 @@ class Status extends ImmutablePureComponent { status = status.reblog; } - const size = status.media_attachments.size; - const firstAttachment = status.media_attachments.first(); - - if (size > 0 && firstAttachment) { - if (this.props.muted) { - media = ( - - ); - } else if (size === 1 && firstAttachment.type === 'video') { - const video = firstAttachment; - - if (video.external_video_id && status.card) { - const { mediaWrapperWidth } = this.state; - - const getHeight = (): number => { - const width = Number(video.meta.getIn(['original', 'width'])); - const height = Number(video.meta.getIn(['original', 'height'])); - return Number(mediaWrapperWidth) / (width / height); - }; - - const height = getHeight(); - - media = ( -
-
-
- ); - } else { - media = ( - - {(Component: any) => ( - - )} - - ); - } - } else if (size === 1 && firstAttachment.type === 'audio') { - const attachment = firstAttachment; - - media = ( - - {(Component: any) => ( - - )} - - ); - } else { - media = ( - - {(Component: any) => ( - - )} - - ); - } - } else if (status.spoiler_text.length === 0 && !status.quote && status.card) { - media = ( - - ); - } else if (status.expectsCard) { - media = ( - - ); - } - let quote; if (status.quote) { @@ -653,7 +539,14 @@ class Status extends ImmutablePureComponent { collapsable /> - {media} + + {poll} {quote}