mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-10 17:20:28 +03:00
Make it possible to mark videos as spoilers
This commit is contained in:
parent
ccd4327dcc
commit
2fa748a470
5 changed files with 95 additions and 54 deletions
|
|
@ -29,7 +29,7 @@ import { ImageViewer } from './image-viewer';
|
||||||
import { PdfViewer } from './Pdf-viewer';
|
import { PdfViewer } from './Pdf-viewer';
|
||||||
import { TextViewer } from './text-viewer';
|
import { TextViewer } from './text-viewer';
|
||||||
import { testMatrixTo } from '../plugins/matrix-to';
|
import { testMatrixTo } from '../plugins/matrix-to';
|
||||||
import {IImageContent} from "../../types/matrix/common";
|
import { IImageContent } from '../../types/matrix/common';
|
||||||
|
|
||||||
type RenderMessageContentProps = {
|
type RenderMessageContentProps = {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
|
|
@ -70,7 +70,7 @@ export function RenderMessageContent({
|
||||||
};
|
};
|
||||||
const renderCaption = () => {
|
const renderCaption = () => {
|
||||||
const content: IImageContent = getContent();
|
const content: IImageContent = getContent();
|
||||||
if(content.filename && content.filename !== content.body) {
|
if (content.filename && content.filename !== content.body) {
|
||||||
return (
|
return (
|
||||||
<MText
|
<MText
|
||||||
edited={edited}
|
edited={edited}
|
||||||
|
|
@ -85,41 +85,40 @@ export function RenderMessageContent({
|
||||||
)}
|
)}
|
||||||
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
|
renderUrlsPreview={urlPreview ? renderUrlsPreview : undefined}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
};
|
||||||
|
|
||||||
const renderFile = () => (
|
const renderFile = () => (
|
||||||
<>
|
<>
|
||||||
<MFile
|
<MFile
|
||||||
content={getContent()}
|
content={getContent()}
|
||||||
renderFileContent={({ body, mimeType, info, encInfo, url }) => (
|
renderFileContent={({ body, mimeType, info, encInfo, url }) => (
|
||||||
<FileContent
|
<FileContent
|
||||||
body={body}
|
body={body}
|
||||||
mimeType={mimeType}
|
mimeType={mimeType}
|
||||||
renderAsPdfFile={() => (
|
renderAsPdfFile={() => (
|
||||||
<ReadPdfFile
|
<ReadPdfFile
|
||||||
body={body}
|
body={body}
|
||||||
mimeType={mimeType}
|
mimeType={mimeType}
|
||||||
url={url}
|
url={url}
|
||||||
encInfo={encInfo}
|
encInfo={encInfo}
|
||||||
renderViewer={(p) => <PdfViewer {...p} />}
|
renderViewer={(p) => <PdfViewer {...p} />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
renderAsTextFile={() => (
|
renderAsTextFile={() => (
|
||||||
<ReadTextFile
|
<ReadTextFile
|
||||||
body={body}
|
body={body}
|
||||||
mimeType={mimeType}
|
mimeType={mimeType}
|
||||||
url={url}
|
url={url}
|
||||||
encInfo={encInfo}
|
encInfo={encInfo}
|
||||||
renderViewer={(p) => <TextViewer {...p} />}
|
renderViewer={(p) => <TextViewer {...p} />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
|
<DownloadFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} info={info} />
|
||||||
</FileContent>
|
</FileContent>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
outlined={outlineAttachment}
|
outlined={outlineAttachment}
|
||||||
/>
|
/>
|
||||||
|
|
@ -188,12 +187,12 @@ export function RenderMessageContent({
|
||||||
<MImage
|
<MImage
|
||||||
content={getContent()}
|
content={getContent()}
|
||||||
renderImageContent={(props) => (
|
renderImageContent={(props) => (
|
||||||
<ImageContent
|
<ImageContent
|
||||||
{...props}
|
{...props}
|
||||||
autoPlay={mediaAutoLoad}
|
autoPlay={mediaAutoLoad}
|
||||||
renderImage={(p) => <Image {...p} loading="lazy" />}
|
renderImage={(p) => <Image {...p} loading="lazy" />}
|
||||||
renderViewer={(p) => <ImageViewer {...p} />}
|
renderViewer={(p) => <ImageViewer {...p} />}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
outlined={outlineAttachment}
|
outlined={outlineAttachment}
|
||||||
/>
|
/>
|
||||||
|
|
@ -208,23 +207,21 @@ export function RenderMessageContent({
|
||||||
<MVideo
|
<MVideo
|
||||||
content={getContent()}
|
content={getContent()}
|
||||||
renderAsFile={renderFile}
|
renderAsFile={renderFile}
|
||||||
renderVideoContent={({ body, info, mimeType, url, encInfo }) => (
|
renderVideoContent={({ body, info, ...props }) => (
|
||||||
<VideoContent
|
<VideoContent
|
||||||
body={body}
|
body={body}
|
||||||
info={info}
|
info={info}
|
||||||
mimeType={mimeType}
|
{...props}
|
||||||
url={url}
|
|
||||||
encInfo={encInfo}
|
|
||||||
renderThumbnail={
|
renderThumbnail={
|
||||||
mediaAutoLoad
|
mediaAutoLoad
|
||||||
? () => (
|
? () => (
|
||||||
<ThumbnailContent
|
<ThumbnailContent
|
||||||
info={info}
|
info={info}
|
||||||
renderImage={(src) => (
|
renderImage={(src) => (
|
||||||
<Image alt={body} title={body} src={src} loading="lazy" />
|
<Image alt={body} title={body} src={src} loading="lazy" />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
renderVideo={(p) => <Video {...p} />}
|
renderVideo={(p) => <Video {...p} />}
|
||||||
|
|
@ -234,7 +231,6 @@ export function RenderMessageContent({
|
||||||
/>
|
/>
|
||||||
{renderCaption()}
|
{renderCaption()}
|
||||||
</>
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +247,6 @@ export function RenderMessageContent({
|
||||||
/>
|
/>
|
||||||
{renderCaption()}
|
{renderCaption()}
|
||||||
</>
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,8 @@ type RenderVideoContentProps = {
|
||||||
mimeType: string;
|
mimeType: string;
|
||||||
url: string;
|
url: string;
|
||||||
encInfo?: IEncryptedFile;
|
encInfo?: IEncryptedFile;
|
||||||
|
markedAsSpoiler?: boolean;
|
||||||
|
spoilerReason?: string;
|
||||||
};
|
};
|
||||||
type MVideoProps = {
|
type MVideoProps = {
|
||||||
content: IVideoContent;
|
content: IVideoContent;
|
||||||
|
|
@ -256,6 +258,8 @@ export function MVideo({ content, renderAsFile, renderVideoContent, outlined }:
|
||||||
mimeType: safeMimeType,
|
mimeType: safeMimeType,
|
||||||
url: mxcUrl,
|
url: mxcUrl,
|
||||||
encInfo: content.file,
|
encInfo: content.file,
|
||||||
|
markedAsSpoiler: content[MATRIX_SPOILER_PROPERTY_NAME],
|
||||||
|
spoilerReason: content[MATRIX_SPOILER_REASON_PROPERTY_NAME],
|
||||||
})}
|
})}
|
||||||
</AttachmentBox>
|
</AttachmentBox>
|
||||||
</Attachment>
|
</Attachment>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {
|
||||||
Badge,
|
Badge,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
Chip,
|
||||||
Icon,
|
Icon,
|
||||||
Icons,
|
Icons,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
|
@ -47,6 +48,8 @@ type VideoContentProps = {
|
||||||
info: IVideoInfo & IThumbnailContent;
|
info: IVideoInfo & IThumbnailContent;
|
||||||
encInfo?: EncryptedAttachmentInfo;
|
encInfo?: EncryptedAttachmentInfo;
|
||||||
autoPlay?: boolean;
|
autoPlay?: boolean;
|
||||||
|
markedAsSpoiler?: boolean;
|
||||||
|
spoilerReason?: string;
|
||||||
renderThumbnail?: () => ReactNode;
|
renderThumbnail?: () => ReactNode;
|
||||||
renderVideo: (props: RenderVideoProps) => ReactNode;
|
renderVideo: (props: RenderVideoProps) => ReactNode;
|
||||||
};
|
};
|
||||||
|
|
@ -60,6 +63,8 @@ export const VideoContent = as<'div', VideoContentProps>(
|
||||||
info,
|
info,
|
||||||
encInfo,
|
encInfo,
|
||||||
autoPlay,
|
autoPlay,
|
||||||
|
markedAsSpoiler,
|
||||||
|
spoilerReason,
|
||||||
renderThumbnail,
|
renderThumbnail,
|
||||||
renderVideo,
|
renderVideo,
|
||||||
...props
|
...props
|
||||||
|
|
@ -72,6 +77,7 @@ export const VideoContent = as<'div', VideoContentProps>(
|
||||||
|
|
||||||
const [load, setLoad] = useState(false);
|
const [load, setLoad] = useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
|
const [blurred, setBlurred] = useState(markedAsSpoiler ?? false);
|
||||||
|
|
||||||
const [srcState, loadSrc] = useAsyncCallback(
|
const [srcState, loadSrc] = useAsyncCallback(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
|
|
@ -114,11 +120,15 @@ export const VideoContent = as<'div', VideoContentProps>(
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{renderThumbnail && !load && (
|
{renderThumbnail && !load && (
|
||||||
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
|
<Box
|
||||||
|
className={classNames(css.AbsoluteContainer, blurred && css.Blur)}
|
||||||
|
alignItems="Center"
|
||||||
|
justifyContent="Center"
|
||||||
|
>
|
||||||
{renderThumbnail()}
|
{renderThumbnail()}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{!autoPlay && srcState.status === AsyncStatus.Idle && (
|
{!autoPlay && !blurred && srcState.status === AsyncStatus.Idle && (
|
||||||
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
|
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
|
||||||
<Button
|
<Button
|
||||||
variant="Secondary"
|
variant="Secondary"
|
||||||
|
|
@ -133,7 +143,7 @@ export const VideoContent = as<'div', VideoContentProps>(
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{srcState.status === AsyncStatus.Success && (
|
{srcState.status === AsyncStatus.Success && (
|
||||||
<Box className={css.AbsoluteContainer}>
|
<Box className={classNames(css.AbsoluteContainer, blurred && css.Blur)}>
|
||||||
{renderVideo({
|
{renderVideo({
|
||||||
title: body,
|
title: body,
|
||||||
src: srcState.data,
|
src: srcState.data,
|
||||||
|
|
@ -144,8 +154,39 @@ export const VideoContent = as<'div', VideoContentProps>(
|
||||||
})}
|
})}
|
||||||
</Box>
|
</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) &&
|
{(srcState.status === AsyncStatus.Loading || srcState.status === AsyncStatus.Success) &&
|
||||||
!load && (
|
!load &&
|
||||||
|
!markedAsSpoiler && (
|
||||||
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
|
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">
|
||||||
<Spinner variant="Secondary" />
|
<Spinner variant="Secondary" />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ export function UploadCardRenderer({
|
||||||
<Text size="B300">Retry</Text>
|
<Text size="B300">Retry</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
)}
|
||||||
{file.type.startsWith('image') && (
|
{(file.type.startsWith('image') || file.type.startsWith('video')) && (
|
||||||
<TooltipProvider
|
<TooltipProvider
|
||||||
tooltip={
|
tooltip={
|
||||||
<Tooltip variant="SurfaceVariant">
|
<Tooltip variant="SurfaceVariant">
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ export const getVideoMsgContent = async (
|
||||||
item: TUploadItem,
|
item: TUploadItem,
|
||||||
mxc: string
|
mxc: string
|
||||||
): Promise<IContent> => {
|
): Promise<IContent> => {
|
||||||
const { file, originalFile, encInfo } = item;
|
const { file, originalFile, encInfo, metadata } = item;
|
||||||
|
|
||||||
const [videoError, videoEl] = await to(loadVideoElement(getVideoFileUrl(originalFile)));
|
const [videoError, videoEl] = await to(loadVideoElement(getVideoFileUrl(originalFile)));
|
||||||
if (videoError) console.warn(videoError);
|
if (videoError) console.warn(videoError);
|
||||||
|
|
@ -91,6 +91,7 @@ export const getVideoMsgContent = async (
|
||||||
msgtype: MsgType.Video,
|
msgtype: MsgType.Video,
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
body: file.name,
|
body: file.name,
|
||||||
|
[MATRIX_SPOILER_PROPERTY_NAME]: metadata.markedAsSpoiler,
|
||||||
};
|
};
|
||||||
if (videoEl) {
|
if (videoEl) {
|
||||||
const [thumbError, thumbContent] = await to(
|
const [thumbError, thumbContent] = await to(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue