Make it possible to mark videos as spoilers

This commit is contained in:
Ginger 2025-03-06 13:06:30 -05:00
parent ccd4327dcc
commit 2fa748a470
5 changed files with 95 additions and 54 deletions

View file

@ -29,7 +29,7 @@ import { ImageViewer } from './image-viewer';
import { PdfViewer } from './Pdf-viewer';
import { TextViewer } from './text-viewer';
import { testMatrixTo } from '../plugins/matrix-to';
import {IImageContent} from "../../types/matrix/common";
import { IImageContent } from '../../types/matrix/common';
type RenderMessageContentProps = {
displayName: string;
@ -70,7 +70,7 @@ export function RenderMessageContent({
};
const renderCaption = () => {
const content: IImageContent = getContent();
if(content.filename && content.filename !== content.body) {
if (content.filename && content.filename !== content.body) {
return (
<MText
edited={edited}
@ -85,41 +85,40 @@ export function RenderMessageContent({
)}
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
/>
)
);
}
return null;
}
};
const renderFile = () => (
<>
<MFile
content={getContent()}
renderFileContent={({ body, mimeType, info, encInfo, url }) => (
<FileContent
body={body}
mimeType={mimeType}
renderAsPdfFile={() => (
<ReadPdfFile
body={body}
mimeType={mimeType}
url={url}
encInfo={encInfo}
renderViewer={(p) => <PdfViewer {...p} />}
/>
)}
renderAsTextFile={() => (
<ReadTextFile
body={body}
mimeType={mimeType}
url={url}
encInfo={encInfo}
renderViewer={(p) => <TextViewer {...p} />}
/>
)}
>
<DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
</FileContent>
<FileContent
body={body}
mimeType={mimeType}
renderAsPdfFile={() => (
<ReadPdfFile
body={body}
mimeType={mimeType}
url={url}
encInfo={encInfo}
renderViewer={(p) => <PdfViewer {...p} />}
/>
)}
renderAsTextFile={() => (
<ReadTextFile
body={body}
mimeType={mimeType}
url={url}
encInfo={encInfo}
renderViewer={(p) => <TextViewer {...p} />}
/>
)}
>
<DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
</FileContent>
)}
outlined={outlineAttachment}
/>
@ -188,12 +187,12 @@ export function RenderMessageContent({
<MImage
content={getContent()}
renderImageContent={(props) => (
<ImageContent
{...props}
autoPlay={mediaAutoLoad}
renderImage={(p) => <Image {...p} loading="lazy" />}
renderViewer={(p) => <ImageViewer {...p} />}
/>
<ImageContent
{...props}
autoPlay={mediaAutoLoad}
renderImage={(p) => <Image {...p} loading="lazy" />}
renderViewer={(p) => <ImageViewer {...p} />}
/>
)}
outlined={outlineAttachment}
/>
@ -208,23 +207,21 @@ export function RenderMessageContent({
<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
? () => (
<ThumbnailContent
info={info}
renderImage={(src) => (
<Image alt={body} title={body} src={src} loading="lazy" />
)}
/>
)
<ThumbnailContent
info={info}
renderImage={(src) => (
<Image alt={body} title={body} src={src} loading="lazy" />
)}
/>
)
: undefined
}
renderVideo={(p) => <Video {...p} />}
@ -234,7 +231,6 @@ export function RenderMessageContent({
/>
{renderCaption()}
</>
);
}
@ -251,7 +247,6 @@ export function RenderMessageContent({
/>
{renderCaption()}
</>
);
}

View file

@ -222,6 +222,8 @@ type RenderVideoContentProps = {
mimeType: string;
url: string;
encInfo?: IEncryptedFile;
markedAsSpoiler?: boolean;
spoilerReason?: string;
};
type MVideoProps = {
content: IVideoContent;
@ -256,6 +258,8 @@ export function MVideo({ content, renderAsFile, renderVideoContent, outlined }:
mimeType: safeMimeType,
url: mxcUrl,
encInfo: content.file,
markedAsSpoiler: content[MATRIX_SPOILER_PROPERTY_NAME],
spoilerReason: content[MATRIX_SPOILER_REASON_PROPERTY_NAME],
})}
</AttachmentBox>
</Attachment>

View file

@ -3,6 +3,7 @@ import {
Badge,
Box,
Button,
Chip,
Icon,
Icons,
Spinner,
@ -47,6 +48,8 @@ type VideoContentProps = {
info: IVideoInfo & IThumbnailContent;
encInfo?: EncryptedAttachmentInfo;
autoPlay?: boolean;
markedAsSpoiler?: boolean;
spoilerReason?: string;
renderThumbnail?: () => ReactNode;
renderVideo: (props: RenderVideoProps) => ReactNode;
};
@ -60,6 +63,8 @@ export const VideoContent = as<'div', VideoContentProps>(
info,
encInfo,
autoPlay,
markedAsSpoiler,
spoilerReason,
renderThumbnail,
renderVideo,
...props
@ -72,6 +77,7 @@ export const VideoContent = as<'div', VideoContentProps>(
const [load, setLoad] = useState(false);
const [error, setError] = useState(false);
const [blurred, setBlurred] = useState(markedAsSpoiler ?? false);
const [srcState, loadSrc] = useAsyncCallback(
useCallback(async () => {
@ -114,11 +120,15 @@ export const VideoContent = as<'div', VideoContentProps>(
/>
)}
{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"
@ -133,7 +143,7 @@ export const VideoContent = as<'div', VideoContentProps>(
</Box>
)}
{srcState.status === AsyncStatus.Success && (
<Box className={css.AbsoluteContainer}>
<Box className={classNames(css.AbsoluteContainer, blurred && css.Blur)}>
{renderVideo({
title: body,
src: srcState.data,
@ -144,8 +154,39 @@ export const VideoContent = as<'div', VideoContentProps>(
})}
</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 &&
!markedAsSpoiler && (
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
<Spinner variant="Secondary" />
</Box>

View file

@ -66,7 +66,7 @@ export function UploadCardRenderer({
<Text size="B300">Retry</Text>
</Chip>
)}
{file.type.startsWith('image') && (
{(file.type.startsWith('image') || file.type.startsWith('video')) && (
<TooltipProvider
tooltip={
<Tooltip variant="SurfaceVariant">

View file

@ -82,7 +82,7 @@ export const getVideoMsgContent = async (
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);
@ -91,6 +91,7 @@ export const getVideoMsgContent = async (
msgtype: MsgType.Video,
filename: file.name,
body: file.name,
[MATRIX_SPOILER_PROPERTY_NAME]: metadata.markedAsSpoiler,
};
if (videoEl) {
const [thumbError, thumbContent] = await to(