From 81b400acfb4178384dbc50d94980a838a85e0561 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 10 Apr 2025 17:21:20 +0100 Subject: [PATCH] Apply media visible hook to inline images. --- src/HtmlUtils.tsx | 6 +++++- src/Linkify.tsx | 17 +++++++++-------- .../views/messages/EventContentBody.tsx | 3 +++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index f704657c32..ec8662eaf5 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -22,7 +22,7 @@ import { getEmojiFromUnicode } from "@matrix-org/emojibase-bindings"; import SettingsStore from "./settings/SettingsStore"; import { stripHTMLReply, stripPlainReply } from "./utils/Reply"; import { PERMITTED_URL_SCHEMES } from "./utils/UrlUtils"; -import { sanitizeHtmlParams, transformTags } from "./Linkify"; +import { filterImg, sanitizeHtmlParams, transformTags } from "./Linkify"; import { graphemeSegmenter } from "./utils/strings"; export { Linkify, linkifyAndSanitizeHtml } from "./Linkify"; @@ -294,6 +294,7 @@ export interface EventRenderOpts { disableBigEmoji?: boolean; stripReplyFallback?: boolean; forComposerQuote?: boolean; + mediaIsVisible?: boolean; } function analyseEvent(content: IContent, highlights: Optional, opts: EventRenderOpts = {}): EventAnalysis { @@ -301,6 +302,9 @@ function analyseEvent(content: IContent, highlights: Optional, opts: E if (opts.forComposerQuote) { sanitizeParams = composerSanitizeHtmlParams; } + if (!opts.mediaIsVisible) { + sanitizeParams.exclusiveFilter = filterImg; + } try { const isFormattedBody = diff --git a/src/Linkify.tsx b/src/Linkify.tsx index 1a695f315e..b67d0294fe 100644 --- a/src/Linkify.tsx +++ b/src/Linkify.tsx @@ -7,16 +7,14 @@ Please see LICENSE files in the repository root for full details. */ import React, { type ReactElement } from "react"; -import sanitizeHtml, { type IOptions } from "sanitize-html"; +import sanitizeHtml, { IFrame, type IOptions } from "sanitize-html"; import { merge } from "lodash"; import _Linkify from "linkify-react"; import { _linkifyString, ELEMENT_URL_PATTERN, options as linkifyMatrixOptions } from "./linkify-matrix"; -import SettingsStore from "./settings/SettingsStore"; import { tryTransformPermalinkToLocalHref } from "./utils/permalinks/Permalinks"; import { mediaFromMxc } from "./customisations/Media"; import { PERMITTED_URL_SCHEMES } from "./utils/UrlUtils"; -import { MediaPreviewValue } from "./@types/media_preview"; const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; const MEDIA_API_MXC_REGEX = /\/_matrix\/media\/r0\/(?:download|thumbnail)\/(.+?)\/(.+?)(?:[?/]|$)/; @@ -48,11 +46,9 @@ export const transformTags: NonNullable = { // Strip out imgs that aren't `mxc` here instead of using allowedSchemesByTag // because transformTags is used _before_ we filter by allowedSchemesByTag and // we don't want to allow images with `https?` `src`s. - // We also drop inline images (as if they were not present at all) when the "show - // images" preference is disabled. Future work might expose some UI to reveal them - // like standalone image events have. - // TODO: Is this a private room? - if (!src || SettingsStore.getValue("mediaPreviewConfig").media_previews !== MediaPreviewValue.On) { + // Filtering out images now happens as a exlusive filter so we can conditionally apply this + // based on settings. + if (!src) { return { tagName, attribs: {} }; } @@ -200,6 +196,7 @@ export const sanitizeHtmlParams: IOptions = { nestingLimit: 50, }; + /* Wrapper around linkify-react merging in our default linkify options */ export function Linkify({ as, options, children }: React.ComponentProps): ReactElement { return ( @@ -230,3 +227,7 @@ export function linkifyString(str: string, options = linkifyMatrixOptions): stri export function linkifyAndSanitizeHtml(dirtyHtml: string, options = linkifyMatrixOptions): string { return sanitizeHtml(linkifyString(dirtyHtml, options), sanitizeHtmlParams); } + +export function filterImg(frame: IFrame): boolean { + return frame.tag === "img"; +} \ No newline at end of file diff --git a/src/components/views/messages/EventContentBody.tsx b/src/components/views/messages/EventContentBody.tsx index 3e51691c24..74018bbab7 100644 --- a/src/components/views/messages/EventContentBody.tsx +++ b/src/components/views/messages/EventContentBody.tsx @@ -28,6 +28,7 @@ import { import MatrixClientContext from "../../../contexts/MatrixClientContext.tsx"; import { useSettingValue } from "../../../hooks/useSettings.ts"; import { filterBoolean } from "../../../utils/arrays.ts"; +import { useMediaVisible } from "../../../hooks/useMediaVisible.ts"; /** * Returns a RegExp pattern for the keyword in the push rule of the given Matrix event, if any @@ -150,6 +151,7 @@ const EventContentBody = memo( forwardRef( ({ as, mxEvent, stripReply, content, linkify, highlights, includeDir = true, ...options }, ref) => { const enableBigEmoji = useSettingValue("TextualBody.enableBigEmoji"); + const [mediaIsVisible] = useMediaVisible(mxEvent?.getId()!, mxEvent?.getRoomId()!); const replacer = useReplacer(content, mxEvent, options); const linkifyOptions = useMemo( @@ -167,6 +169,7 @@ const EventContentBody = memo( disableBigEmoji: isEmote || !enableBigEmoji, // Part of Replies fallback support stripReplyFallback: stripReply, + mediaIsVisible, }), [content, enableBigEmoji, highlights, isEmote, stripReply], );