* Initial reword of upload to MVVM. * Update tests * More incremental improvements * Refactor tests to use helper method for composer uploads. * Add drag and drop tests * lint * Add commentary * fixup test * More precise selector * Retarget uploads * lint * fixup * one more type * update snap * Fixup composerUploadFiles * fix import * lint * Copy and paste fixes too * Add tests for pasting * Add tests for pasting files. * Remove redundant fn * rm comment * tidy up * Test cleanup * More clean up * another fix * Begin fleshing out * Park changes * More stuff * Use condensed version * Cleanup tests * more cleaning * last bity * Add a test for the composer * Park up changes * Rewrite Measured to be a functional component * Add tests to cover narrow viewports * lint * breakpoint is optional * Cleanup * Support narrow mode * fixup * begone * Provide default value * add label * fixup test * update copyright * cleanup * Be a bit more lazy with FileDropTarget * remove a debug statement * Fixup * fix two snaps * Update screenshot * and the other one * Update snaps * unfake CIDER * update screens again * remove extra test * Undo accidental snapshots * Bit of tidyup * fixup * even more tidyup * may drag and drop file * tidy up again * snap snap snap * Use load to make sonarQube happy * Bunch of refactors * More cleanup * cleanup debug code * tweaks * remove a test we no longer need * make it happy * fix import * fixup * Update snaps * typo * one off * Add tests * lint * remove only * Reduce screenshot scope * fix snapshot usage * cleanup
81 lines
3.1 KiB
TypeScript
81 lines
3.1 KiB
TypeScript
/*
|
|
Copyright 2026 Element Creations Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import {
|
|
type ComposerApi as ModuleComposerApi,
|
|
type ComposerApiFileUploadOption,
|
|
type ComposerApiTarget,
|
|
} from "@element-hq/element-web-module-api";
|
|
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
|
|
|
|
import type { MatrixDispatcher } from "../dispatcher/dispatcher";
|
|
import { Action } from "../dispatcher/actions";
|
|
import { ComposerType, type ComposerInsertPayload } from "../dispatcher/payloads/ComposerInsertPayload";
|
|
import { TimelineRenderingType } from "../contexts/RoomContext";
|
|
import type { ComposerInsertFilesPayload } from "../dispatcher/payloads/ComposerInsertFilePayload";
|
|
|
|
export enum ModuleComposerApiEvents {
|
|
UploaderOptionsChanged = "uploaderOptionsChanged",
|
|
}
|
|
|
|
interface ModuleComposerApiEventsMap {
|
|
[ModuleComposerApiEvents.UploaderOptionsChanged]: (option: ComposerApiFileUploadOption) => void;
|
|
}
|
|
|
|
export class ComposerApi
|
|
extends TypedEventEmitter<ModuleComposerApiEvents, ModuleComposerApiEventsMap>
|
|
implements ModuleComposerApi
|
|
{
|
|
private readonly configuredFileUploadOptions = new Map<string, ComposerApiFileUploadOption>();
|
|
|
|
public constructor(private readonly dispatcher: MatrixDispatcher) {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* List of possible file upload options.
|
|
*/
|
|
public get fileUploadOptions(): ComposerApiFileUploadOption[] {
|
|
return [...this.configuredFileUploadOptions.values()];
|
|
}
|
|
|
|
public addFileUploadOption(option: ComposerApiFileUploadOption): void {
|
|
if (this.configuredFileUploadOptions.has(option.type)) {
|
|
throw new Error(`Option "${option.type}" already exists`);
|
|
}
|
|
if (option.type === "local") {
|
|
throw new Error(`Option "local" is reserved`);
|
|
}
|
|
this.configuredFileUploadOptions.set(option.type, option);
|
|
this.emit(ModuleComposerApiEvents.UploaderOptionsChanged, option);
|
|
}
|
|
|
|
public openFileUploadConfirmation(files: File[], view: ComposerApiTarget = { view: "room" }): void {
|
|
if (!["room", "thread"].includes(view.view)) {
|
|
throw new Error(`Invalid view '${view.view}'`);
|
|
}
|
|
this.dispatcher.dispatch({
|
|
action: Action.ComposerFileInsert,
|
|
files,
|
|
timelineRenderingType: view.view === "room" ? TimelineRenderingType.Room : TimelineRenderingType.Thread,
|
|
} satisfies ComposerInsertFilesPayload);
|
|
}
|
|
|
|
public insertPlaintextIntoComposer(plaintext: string, view: ComposerApiTarget = { view: "room" }): void {
|
|
if (!["room", "thread"].includes(view.view)) {
|
|
throw new Error(`Invalid view '${view.view}'`);
|
|
}
|
|
this.dispatcher.dispatch({
|
|
action: Action.ComposerInsert,
|
|
text: plaintext,
|
|
timelineRenderingType: view.view === "room" ? TimelineRenderingType.Room : TimelineRenderingType.Thread,
|
|
// We only support send.
|
|
composerType: ComposerType.Send,
|
|
} satisfies ComposerInsertPayload);
|
|
}
|
|
}
|