sforkowany z mirror/soapbox
Break out StatusMedia into a separate component
rodzic
8304f25577
commit
ba8379c753
|
@ -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<IStatusMedia> = ({
|
||||
status,
|
||||
muted = false,
|
||||
onClick,
|
||||
showMedia = true,
|
||||
onToggleVisibility,
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [mediaWrapperWidth, setMediaWrapperWidth] = useState<number | undefined>(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 <div className='media_gallery' style={{ height: '285px' }} />;
|
||||
};
|
||||
|
||||
const renderLoadingVideoPlayer = (): JSX.Element => {
|
||||
return <div className='media-spoiler-video' style={{ height: '285px' }} />;
|
||||
};
|
||||
|
||||
const renderLoadingAudioPlayer = (): JSX.Element => {
|
||||
return <div className='media-spoiler-audio' style={{ height: '285px' }} />;
|
||||
};
|
||||
|
||||
const openMedia = (media: ImmutableList<Attachment>, 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 = (
|
||||
<AttachmentThumbs
|
||||
media={status.media_attachments}
|
||||
onClick={onClick}
|
||||
sensitive={status.sensitive}
|
||||
/>
|
||||
);
|
||||
} 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 = (
|
||||
<div className='status-card horizontal compact interactive status-card--video'>
|
||||
<div
|
||||
ref={setRef}
|
||||
className='status-card__image status-card-video'
|
||||
style={height ? { height } : undefined}
|
||||
dangerouslySetInnerHTML={{ __html: status.card.html }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={Video} loading={renderLoadingVideoPlayer} >
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
preview={video.preview_url}
|
||||
blurhash={video.blurhash}
|
||||
src={video.url}
|
||||
alt={video.description}
|
||||
aspectRatio={video.meta.getIn(['original', 'aspect'])}
|
||||
height={285}
|
||||
inline
|
||||
sensitive={status.sensitive}
|
||||
onOpenVideo={openVideo}
|
||||
visible={showMedia}
|
||||
onToggleVisibility={onToggleVisibility}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
}
|
||||
} else if (size === 1 && firstAttachment.type === 'audio') {
|
||||
const attachment = firstAttachment;
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Audio} loading={renderLoadingAudioPlayer} >
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
src={attachment.url}
|
||||
alt={attachment.description}
|
||||
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : status.getIn(['account', 'avatar_static'])}
|
||||
backgroundColor={attachment.meta.getIn(['colors', 'background'])}
|
||||
foregroundColor={attachment.meta.getIn(['colors', 'foreground'])}
|
||||
accentColor={attachment.meta.getIn(['colors', 'accent'])}
|
||||
duration={attachment.meta.getIn(['original', 'duration'], 0)}
|
||||
height={263}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={MediaGallery} loading={renderLoadingMediaGallery}>
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
media={status.media_attachments}
|
||||
sensitive={status.sensitive}
|
||||
height={285}
|
||||
onOpenMedia={openMedia}
|
||||
visible={showMedia}
|
||||
onToggleVisibility={onToggleVisibility}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
}
|
||||
} else if (status.spoiler_text.length === 0 && !status.quote && status.card) {
|
||||
media = (
|
||||
<Card
|
||||
onOpenMedia={openMedia}
|
||||
card={status.card}
|
||||
compact
|
||||
/>
|
||||
);
|
||||
} else if (status.expectsCard) {
|
||||
media = (
|
||||
<PlaceholderCard />
|
||||
);
|
||||
}
|
||||
|
||||
return media;
|
||||
};
|
||||
|
||||
export default StatusMedia;
|
|
@ -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<IStatus, IStatusState> {
|
|||
}
|
||||
|
||||
render() {
|
||||
let media = null;
|
||||
const poll = null;
|
||||
let prepend, rebloggedByText, reblogElement, reblogElementMobile;
|
||||
|
||||
|
@ -450,120 +450,6 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
status = status.reblog;
|
||||
}
|
||||
|
||||
const size = status.media_attachments.size;
|
||||
const firstAttachment = status.media_attachments.first();
|
||||
|
||||
if (size > 0 && firstAttachment) {
|
||||
if (this.props.muted) {
|
||||
media = (
|
||||
<AttachmentThumbs
|
||||
media={status.media_attachments}
|
||||
onClick={this.handleClick}
|
||||
sensitive={status.sensitive}
|
||||
/>
|
||||
);
|
||||
} 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 = (
|
||||
<div className='status-card horizontal compact interactive status-card--video'>
|
||||
<div
|
||||
ref={this.setRef}
|
||||
className='status-card__image status-card-video'
|
||||
style={height ? { height } : undefined}
|
||||
dangerouslySetInnerHTML={{ __html: status.card.html }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
preview={video.preview_url}
|
||||
blurhash={video.blurhash}
|
||||
src={video.url}
|
||||
alt={video.description}
|
||||
aspectRatio={video.meta.getIn(['original', 'aspect'])}
|
||||
width={this.props.cachedMediaWidth}
|
||||
height={285}
|
||||
inline
|
||||
sensitive={status.sensitive}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
visible={this.state.showMedia}
|
||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
}
|
||||
} else if (size === 1 && firstAttachment.type === 'audio') {
|
||||
const attachment = firstAttachment;
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
src={attachment.url}
|
||||
alt={attachment.description}
|
||||
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : status.getIn(['account', 'avatar_static'])}
|
||||
backgroundColor={attachment.meta.getIn(['colors', 'background'])}
|
||||
foregroundColor={attachment.meta.getIn(['colors', 'foreground'])}
|
||||
accentColor={attachment.meta.getIn(['colors', 'accent'])}
|
||||
duration={attachment.meta.getIn(['original', 'duration'], 0)}
|
||||
width={this.props.cachedMediaWidth}
|
||||
height={263}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
media={status.media_attachments}
|
||||
sensitive={status.sensitive}
|
||||
height={285}
|
||||
onOpenMedia={this.props.onOpenMedia}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
defaultWidth={this.props.cachedMediaWidth}
|
||||
visible={this.state.showMedia}
|
||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
}
|
||||
} else if (status.spoiler_text.length === 0 && !status.quote && status.card) {
|
||||
media = (
|
||||
<Card
|
||||
onOpenMedia={this.props.onOpenMedia}
|
||||
card={status.card}
|
||||
compact
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
defaultWidth={this.props.cachedMediaWidth}
|
||||
/>
|
||||
);
|
||||
} else if (status.expectsCard) {
|
||||
media = (
|
||||
<PlaceholderCard />
|
||||
);
|
||||
}
|
||||
|
||||
let quote;
|
||||
|
||||
if (status.quote) {
|
||||
|
@ -653,7 +539,14 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
collapsable
|
||||
/>
|
||||
|
||||
{media}
|
||||
<StatusMedia
|
||||
status={status}
|
||||
muted={this.props.muted}
|
||||
onClick={this.handleClick}
|
||||
showMedia={this.state.showMedia}
|
||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
|
||||
{poll}
|
||||
{quote}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue