<MVideo
content={getContent()}
renderAsFile={renderFile}
- renderVideoContent={({ body, info, mimeType, url, encInfo }) => (
+ renderVideoContent={({ body, info, ...props }) => (
<VideoContent
body={body}
info={info}
- mimeType={mimeType}
- url={url}
- encInfo={encInfo}
+ {...props}
renderThumbnail={
mediaAutoLoad
? () => (
mimeType: string;
url: string;
encInfo?: IEncryptedFile;
+ markedAsSpoiler?: boolean;
+ spoilerReason?: string;
};
type MVideoProps = {
content: IVideoContent;
mimeType: safeMimeType,
url: mxcUrl,
encInfo: content.file,
+ markedAsSpoiler: content[MATRIX_SPOILER_PROPERTY_NAME],
+ spoilerReason: content[MATRIX_SPOILER_REASON_PROPERTY_NAME],
})}
</AttachmentBox>
</Attachment>
)}
{(srcState.status === AsyncStatus.Loading || srcState.status === AsyncStatus.Success) &&
!load &&
- !markedAsSpoiler && (
+ !blurred && (
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
<Spinner variant="Secondary" />
</Box>
Badge,
Box,
Button,
+ Chip,
Icon,
Icons,
Spinner,
info: IVideoInfo & IThumbnailContent;
encInfo?: EncryptedAttachmentInfo;
autoPlay?: boolean;
+ markedAsSpoiler?: boolean;
+ spoilerReason?: string;
renderThumbnail?: () => ReactNode;
renderVideo: (props: RenderVideoProps) => ReactNode;
};
info,
encInfo,
autoPlay,
+ markedAsSpoiler,
+ spoilerReason,
renderThumbnail,
renderVideo,
...props
const [load, setLoad] = useState(false);
const [error, setError] = useState(false);
+ const [blurred, setBlurred] = useState(markedAsSpoiler ?? false);
const [srcState, loadSrc] = useAsyncCallback(
useCallback(async () => {
/>
)}
{renderThumbnail && !load && (
- <Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
+ <Box
+ className={classNames(css.AbsoluteContainer, blurred && css.Blur)}
+ alignItems="Center"
+ justifyContent="Center"
+ >
{renderThumbnail()}
</Box>
)}
- {!autoPlay && srcState.status === AsyncStatus.Idle && (
+ {!autoPlay && !blurred && srcState.status === AsyncStatus.Idle && (
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
<Button
variant="Secondary"
</Box>
)}
{srcState.status === AsyncStatus.Success && (
- <Box className={css.AbsoluteContainer}>
+ <Box className={classNames(css.AbsoluteContainer, blurred && css.Blur)}>
{renderVideo({
title: body,
src: srcState.data,
})}
</Box>
)}
+ {blurred && !error && srcState.status !== AsyncStatus.Error && (
+ <Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
+ <TooltipProvider
+ tooltip={
+ typeof spoilerReason === 'string' && (
+ <Tooltip variant="Secondary">
+ <Text>{spoilerReason}</Text>
+ </Tooltip>
+ )
+ }
+ position="Top"
+ align="Center"
+ >
+ {(triggerRef) => (
+ <Chip
+ ref={triggerRef}
+ variant="Secondary"
+ radii="Pill"
+ size="500"
+ outlined
+ onClick={() => {
+ setBlurred(false);
+ }}
+ >
+ <Text size="B300">Spoiler</Text>
+ </Chip>
+ )}
+ </TooltipProvider>
+ </Box>
+ )}
{(srcState.status === AsyncStatus.Loading || srcState.status === AsyncStatus.Success) &&
- !load && (
+ !load &&
+ !blurred && (
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
<Spinner variant="Secondary" />
</Box>
-import React, { useEffect } from 'react';
+import React, { ReactNode, useEffect } from 'react';
import { Box, Chip, Icon, IconButton, Icons, Text, color, config, toRem } from 'folds';
import { UploadCard, UploadCardError, UploadCardProgress } from './UploadCard';
import { UploadStatus, UploadSuccess, useBindUploadAtom } from '../../state/upload';
import { useObjectURL } from '../../hooks/useObjectURL';
import { useMediaConfig } from '../../hooks/useMediaConfig';
-type ImagePreviewProps = { fileItem: TUploadItem; onSpoiler: (marked: boolean) => void };
-function ImagePreview({ fileItem, onSpoiler }: ImagePreviewProps) {
+type PreviewImageProps = {
+ fileItem: TUploadItem;
+};
+function PreviewImage({ fileItem }: PreviewImageProps) {
+ const { originalFile, metadata } = fileItem;
+ const fileUrl = useObjectURL(originalFile);
+
+ return (
+ <img
+ style={{
+ objectFit: 'contain',
+ width: '100%',
+ height: toRem(152),
+ filter: metadata.markedAsSpoiler ? 'blur(44px)' : undefined,
+ }}
+ alt={originalFile.name}
+ src={fileUrl}
+ />
+ );
+}
+
+type PreviewVideoProps = {
+ fileItem: TUploadItem;
+};
+function PreviewVideo({ fileItem }: PreviewVideoProps) {
+ const { originalFile, metadata } = fileItem;
+ const fileUrl = useObjectURL(originalFile);
+
+ return (
+ // eslint-disable-next-line jsx-a11y/media-has-caption
+ <video
+ style={{
+ objectFit: 'contain',
+ width: '100%',
+ height: toRem(152),
+ filter: metadata.markedAsSpoiler ? 'blur(44px)' : undefined,
+ }}
+ src={fileUrl}
+ />
+ );
+}
+
+type MediaPreviewProps = {
+ fileItem: TUploadItem;
+ onSpoiler: (marked: boolean) => void;
+ children: ReactNode;
+};
+function MediaPreview({ fileItem, onSpoiler, children }: MediaPreviewProps) {
const { originalFile, metadata } = fileItem;
const fileUrl = useObjectURL(originalFile);
position: 'relative',
}}
>
- <img
- style={{
- objectFit: 'contain',
- width: '100%',
- height: toRem(152),
- filter: fileItem.metadata.markedAsSpoiler ? 'blur(44px)' : undefined,
- }}
- src={fileUrl}
- alt={originalFile.name}
- />
+ {children}
<Box
justifyContent="End"
style={{
bottom={
<>
{fileItem.originalFile.type.startsWith('image') && (
- <ImagePreview fileItem={fileItem} onSpoiler={handleSpoiler} />
+ <MediaPreview fileItem={fileItem} onSpoiler={handleSpoiler}>
+ <PreviewImage fileItem={fileItem} />
+ </MediaPreview>
+ )}
+ {fileItem.originalFile.type.startsWith('video') && (
+ <MediaPreview fileItem={fileItem} onSpoiler={handleSpoiler}>
+ <PreviewVideo fileItem={fileItem} />
+ </MediaPreview>
)}
{upload.status === UploadStatus.Idle && !fileSizeExceeded && (
<UploadCardProgress sentBytes={0} totalBytes={file.size} />
item: TUploadItem,
mxc: string
): Promise<IContent> => {
- const { file, originalFile, encInfo } = item;
+ const { file, originalFile, encInfo, metadata } = item;
const [videoError, videoEl] = await to(loadVideoElement(getVideoFileUrl(originalFile)));
if (videoError) console.warn(videoError);
msgtype: MsgType.Video,
filename: file.name,
body: file.name,
+ [MATRIX_SPOILER_PROPERTY_NAME]: metadata.markedAsSpoiler,
};
if (videoEl) {
const [thumbError, thumbContent] = await to(