Add hinting

This commit is contained in:
Half-Shot 2025-06-10 11:28:15 +01:00
parent f740dc3829
commit 9136d841ee
2 changed files with 41 additions and 14 deletions

View File

@ -10,17 +10,27 @@ import type {
CustomMessageRenderFunction,
CustomMessageComponentProps,
OriginalComponentProps,
CustomMessageRenderHints,
} from "@element-hq/element-web-module-api";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import type React from "react";
export class CustomComponentsApi implements ICustomComponentsApi {
private readonly registeredMessageRenderers: {
eventType: string | RegExp;
renderer: CustomMessageRenderFunction;
}[] = [];
type EventRenderer = {
eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean);
renderer: CustomMessageRenderFunction;
hints: CustomMessageRenderHints,
}
public registerMessageRenderer(eventType: string | RegExp, renderer: CustomMessageRenderFunction): void {
this.registeredMessageRenderers.push({ eventType, renderer });
export class CustomComponentsApi implements ICustomComponentsApi {
private readonly registeredMessageRenderers: EventRenderer[] = [];
public registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints: CustomMessageRenderHints = {}): void {
this.registeredMessageRenderers.push({ eventTypeOrFilter: eventTypeOrFilter, renderer, hints });
}
private selectRenderer(mxEvent: MatrixEvent): EventRenderer|undefined {
return this.registeredMessageRenderers.find((rdr) => typeof rdr.eventTypeOrFilter === "string" ? mxEvent.getType().match(rdr.eventTypeOrFilter) : rdr.eventTypeOrFilter(mxEvent));
}
/**
@ -33,14 +43,26 @@ export class CustomComponentsApi implements ICustomComponentsApi {
props: CustomMessageComponentProps,
originalComponent?: (props?: OriginalComponentProps) => React.JSX.Element,
): React.JSX.Element | null {
for (const renderer of this.registeredMessageRenderers.filter((e) =>
props.mxEvent.getType().match(e.eventType),
) ?? []) {
const component = renderer.renderer(props, originalComponent);
if (component) {
return component;
}
const renderer = this.selectRenderer(props.mxEvent);
if (renderer) {
return renderer.renderer(props, originalComponent);
}
return originalComponent?.() || null;
}
/**
* Get hints about a message before rendering it.
* @param props Props to be passed to the custom renderer.
* @param originalComponent Function that will be rendered if no custom renderers are present, or as a child of a custom component.
* @returns A component if a custom renderer exists, or originalComponent returns a value. Otherwise null.
*/
public getHintsForMessage(
mxEvent: MatrixEvent,
): CustomMessageRenderHints {
const renderer = this.selectRenderer(mxEvent);
if (renderer) {
return renderer.hints;
}
return {};
}
}

View File

@ -30,6 +30,7 @@ import { type TimelineRenderingType } from "../contexts/RoomContext";
import { launchPollEditor } from "../components/views/messages/MPollBody";
import { Action } from "../dispatcher/actions";
import { type ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
import ModuleApi from "../modules/Api";
/**
* Returns whether an event should allow actions like reply, reactions, edit, etc.
@ -77,6 +78,10 @@ export function canEditContent(matrixClient: MatrixClient, mxEvent: MatrixEvent)
return false;
}
if (ModuleApi.customComponents.getHintsForMessage(mxEvent).allowEditingEvent === false) {
return false;
}
const { msgtype, body } = mxEvent.getOriginalContent();
return (
M_POLL_START.matches(mxEvent.getType()) ||