Compare commits

...

2 Commits

Author SHA1 Message Date
Will Hunt
e0d176790c Prevent flicker. 2025-07-15 15:23:10 +01:00
Will Hunt
09261c53b8 Update custom component logic to always return a hint. 2025-07-15 14:36:16 +01:00
4 changed files with 45 additions and 31 deletions

View File

@@ -606,9 +606,12 @@ function DownloadButton({
setCanDownload(true);
return;
}
// Get the hint. If the hint is static then we can immediately set it,
// otherwise set to false until the promise completes.
const hints = ModuleApi.customComponents.getHintsForMessage(mxEvent);
if (hints?.allowDownloadingMedia) {
// Disable downloading as soon as we know there is a hint.
if (typeof hints.allowDownloadingMedia === "boolean") {
setCanDownload(hints.allowDownloadingMedia);
} else {
setCanDownload(false);
hints
.allowDownloadingMedia()

View File

@@ -43,22 +43,21 @@ export default class DownloadActionButton extends React.PureComponent<IProps, IS
public constructor(props: IProps) {
super(props);
const moduleHints = ModuleApi.customComponents.getHintsForMessage(props.mxEvent);
const downloadState: Pick<IState, "canDownload"> = { canDownload: true };
if (moduleHints?.allowDownloadingMedia) {
downloadState.canDownload = null;
moduleHints
const hints = ModuleApi.customComponents.getHintsForMessage(props.mxEvent);
const downloadState: Pick<IState, "canDownload"> = { canDownload: null };
if (typeof hints.allowDownloadingMedia === "boolean") {
downloadState.canDownload = hints.allowDownloadingMedia;
} else {
downloadState.canDownload = false;
hints
.allowDownloadingMedia()
.then((canDownload) => {
this.setState({
canDownload: canDownload,
});
.then((downloadable) => {
this.setState({ canDownload: downloadable });
})
.catch((ex) => {
logger.error(`Failed to check if media from ${props.mxEvent.getId()} could be downloaded`, ex);
this.setState({
canDownload: false,
});
// Err on the side of safety.
this.setState({ canDownload: false });
});
}

View File

@@ -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;
}

View File

@@ -31,10 +31,15 @@ interface CustomMessageComponentProps extends Omit<ModuleCustomMessageComponentP
}
interface CustomMessageRenderHints extends Omit<ModuleCustomCustomMessageRenderHints, "allowDownloadingMedia"> {
// Note. This just makes it easier to use this API on Element Web as we already have the moduleized event stored.
allowDownloadingMedia?: () => Promise<boolean>;
// Note. This just makes it easier to use this API on Element Web as we already have the modularized event stored.
allowDownloadingMedia?: (() => Promise<boolean>) | boolean;
}
const DEFAULT_HINTS: Required<CustomMessageRenderHints> = {
allowEditingEvent: true,
allowDownloadingMedia: 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<CustomMessageRenderHints> {
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,
};
}
}