Add a module API for overriding the composer preview. (#33978)
* Modify LinkPreview to export shared atomics * Implement MessageComposerUrlPreviewView * Create UrlPreviewFetcher utility function * Modify view models * Implement in composer * Support running tests in dom-less vitest environment * Add a playwright test * hide another one * fmt * cleanup * test rte too * fixup * Add back docstring * cleanup * off by one * remove description check * Cleanup hacks * Remove another hack * cleanup * one more * fixup window here too * whoops type * Rename to be cleaeer * Trim URLs first * fix bug * Add a module API for overriding the composer preview. * tests + cleanup * fixup comment * revert accidental change * fixup * happy tests * cleanup
This commit is contained in:
@@ -17,10 +17,13 @@ import type {
|
||||
MatrixEvent as ModuleMatrixEvent,
|
||||
CustomRoomPreviewBarRenderFunction,
|
||||
CustomLoginRenderFunction,
|
||||
CustomComposerPreviewRenderFunction,
|
||||
CustomComposerPreviewComponentProps,
|
||||
} from "@element-hq/element-web-module-api";
|
||||
import type React from "react";
|
||||
|
||||
type EventTypeOrFilter = Parameters<ICustomComponentsApi["registerMessageRenderer"]>[0];
|
||||
type ComposerPreviewFilterFn = Parameters<ICustomComponentsApi["registerComposerPreview"]>[0];
|
||||
|
||||
type EventRenderer = {
|
||||
eventTypeOrFilter: EventTypeOrFilter;
|
||||
@@ -37,6 +40,11 @@ interface CustomMessageRenderHints extends Omit<ModuleCustomCustomMessageRenderH
|
||||
allowDownloadingMedia?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
type ComposerPreviewRenderer = {
|
||||
filter: ComposerPreviewFilterFn;
|
||||
renderer: CustomComposerPreviewRenderFunction;
|
||||
};
|
||||
|
||||
export class CustomComponentsApi implements ICustomComponentsApi {
|
||||
/**
|
||||
* Convert a matrix-js-sdk event into a ModuleMatrixEvent.
|
||||
@@ -66,6 +74,7 @@ export class CustomComponentsApi implements ICustomComponentsApi {
|
||||
}
|
||||
|
||||
private readonly registeredMessageRenderers: EventRenderer[] = [];
|
||||
private readonly registeredComposerPreviewRenderers: ComposerPreviewRenderer[] = [];
|
||||
|
||||
public registerMessageRenderer(
|
||||
eventTypeOrFilter: EventTypeOrFilter,
|
||||
@@ -80,7 +89,7 @@ export class CustomComponentsApi implements ICustomComponentsApi {
|
||||
* @param mxEvent The message event being rendered.
|
||||
* @returns The registered renderer.
|
||||
*/
|
||||
private selectRenderer(mxEvent: ModuleMatrixEvent): EventRenderer | undefined {
|
||||
private selectMessageRenderer(mxEvent: ModuleMatrixEvent): EventRenderer | undefined {
|
||||
return this.registeredMessageRenderers.find((renderer) => {
|
||||
if (typeof renderer.eventTypeOrFilter === "string") {
|
||||
return renderer.eventTypeOrFilter === mxEvent.type;
|
||||
@@ -106,7 +115,7 @@ export class CustomComponentsApi implements ICustomComponentsApi {
|
||||
originalComponent?: (props?: OriginalMessageComponentProps) => React.JSX.Element,
|
||||
): React.JSX.Element | null {
|
||||
const moduleEv = CustomComponentsApi.getModuleMatrixEvent(props.mxEvent);
|
||||
const renderer = moduleEv && this.selectRenderer(moduleEv);
|
||||
const renderer = moduleEv && this.selectMessageRenderer(moduleEv);
|
||||
if (renderer) {
|
||||
try {
|
||||
return renderer.renderer({ ...props, mxEvent: moduleEv }, originalComponent);
|
||||
@@ -125,7 +134,7 @@ export class CustomComponentsApi implements ICustomComponentsApi {
|
||||
*/
|
||||
public getHintsForMessage(mxEvent: MatrixEvent): CustomMessageRenderHints | null {
|
||||
const moduleEv = CustomComponentsApi.getModuleMatrixEvent(mxEvent);
|
||||
const renderer = moduleEv && this.selectRenderer(moduleEv);
|
||||
const renderer = moduleEv && this.selectMessageRenderer(moduleEv);
|
||||
if (renderer) {
|
||||
return {
|
||||
...renderer.hints,
|
||||
@@ -171,4 +180,31 @@ export class CustomComponentsApi implements ICustomComponentsApi {
|
||||
public registerLoginComponent(renderer: CustomLoginRenderFunction): void {
|
||||
this._loginRenderer = renderer;
|
||||
}
|
||||
|
||||
public registerComposerPreview(
|
||||
filter: ComposerPreviewFilterFn,
|
||||
renderer: CustomComposerPreviewRenderFunction,
|
||||
): void {
|
||||
this.registeredComposerPreviewRenderers.push({ filter, renderer });
|
||||
}
|
||||
/**
|
||||
* Render the component for a composer preview.
|
||||
* @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 was found. Otherwise null.
|
||||
*/
|
||||
public renderComposerPreview(
|
||||
props: CustomComposerPreviewComponentProps,
|
||||
originalComponent: (props?: CustomComposerPreviewComponentProps) => React.JSX.Element,
|
||||
): React.JSX.Element | null {
|
||||
const renderer = this.registeredComposerPreviewRenderers.find(({ filter }) => filter(props.text, props.roomId));
|
||||
if (renderer) {
|
||||
try {
|
||||
return renderer.renderer({ ...props }, originalComponent);
|
||||
} catch (ex) {
|
||||
logger.warn("Composer preview failed to render", ex);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user