From dd4c1a94e6aa704f316891ce58760dee616f8ffb Mon Sep 17 00:00:00 2001 From: Ginger <75683114+gingershaped@users.noreply.github.com> Date: Sat, 22 Feb 2025 03:55:13 -0500 Subject: [PATCH] Add support for spoilers on images (MSC4193) (#2212) * Add support for MSC4193: Spoilers on Media * Clarify variable names and wording * Restore list atom * Improve spoilered image UX with autoload off * Use `aria-pressed` to indicate attachment spoiler state * Improve spoiler button tooltip wording, keep reveal button from conflicting with load errors --- .../components/message/MsgTypeRenderers.tsx | 6 +++ .../message/content/ImageContent.tsx | 46 ++++++++++++++++-- .../components/message/content/style.css.ts | 7 +++ .../upload-card/UploadCardRenderer.tsx | 48 +++++++++++++++++-- src/app/features/room/RoomInput.tsx | 27 +++++++++-- src/app/features/room/msgContent.ts | 13 +++-- src/app/state/list.ts | 11 ++++- src/app/state/room/roomInputDrafts.ts | 13 +++-- src/types/matrix/common.ts | 6 +++ 9 files changed, 158 insertions(+), 19 deletions(-) diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index 6138d0d7..287a5ca4 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -22,6 +22,8 @@ import { IThumbnailContent, IVideoContent, IVideoInfo, + MATRIX_SPOILER_PROPERTY_NAME, + MATRIX_SPOILER_REASON_PROPERTY_NAME, } from '../../../types/matrix/common'; import { FALLBACK_MIMETYPE, getBlobSafeMimeType } from '../../utils/mimeTypes'; import { parseGeoUri, scaleYDimension } from '../../utils/common'; @@ -177,6 +179,8 @@ type RenderImageContentProps = { mimeType?: string; url: string; encInfo?: IEncryptedFile; + markedAsSpoiler?: boolean; + spoilerReason?: string; }; type MImageProps = { content: IImageContent; @@ -204,6 +208,8 @@ export function MImage({ content, renderImageContent, outlined }: MImageProps) { mimeType: imgInfo?.mimetype, url: mxcUrl, encInfo: content.file, + markedAsSpoiler: content[MATRIX_SPOILER_PROPERTY_NAME], + spoilerReason: content[MATRIX_SPOILER_REASON_PROPERTY_NAME], })} diff --git a/src/app/components/message/content/ImageContent.tsx b/src/app/components/message/content/ImageContent.tsx index d4241b64..69c7ade8 100644 --- a/src/app/components/message/content/ImageContent.tsx +++ b/src/app/components/message/content/ImageContent.tsx @@ -3,6 +3,7 @@ import { Badge, Box, Button, + Chip, Icon, Icons, Modal, @@ -51,6 +52,8 @@ export type ImageContentProps = { info?: IImageInfo; encInfo?: EncryptedAttachmentInfo; autoPlay?: boolean; + markedAsSpoiler?: boolean; + spoilerReason?: string; renderViewer: (props: RenderViewerProps) => ReactNode; renderImage: (props: RenderImageProps) => ReactNode; }; @@ -64,6 +67,8 @@ export const ImageContent = as<'div', ImageContentProps>( info, encInfo, autoPlay, + markedAsSpoiler, + spoilerReason, renderViewer, renderImage, ...props @@ -77,6 +82,7 @@ export const ImageContent = as<'div', ImageContentProps>( const [load, setLoad] = useState(false); const [error, setError] = useState(false); const [viewer, setViewer] = useState(false); + const [blurred, setBlurred] = useState(markedAsSpoiler ?? false); const [srcState, loadSrc] = useAsyncCallback( useCallback(async () => { @@ -145,7 +151,7 @@ export const ImageContent = as<'div', ImageContentProps>( punch={1} /> )} - {!autoPlay && srcState.status === AsyncStatus.Idle && ( + {!autoPlay && !markedAsSpoiler && srcState.status === AsyncStatus.Idle && (