sforkowany z mirror/soapbox
Add StillVideo component
rodzic
fd2bb2e16f
commit
49b996a901
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
import StillVideo from 'soapbox/components/still-video';
|
||||
import { Icon } from 'soapbox/components/ui';
|
||||
import { MIMETYPE_ICONS } from 'soapbox/components/upload';
|
||||
|
||||
|
@ -8,15 +9,15 @@ import type { Attachment } from 'soapbox/types/entities';
|
|||
const defaultIcon = require('@tabler/icons/paperclip.svg');
|
||||
|
||||
interface IMediaPreview {
|
||||
className?: string
|
||||
attachment: Attachment
|
||||
withExt?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a generic preview for an attachment depending on its media type.
|
||||
* It fills its container and is expected to be sized by its parent.
|
||||
*/
|
||||
const MediaPreview: React.FC<IMediaPreview> = ({ className, attachment }) => {
|
||||
const MediaPreview: React.FC<IMediaPreview> = ({ attachment, withExt }) => {
|
||||
const mimeType = attachment.pleroma.get('mime_type') as string | undefined;
|
||||
|
||||
switch (attachment.type) {
|
||||
|
@ -31,14 +32,10 @@ const MediaPreview: React.FC<IMediaPreview> = ({ className, attachment }) => {
|
|||
);
|
||||
case 'video':
|
||||
return (
|
||||
<video
|
||||
className='pointer-events-none h-full w-full object-cover'
|
||||
src={attachment.preview_url}
|
||||
autoPlay
|
||||
playsInline
|
||||
controls={false}
|
||||
muted
|
||||
loop
|
||||
<StillVideo
|
||||
className='h-full w-full object-cover'
|
||||
src={attachment.url}
|
||||
withExt={withExt}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import ExtensionBadge from './extension-badge';
|
||||
|
||||
interface IStillVideo {
|
||||
src: string
|
||||
className?: string
|
||||
hoverToPlay?: boolean
|
||||
playbackRate?: number
|
||||
withExt?: boolean
|
||||
}
|
||||
|
||||
/** Displays a frozen frame of a video unless hovered. */
|
||||
const StillVideo: React.FC<IStillVideo> = ({
|
||||
src,
|
||||
className,
|
||||
hoverToPlay = true,
|
||||
playbackRate = 3,
|
||||
withExt = true,
|
||||
}) => {
|
||||
// https://stackoverflow.com/a/4695156
|
||||
const ext = src.split('.').pop()?.toUpperCase();
|
||||
|
||||
const handleMouseEnter: React.MouseEventHandler<HTMLVideoElement> = ({ currentTarget: video }) => {
|
||||
if (hoverToPlay) {
|
||||
video.playbackRate = playbackRate;
|
||||
video.play();
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave: React.MouseEventHandler<HTMLVideoElement> = ({ currentTarget: video }) => {
|
||||
if (hoverToPlay) {
|
||||
video.pause();
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick: React.MouseEventHandler<HTMLVideoElement> = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx(className, 'relative isolate overflow-hidden')}>
|
||||
<video
|
||||
className='h-full w-full object-cover'
|
||||
src={src}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleClick}
|
||||
playsInline
|
||||
controls={false}
|
||||
muted
|
||||
loop
|
||||
/>
|
||||
|
||||
{(withExt && ext) && (
|
||||
<div className='pointer-events-none absolute left-2 bottom-2 opacity-90'>
|
||||
<ExtensionBadge ext={ext} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StillVideo;
|
|
@ -1,55 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Icon } from 'soapbox/components/ui';
|
||||
import { MIMETYPE_ICONS } from 'soapbox/components/upload';
|
||||
|
||||
import type { Attachment } from 'soapbox/types/entities';
|
||||
|
||||
const defaultIcon = require('@tabler/icons/paperclip.svg');
|
||||
|
||||
interface IChatUploadPreview {
|
||||
className?: string
|
||||
attachment: Attachment
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a generic preview for an upload depending on its media type.
|
||||
* It fills its container and is expected to be sized by its parent.
|
||||
*/
|
||||
const ChatUploadPreview: React.FC<IChatUploadPreview> = ({ className, attachment }) => {
|
||||
const mimeType = attachment.pleroma.get('mime_type') as string | undefined;
|
||||
|
||||
switch (attachment.type) {
|
||||
case 'image':
|
||||
return (
|
||||
<img
|
||||
className='pointer-events-none h-full w-full object-cover'
|
||||
src={attachment.preview_url}
|
||||
alt=''
|
||||
/>
|
||||
);
|
||||
case 'video':
|
||||
return (
|
||||
<video
|
||||
className='pointer-events-none h-full w-full object-cover'
|
||||
src={attachment.preview_url}
|
||||
autoPlay
|
||||
playsInline
|
||||
controls={false}
|
||||
muted
|
||||
loop
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div className='pointer-events-none flex h-full w-full items-center justify-center'>
|
||||
<Icon
|
||||
className='mx-auto my-12 h-16 w-16 text-gray-800 dark:text-gray-200'
|
||||
src={MIMETYPE_ICONS[mimeType || ''] || defaultIcon}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ChatUploadPreview;
|
|
@ -28,7 +28,7 @@ const ChatUpload: React.FC<IChatUpload> = ({ attachment, onDelete }) => {
|
|||
<div className='relative isolate inline-block h-24 w-24 overflow-hidden rounded-lg bg-gray-200 dark:bg-primary-900'>
|
||||
<Blurhash hash={attachment.blurhash} className='absolute inset-0 -z-10 h-full w-full' />
|
||||
|
||||
<div className='absolute right-[6px] top-[6px]'>
|
||||
<div className='absolute right-[6px] top-[6px] z-10'>
|
||||
<RemoveButton onClick={onDelete} />
|
||||
</div>
|
||||
|
||||
|
@ -36,7 +36,7 @@ const ChatUpload: React.FC<IChatUpload> = ({ attachment, onDelete }) => {
|
|||
onClick={clickable ? handleOpenModal : undefined}
|
||||
className={clsx('h-full w-full', { 'cursor-zoom-in': clickable, 'cursor-default': !clickable })}
|
||||
>
|
||||
<MediaPreview attachment={attachment} />
|
||||
<MediaPreview attachment={attachment} withExt={false} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
Ładowanie…
Reference in New Issue