From 09261c53b8c12d8ce6e329aa66f4855ab6d21103 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 15 Jul 2025 14:36:16 +0100 Subject: [PATCH] Update custom component logic to always return a hint. --- src/components/views/elements/ImageView.tsx | 26 ++++++------- .../views/messages/DownloadActionButton.tsx | 29 +++++++------- src/events/EventTileFactory.tsx | 4 +- src/modules/customComponentApi.ts | 38 +++++++++++++------ 4 files changed, 52 insertions(+), 45 deletions(-) diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index f236e5193e..b43564cf37 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -607,20 +607,18 @@ function DownloadButton({ return; } const hints = ModuleApi.customComponents.getHintsForMessage(mxEvent); - if (hints?.allowDownloadingMedia) { - // Disable downloading as soon as we know there is a hint. - setCanDownload(false); - hints - .allowDownloadingMedia() - .then((downloadable) => { - setCanDownload(downloadable); - }) - .catch((ex) => { - logger.error(`Failed to check if media from ${mxEvent.getId()} could be downloaded`, ex); - // Err on the side of safety. - setCanDownload(false); - }); - } + // Disable downloading as soon as we know there is a hint. + setCanDownload(false); + hints + .allowDownloadingMedia() + .then((downloadable) => { + setCanDownload(downloadable); + }) + .catch((ex) => { + logger.error(`Failed to check if media from ${mxEvent.getId()} could be downloaded`, ex); + // Err on the side of safety. + setCanDownload(false); + }); }, [mxEvent]); function showError(e: unknown): void { diff --git a/src/components/views/messages/DownloadActionButton.tsx b/src/components/views/messages/DownloadActionButton.tsx index b1d3407185..2cc2c02944 100644 --- a/src/components/views/messages/DownloadActionButton.tsx +++ b/src/components/views/messages/DownloadActionButton.tsx @@ -44,23 +44,20 @@ export default class DownloadActionButton extends React.PureComponent = { canDownload: true }; - if (moduleHints?.allowDownloadingMedia) { - downloadState.canDownload = null; - moduleHints - .allowDownloadingMedia() - .then((canDownload) => { - this.setState({ - canDownload: canDownload, - }); - }) - .catch((ex) => { - logger.error(`Failed to check if media from ${props.mxEvent.getId()} could be downloaded`, ex); - this.setState({ - canDownload: false, - }); + const downloadState: Pick = { canDownload: null }; + moduleHints + .allowDownloadingMedia() + .then((canDownload) => { + this.setState({ + canDownload, }); - } + }) + .catch((ex) => { + logger.error(`Failed to check if media from ${props.mxEvent.getId()} could be downloaded`, ex); + this.setState({ + canDownload: false, + }); + }); this.state = { loading: false, diff --git a/src/events/EventTileFactory.tsx b/src/events/EventTileFactory.tsx index 9d0daadb80..080cac3bdd 100644 --- a/src/events/EventTileFactory.tsx +++ b/src/events/EventTileFactory.tsx @@ -427,9 +427,7 @@ export function haveRendererForEvent( return false; } - // Check to see if we have any hints for this message, which indicates - // there is a custom renderer for the event. - if (ModuleApi.customComponents.getHintsForMessage(mxEvent)) { + if (ModuleApi.customComponents.hasRendererForEvent(mxEvent)) { return true; } diff --git a/src/modules/customComponentApi.ts b/src/modules/customComponentApi.ts index db2f9ab58a..2729823c79 100644 --- a/src/modules/customComponentApi.ts +++ b/src/modules/customComponentApi.ts @@ -35,6 +35,11 @@ interface CustomMessageRenderHints extends Omit Promise; } +const DEFAULT_HINTS: Required = { + allowEditingEvent: true, + allowDownloadingMedia: () => Promise.resolve(true), +}; + export class CustomComponentsApi implements ICustomComponentsApi { /** * Convert a matrix-js-sdk event into a ModuleMatrixEvent. @@ -115,23 +120,32 @@ export class CustomComponentsApi implements ICustomComponentsApi { return originalComponent?.() ?? null; } + /** + * Has a custom component been registered for this event. + * @param mxEvent + * @returns `true` if a component has been registered and would be rendered, otherwise false. + */ + public hasRendererForEvent(mxEvent: MatrixEvent): boolean { + const moduleEv = CustomComponentsApi.getModuleMatrixEvent(mxEvent); + return !!(moduleEv && this.selectRenderer(moduleEv)); + } + /** * Get hints about an message before rendering it. * @param mxEvent The message event being rendered. - * @returns A component if a custom renderer exists, or originalComponent returns a value. Otherwise null. + * @returns A set of hints to use when rendering messages, provided by custom renderers. If a hint + * is not provided by a renderer, or no renderers are present then `DEFAULT_HINTS` are used. */ - public getHintsForMessage(mxEvent: MatrixEvent): CustomMessageRenderHints | null { + public getHintsForMessage(mxEvent: MatrixEvent): Required { const moduleEv = CustomComponentsApi.getModuleMatrixEvent(mxEvent); const renderer = moduleEv && this.selectRenderer(moduleEv); - if (renderer) { - return { - ...renderer.hints, - // Convert from js-sdk style events to module events automatically. - allowDownloadingMedia: renderer.hints.allowDownloadingMedia - ? () => renderer.hints.allowDownloadingMedia!(moduleEv) - : undefined, - }; - } - return null; + return { + ...DEFAULT_HINTS, + ...renderer?.hints, + // Convert from js-sdk style events to module events automatically. + allowDownloadingMedia: renderer?.hints.allowDownloadingMedia + ? () => renderer.hints.allowDownloadingMedia!(moduleEv!) + : DEFAULT_HINTS.allowDownloadingMedia, + }; } }