Improve types for sendEvent
(#12335)
This commit is contained in:
parent
4941327c78
commit
ef2bd7ae04
26 changed files with 209 additions and 99 deletions
|
@ -32,7 +32,9 @@ import type {
|
||||||
UploadOpts,
|
UploadOpts,
|
||||||
Upload,
|
Upload,
|
||||||
StateEvents,
|
StateEvents,
|
||||||
|
TimelineEvents,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
|
import type { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
import { Credentials } from "../plugins/homeserver";
|
import { Credentials } from "../plugins/homeserver";
|
||||||
|
|
||||||
export class Client {
|
export class Client {
|
||||||
|
@ -98,7 +100,12 @@ export class Client {
|
||||||
const client = await this.prepareClient();
|
const client = await this.prepareClient();
|
||||||
return client.evaluate(
|
return client.evaluate(
|
||||||
async (client, { roomId, threadId, eventType, content }) => {
|
async (client, { roomId, threadId, eventType, content }) => {
|
||||||
return client.sendEvent(roomId, threadId, eventType, content);
|
return client.sendEvent(
|
||||||
|
roomId,
|
||||||
|
threadId,
|
||||||
|
eventType as keyof TimelineEvents,
|
||||||
|
content as TimelineEvents[keyof TimelineEvents],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
{ roomId, threadId, eventType, content },
|
{ roomId, threadId, eventType, content },
|
||||||
);
|
);
|
||||||
|
@ -125,7 +132,7 @@ export class Client {
|
||||||
const client = await this.prepareClient();
|
const client = await this.prepareClient();
|
||||||
return client.evaluate(
|
return client.evaluate(
|
||||||
(client, { roomId, content, threadId }) => {
|
(client, { roomId, content, threadId }) => {
|
||||||
return client.sendMessage(roomId, threadId, content);
|
return client.sendMessage(roomId, threadId, content as RoomMessageEventContent);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
roomId,
|
roomId,
|
||||||
|
|
34
src/@types/matrix-js-sdk.d.ts
vendored
34
src/@types/matrix-js-sdk.d.ts
vendored
|
@ -19,8 +19,9 @@ import type { BLURHASH_FIELD } from "../utils/image-media";
|
||||||
import type { JitsiCallMemberEventType, JitsiCallMemberContent } from "../call-types";
|
import type { JitsiCallMemberEventType, JitsiCallMemberContent } from "../call-types";
|
||||||
import type { ILayoutStateEvent, WIDGET_LAYOUT_EVENT_TYPE } from "../stores/widgets/types";
|
import type { ILayoutStateEvent, WIDGET_LAYOUT_EVENT_TYPE } from "../stores/widgets/types";
|
||||||
import type { VoiceBroadcastInfoEventContent, VoiceBroadcastInfoEventType } from "../voice-broadcast/types";
|
import type { VoiceBroadcastInfoEventContent, VoiceBroadcastInfoEventType } from "../voice-broadcast/types";
|
||||||
|
import type { EncryptedFile } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
// Matrix JS SDK extensions
|
// Extend Matrix JS SDK types via Typescript declaration merging to support unspecced event fields and types
|
||||||
declare module "matrix-js-sdk/src/types" {
|
declare module "matrix-js-sdk/src/types" {
|
||||||
export interface FileInfo {
|
export interface FileInfo {
|
||||||
/**
|
/**
|
||||||
|
@ -50,4 +51,35 @@ declare module "matrix-js-sdk/src/types" {
|
||||||
};
|
};
|
||||||
"m.room.bot.options": unknown;
|
"m.room.bot.options": unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TimelineEvents {
|
||||||
|
"io.element.performance_metric": {
|
||||||
|
"io.element.performance_metrics": {
|
||||||
|
forEventId: string;
|
||||||
|
responseTs: number;
|
||||||
|
kind: "send_time";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AudioContent {
|
||||||
|
// MSC1767 + Ideals of MSC2516 as MSC3245
|
||||||
|
// https://github.com/matrix-org/matrix-doc/pull/3245
|
||||||
|
"org.matrix.msc1767.text"?: string;
|
||||||
|
"org.matrix.msc1767.file"?: {
|
||||||
|
url?: string;
|
||||||
|
file?: EncryptedFile;
|
||||||
|
name: string;
|
||||||
|
mimetype: string;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
"org.matrix.msc1767.audio"?: {
|
||||||
|
duration: number;
|
||||||
|
// https://github.com/matrix-org/matrix-doc/pull/3246
|
||||||
|
waveform?: number[];
|
||||||
|
};
|
||||||
|
"org.matrix.msc3245.voice"?: {};
|
||||||
|
|
||||||
|
"io.element.voice_broadcast_chunk"?: { sequence: number };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,7 +622,7 @@ export default class ContentMessages {
|
||||||
if (upload.cancelled) throw new UploadCanceledError();
|
if (upload.cancelled) throw new UploadCanceledError();
|
||||||
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
|
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
|
||||||
|
|
||||||
const response = await matrixClient.sendMessage(roomId, threadId ?? null, content);
|
const response = await matrixClient.sendMessage(roomId, threadId ?? null, content as MediaEventContent);
|
||||||
|
|
||||||
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
||||||
sendRoundTripMetric(matrixClient, roomId, response.event_id);
|
sendRoundTripMetric(matrixClient, roomId, response.event_id);
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {
|
||||||
RoomType,
|
RoomType,
|
||||||
SyncStateData,
|
SyncStateData,
|
||||||
SyncState,
|
SyncState,
|
||||||
|
TimelineEvents,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { defer, IDeferred, QueryDict } from "matrix-js-sdk/src/utils";
|
import { defer, IDeferred, QueryDict } from "matrix-js-sdk/src/utils";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
@ -1930,7 +1931,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (!cli) return;
|
if (!cli) return;
|
||||||
|
|
||||||
cli.sendEvent(roomId, event.getType(), event.getContent()).then(() => {
|
cli.sendEvent(roomId, event.getType() as keyof TimelineEvents, event.getContent()).then(() => {
|
||||||
dis.dispatch({ action: "message_sent" });
|
dis.dispatch({ action: "message_sent" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { MatrixEvent, MatrixClient, TimelineEvents } from "matrix-js-sdk/src/matrix";
|
||||||
import { PollEndEvent } from "matrix-js-sdk/src/extensible_events_v1/PollEndEvent";
|
import { PollEndEvent } from "matrix-js-sdk/src/extensible_events_v1/PollEndEvent";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
|
@ -51,7 +51,11 @@ export default class EndPollDialog extends React.Component<IProps> {
|
||||||
|
|
||||||
const endEvent = PollEndEvent.from(this.props.event.getId()!, message).serialize();
|
const endEvent = PollEndEvent.from(this.props.event.getId()!, message).serialize();
|
||||||
|
|
||||||
await this.props.matrixClient.sendEvent(this.props.event.getRoomId()!, endEvent.type, endEvent.content);
|
await this.props.matrixClient.sendEvent(
|
||||||
|
this.props.event.getRoomId()!,
|
||||||
|
endEvent.type as keyof TimelineEvents,
|
||||||
|
endEvent.content as TimelineEvents[keyof TimelineEvents],
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to submit poll response event:", e);
|
console.error("Failed to submit poll response event:", e);
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import {
|
||||||
LocationAssetType,
|
LocationAssetType,
|
||||||
M_TIMESTAMP,
|
M_TIMESTAMP,
|
||||||
M_BEACON,
|
M_BEACON,
|
||||||
|
TimelineEvents,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
|
@ -80,10 +81,10 @@ interface IProps {
|
||||||
onFinished(): void;
|
onFinished(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IEntryProps {
|
interface IEntryProps<K extends keyof TimelineEvents> {
|
||||||
room: Room;
|
room: Room;
|
||||||
type: EventType | string;
|
type: K;
|
||||||
content: IContent;
|
content: TimelineEvents[K];
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
onFinished(success: boolean): void;
|
onFinished(success: boolean): void;
|
||||||
}
|
}
|
||||||
|
@ -95,7 +96,7 @@ enum SendState {
|
||||||
Failed,
|
Failed,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Entry: React.FC<IEntryProps> = ({ room, type, content, matrixClient: cli, onFinished }) => {
|
const Entry: React.FC<IEntryProps<any>> = ({ room, type, content, matrixClient: cli, onFinished }) => {
|
||||||
const [sendState, setSendState] = useState<SendState>(SendState.CanSend);
|
const [sendState, setSendState] = useState<SendState>(SendState.CanSend);
|
||||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLDivElement>();
|
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLDivElement>();
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,12 @@ import Field from "../elements/Field";
|
||||||
import Spinner from "../elements/Spinner";
|
import Spinner from "../elements/Spinner";
|
||||||
import LabelledCheckbox from "../elements/LabelledCheckbox";
|
import LabelledCheckbox from "../elements/LabelledCheckbox";
|
||||||
|
|
||||||
|
declare module "matrix-js-sdk/src/types" {
|
||||||
|
interface TimelineEvents {
|
||||||
|
[ABUSE_EVENT_TYPE]: AbuseEventContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
onFinished(report?: boolean): void;
|
onFinished(report?: boolean): void;
|
||||||
|
@ -56,7 +62,16 @@ const MODERATED_BY_STATE_EVENT_TYPE = [
|
||||||
*/
|
*/
|
||||||
];
|
];
|
||||||
|
|
||||||
const ABUSE_EVENT_TYPE = "org.matrix.msc3215.abuse.report";
|
export const ABUSE_EVENT_TYPE = "org.matrix.msc3215.abuse.report";
|
||||||
|
|
||||||
|
interface AbuseEventContent {
|
||||||
|
event_id: string;
|
||||||
|
room_id: string;
|
||||||
|
moderated_by_id: string;
|
||||||
|
nature?: ExtendedNature;
|
||||||
|
reporter: string;
|
||||||
|
comment: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Standard abuse natures.
|
// Standard abuse natures.
|
||||||
enum Nature {
|
enum Nature {
|
||||||
|
@ -250,13 +265,13 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.sendEvent(dmRoomId, ABUSE_EVENT_TYPE, {
|
await client.sendEvent(dmRoomId, ABUSE_EVENT_TYPE, {
|
||||||
event_id: ev.getId(),
|
event_id: ev.getId()!,
|
||||||
room_id: ev.getRoomId(),
|
room_id: ev.getRoomId()!,
|
||||||
moderated_by_id: this.moderation.moderationRoomId,
|
moderated_by_id: this.moderation.moderationRoomId,
|
||||||
nature,
|
nature,
|
||||||
reporter: client.getUserId(),
|
reporter: client.getUserId()!,
|
||||||
comment: this.state.reason.trim(),
|
comment: this.state.reason.trim(),
|
||||||
});
|
} satisfies AbuseEventContent);
|
||||||
} else {
|
} else {
|
||||||
// Report to homeserver admin through the dedicated Matrix API.
|
// Report to homeserver admin through the dedicated Matrix API.
|
||||||
await client.reportEvent(ev.getRoomId()!, ev.getId()!, -100, this.state.reason.trim());
|
await client.reportEvent(ev.getRoomId()!, ev.getId()!, -100, this.state.reason.trim());
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ChangeEvent, ReactNode, useContext, useMemo, useRef, useState } from "react";
|
import React, { ChangeEvent, ReactNode, useContext, useMemo, useRef, useState } from "react";
|
||||||
import { IContent, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { IContent, MatrixEvent, TimelineEvents } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { _t, _td, TranslationKey } from "../../../../languageHandler";
|
import { _t, _td, TranslationKey } from "../../../../languageHandler";
|
||||||
import Field from "../../elements/Field";
|
import Field from "../../elements/Field";
|
||||||
|
@ -32,7 +32,7 @@ export const stringify = (object: object): string => {
|
||||||
interface IEventEditorProps extends Pick<IDevtoolsProps, "onBack"> {
|
interface IEventEditorProps extends Pick<IDevtoolsProps, "onBack"> {
|
||||||
fieldDefs: IFieldDef[]; // immutable
|
fieldDefs: IFieldDef[]; // immutable
|
||||||
defaultContent?: string;
|
defaultContent?: string;
|
||||||
onSend(fields: string[], content?: IContent): Promise<unknown>;
|
onSend(fields: string[], content: IContent): Promise<unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFieldDef {
|
interface IFieldDef {
|
||||||
|
@ -180,8 +180,8 @@ export const TimelineEventEditor: React.FC<IEditorProps> = ({ mxEvent, onBack })
|
||||||
|
|
||||||
const fields = useMemo(() => [eventTypeField(mxEvent?.getType())], [mxEvent]);
|
const fields = useMemo(() => [eventTypeField(mxEvent?.getType())], [mxEvent]);
|
||||||
|
|
||||||
const onSend = ([eventType]: string[], content?: IContent): Promise<unknown> => {
|
const onSend = ([eventType]: string[], content: TimelineEvents[keyof TimelineEvents]): Promise<unknown> => {
|
||||||
return cli.sendEvent(context.room.roomId, eventType, content || {});
|
return cli.sendEvent(context.room.roomId, eventType as keyof TimelineEvents, content);
|
||||||
};
|
};
|
||||||
|
|
||||||
let defaultContent: string | undefined;
|
let defaultContent: string | undefined;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
M_POLL_KIND_UNDISCLOSED,
|
M_POLL_KIND_UNDISCLOSED,
|
||||||
M_POLL_START,
|
M_POLL_START,
|
||||||
IPartialEvent,
|
IPartialEvent,
|
||||||
|
TimelineEvents,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
||||||
|
|
||||||
|
@ -166,8 +167,8 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
|
||||||
this.matrixClient.sendEvent(
|
this.matrixClient.sendEvent(
|
||||||
actualRoomId,
|
actualRoomId,
|
||||||
this.props.threadId ?? null,
|
this.props.threadId ?? null,
|
||||||
pollEvent.type,
|
pollEvent.type as keyof TimelineEvents,
|
||||||
pollEvent.content,
|
pollEvent.content as TimelineEvents[keyof TimelineEvents],
|
||||||
),
|
),
|
||||||
this.matrixClient,
|
this.matrixClient,
|
||||||
)
|
)
|
||||||
|
|
|
@ -108,7 +108,7 @@ class ReactionPicker extends React.Component<IProps, IState> {
|
||||||
MatrixClientPeg.safeGet().sendEvent(this.props.mxEvent.getRoomId()!, EventType.Reaction, {
|
MatrixClientPeg.safeGet().sendEvent(this.props.mxEvent.getRoomId()!, EventType.Reaction, {
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
rel_type: RelationType.Annotation,
|
rel_type: RelationType.Annotation,
|
||||||
event_id: this.props.mxEvent.getId(),
|
event_id: this.props.mxEvent.getId()!,
|
||||||
key: reaction,
|
key: reaction,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,13 +16,13 @@ limitations under the License.
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
IContent,
|
|
||||||
IEventRelation,
|
IEventRelation,
|
||||||
MatrixError,
|
MatrixError,
|
||||||
THREAD_RELATION_TYPE,
|
THREAD_RELATION_TYPE,
|
||||||
ContentHelpers,
|
ContentHelpers,
|
||||||
LocationAssetType,
|
LocationAssetType,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
|
import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
|
@ -146,7 +146,7 @@ export const shareLocation =
|
||||||
timestamp,
|
timestamp,
|
||||||
undefined,
|
undefined,
|
||||||
assetType,
|
assetType,
|
||||||
) as IContent;
|
) as RoomMessageEventContent;
|
||||||
await doMaybeLocalRoomAction(
|
await doMaybeLocalRoomAction(
|
||||||
roomId,
|
roomId,
|
||||||
(actualRoomId: string) => client.sendMessage(actualRoomId, threadId, content),
|
(actualRoomId: string) => client.sendMessage(actualRoomId, threadId, content),
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
M_POLL_KIND_DISCLOSED,
|
M_POLL_KIND_DISCLOSED,
|
||||||
M_POLL_RESPONSE,
|
M_POLL_RESPONSE,
|
||||||
M_POLL_START,
|
M_POLL_START,
|
||||||
|
TimelineEvents,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { RelatedRelations } from "matrix-js-sdk/src/models/related-relations";
|
import { RelatedRelations } from "matrix-js-sdk/src/models/related-relations";
|
||||||
import { PollStartEvent, PollAnswerSubevent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
import { PollStartEvent, PollAnswerSubevent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
||||||
|
@ -225,14 +226,20 @@ export default class MPollBody extends React.Component<IBodyProps, IState> {
|
||||||
|
|
||||||
const response = PollResponseEvent.from([answerId], this.props.mxEvent.getId()!).serialize();
|
const response = PollResponseEvent.from([answerId], this.props.mxEvent.getId()!).serialize();
|
||||||
|
|
||||||
this.context.sendEvent(this.props.mxEvent.getRoomId()!, response.type, response.content).catch((e: any) => {
|
this.context
|
||||||
console.error("Failed to submit poll response event:", e);
|
.sendEvent(
|
||||||
|
this.props.mxEvent.getRoomId()!,
|
||||||
|
response.type as keyof TimelineEvents,
|
||||||
|
response.content as TimelineEvents[keyof TimelineEvents],
|
||||||
|
)
|
||||||
|
.catch((e: any) => {
|
||||||
|
console.error("Failed to submit poll response event:", e);
|
||||||
|
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
title: _t("poll|error_voting_title"),
|
title: _t("poll|error_voting_title"),
|
||||||
description: _t("poll|error_voting_description"),
|
description: _t("poll|error_voting_description"),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
this.setState({ selected: answerId });
|
this.setState({ selected: answerId });
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { EventType, MatrixEvent, RelationType } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { mediaFromMxc } from "../../../customisations/Media";
|
import { mediaFromMxc } from "../../../customisations/Media";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
|
@ -26,6 +26,7 @@ import ReactionsRowButtonTooltip from "./ReactionsRowButtonTooltip";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { REACTION_SHORTCODE_KEY } from "./ReactionsRow";
|
import { REACTION_SHORTCODE_KEY } from "./ReactionsRow";
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
// The event we're displaying reactions for
|
// The event we're displaying reactions for
|
||||||
mxEvent: MatrixEvent;
|
mxEvent: MatrixEvent;
|
||||||
|
@ -62,10 +63,10 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
|
||||||
if (myReactionEvent) {
|
if (myReactionEvent) {
|
||||||
this.context.redactEvent(mxEvent.getRoomId()!, myReactionEvent.getId()!);
|
this.context.redactEvent(mxEvent.getRoomId()!, myReactionEvent.getId()!);
|
||||||
} else {
|
} else {
|
||||||
this.context.sendEvent(mxEvent.getRoomId()!, "m.reaction", {
|
this.context.sendEvent(mxEvent.getRoomId()!, EventType.Reaction, {
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
rel_type: "m.annotation",
|
rel_type: RelationType.Annotation,
|
||||||
event_id: mxEvent.getId(),
|
event_id: mxEvent.getId()!,
|
||||||
key: content,
|
key: content,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,9 +16,10 @@ limitations under the License.
|
||||||
|
|
||||||
import React, { createRef, KeyboardEvent } from "react";
|
import React, { createRef, KeyboardEvent } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { EventStatus, IContent, MatrixEvent, Room, MsgType } from "matrix-js-sdk/src/matrix";
|
import { EventStatus, MatrixEvent, Room, MsgType } from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
|
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
|
||||||
|
import { ReplacementEvent, RoomMessageEventContent, RoomMessageTextEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
|
@ -70,7 +71,11 @@ function getTextReplyFallback(mxEvent: MatrixEvent): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// exported for tests
|
// exported for tests
|
||||||
export function createEditContent(model: EditorModel, editedEvent: MatrixEvent, replyToEvent?: MatrixEvent): IContent {
|
export function createEditContent(
|
||||||
|
model: EditorModel,
|
||||||
|
editedEvent: MatrixEvent,
|
||||||
|
replyToEvent?: MatrixEvent,
|
||||||
|
): RoomMessageEventContent {
|
||||||
const isEmote = containsEmote(model);
|
const isEmote = containsEmote(model);
|
||||||
if (isEmote) {
|
if (isEmote) {
|
||||||
model = stripEmoteCommand(model);
|
model = stripEmoteCommand(model);
|
||||||
|
@ -86,11 +91,11 @@ export function createEditContent(model: EditorModel, editedEvent: MatrixEvent,
|
||||||
|
|
||||||
const body = textSerialize(model);
|
const body = textSerialize(model);
|
||||||
|
|
||||||
const newContent: IContent = {
|
const newContent: RoomMessageEventContent = {
|
||||||
msgtype: isEmote ? MsgType.Emote : MsgType.Text,
|
msgtype: isEmote ? MsgType.Emote : MsgType.Text,
|
||||||
body: body,
|
body: body,
|
||||||
};
|
};
|
||||||
const contentBody: IContent = {
|
const contentBody: RoomMessageTextEventContent & Omit<ReplacementEvent<RoomMessageEventContent>, "m.relates_to"> = {
|
||||||
"msgtype": newContent.msgtype,
|
"msgtype": newContent.msgtype,
|
||||||
"body": `${plainPrefix} * ${body}`,
|
"body": `${plainPrefix} * ${body}`,
|
||||||
"m.new_content": newContent,
|
"m.new_content": newContent,
|
||||||
|
@ -111,7 +116,7 @@ export function createEditContent(model: EditorModel, editedEvent: MatrixEvent,
|
||||||
attachMentions(editedEvent.sender!.userId, contentBody, model, replyToEvent, editedEvent.getContent());
|
attachMentions(editedEvent.sender!.userId, contentBody, model, replyToEvent, editedEvent.getContent());
|
||||||
attachRelation(contentBody, { rel_type: "m.replace", event_id: editedEvent.getId() });
|
attachRelation(contentBody, { rel_type: "m.replace", event_id: editedEvent.getId() });
|
||||||
|
|
||||||
return contentBody;
|
return contentBody as RoomMessageEventContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IEditMessageComposerProps extends MatrixClientProps {
|
interface IEditMessageComposerProps extends MatrixClientProps {
|
||||||
|
@ -142,7 +147,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
|
|
||||||
const editContent = createEditContent(this.model, ev, this.replyToEvent);
|
const editContent = createEditContent(this.model, ev, this.replyToEvent);
|
||||||
this.state = {
|
this.state = {
|
||||||
saveDisabled: !isRestored || !this.isContentModified(editContent["m.new_content"]),
|
saveDisabled: !isRestored || !this.isContentModified(editContent["m.new_content"]!),
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
||||||
|
@ -284,14 +289,16 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
localStorage.setItem(this.editorStateKey, JSON.stringify(item));
|
localStorage.setItem(this.editorStateKey, JSON.stringify(item));
|
||||||
};
|
};
|
||||||
|
|
||||||
private isContentModified(newContent: IContent): boolean {
|
private isContentModified(newContent: RoomMessageEventContent): boolean {
|
||||||
// if nothing has changed then bail
|
// if nothing has changed then bail
|
||||||
const oldContent = this.props.editState.getEvent().getContent();
|
const oldContent = this.props.editState.getEvent().getContent<RoomMessageEventContent>();
|
||||||
if (
|
if (
|
||||||
oldContent["msgtype"] === newContent["msgtype"] &&
|
oldContent["msgtype"] === newContent["msgtype"] &&
|
||||||
oldContent["body"] === newContent["body"] &&
|
oldContent["body"] === newContent["body"] &&
|
||||||
oldContent["format"] === newContent["format"] &&
|
(oldContent as RoomMessageTextEventContent)["format"] ===
|
||||||
oldContent["formatted_body"] === newContent["formatted_body"]
|
(newContent as RoomMessageTextEventContent)["format"] &&
|
||||||
|
(oldContent as RoomMessageTextEventContent)["formatted_body"] ===
|
||||||
|
(newContent as RoomMessageTextEventContent)["formatted_body"]
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -318,7 +325,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
this.editorRef.current.replaceEmoticon(position, REGEX_EMOTICON);
|
this.editorRef.current.replaceEmoticon(position, REGEX_EMOTICON);
|
||||||
}
|
}
|
||||||
const editContent = createEditContent(this.model, editedEvent, this.replyToEvent);
|
const editContent = createEditContent(this.model, editedEvent, this.replyToEvent);
|
||||||
const newContent = editContent["m.new_content"];
|
const newContent = editContent["m.new_content"]!;
|
||||||
|
|
||||||
let shouldSend = true;
|
let shouldSend = true;
|
||||||
|
|
||||||
|
@ -352,7 +359,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd.category === CommandCategories.messages || cmd.category === CommandCategories.effects) {
|
if (cmd.category === CommandCategories.messages || cmd.category === CommandCategories.effects) {
|
||||||
editContent["m.new_content"] = content;
|
editContent["m.new_content"] = content!;
|
||||||
} else {
|
} else {
|
||||||
shouldSend = false;
|
shouldSend = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {
|
||||||
import { DebouncedFunc, throttle } from "lodash";
|
import { DebouncedFunc, throttle } from "lodash";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
|
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
|
||||||
|
import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import EditorModel from "../../../editor/model";
|
import EditorModel from "../../../editor/model";
|
||||||
|
@ -183,7 +184,7 @@ export function createMessageContent(
|
||||||
relation: IEventRelation | undefined,
|
relation: IEventRelation | undefined,
|
||||||
permalinkCreator?: RoomPermalinkCreator,
|
permalinkCreator?: RoomPermalinkCreator,
|
||||||
includeReplyLegacyFallback = true,
|
includeReplyLegacyFallback = true,
|
||||||
): IContent {
|
): RoomMessageEventContent {
|
||||||
const isEmote = containsEmote(model);
|
const isEmote = containsEmote(model);
|
||||||
if (isEmote) {
|
if (isEmote) {
|
||||||
model = stripEmoteCommand(model);
|
model = stripEmoteCommand(model);
|
||||||
|
@ -195,7 +196,7 @@ export function createMessageContent(
|
||||||
|
|
||||||
const body = textSerialize(model);
|
const body = textSerialize(model);
|
||||||
|
|
||||||
const content: IContent = {
|
const content: RoomMessageEventContent = {
|
||||||
msgtype: isEmote ? MsgType.Emote : MsgType.Text,
|
msgtype: isEmote ? MsgType.Emote : MsgType.Text,
|
||||||
body: body,
|
body: body,
|
||||||
};
|
};
|
||||||
|
@ -432,7 +433,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
MatrixClientPeg.safeGet().sendEvent(lastMessage.getRoomId()!, EventType.Reaction, {
|
MatrixClientPeg.safeGet().sendEvent(lastMessage.getRoomId()!, EventType.Reaction, {
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
rel_type: RelationType.Annotation,
|
rel_type: RelationType.Annotation,
|
||||||
event_id: lastMessage.getId(),
|
event_id: lastMessage.getId()!,
|
||||||
key: reaction,
|
key: reaction,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -475,7 +476,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
|
|
||||||
const replyToEvent = this.props.replyToEvent;
|
const replyToEvent = this.props.replyToEvent;
|
||||||
let shouldSend = true;
|
let shouldSend = true;
|
||||||
let content: IContent | null = null;
|
let content: RoomMessageEventContent | null = null;
|
||||||
|
|
||||||
if (!containsEmote(model) && isSlashCommand(this.model)) {
|
if (!containsEmote(model) && isSlashCommand(this.model)) {
|
||||||
const [cmd, args, commandText] = getSlashCommand(this.model);
|
const [cmd, args, commandText] = getSlashCommand(this.model);
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import { richToPlain, plainToRich } from "@matrix-org/matrix-wysiwyg";
|
import { richToPlain, plainToRich } from "@matrix-org/matrix-wysiwyg";
|
||||||
import { IContent, IEventRelation, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix";
|
import { IContent, IEventRelation, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { ReplacementEvent, RoomMessageEventContent, RoomMessageTextEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||||
import { parsePermalink, RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
|
import { parsePermalink, RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
|
||||||
|
@ -76,7 +77,7 @@ export async function createMessageContent(
|
||||||
includeReplyLegacyFallback = true,
|
includeReplyLegacyFallback = true,
|
||||||
editedEvent,
|
editedEvent,
|
||||||
}: CreateMessageContentParams,
|
}: CreateMessageContentParams,
|
||||||
): Promise<IContent> {
|
): Promise<RoomMessageEventContent> {
|
||||||
const isEditing = isMatrixEvent(editedEvent);
|
const isEditing = isMatrixEvent(editedEvent);
|
||||||
const isReply = isEditing ? Boolean(editedEvent.replyEventId) : isMatrixEvent(replyToEvent);
|
const isReply = isEditing ? Boolean(editedEvent.replyEventId) : isMatrixEvent(replyToEvent);
|
||||||
const isReplyAndEditing = isEditing && isReply;
|
const isReplyAndEditing = isEditing && isReply;
|
||||||
|
@ -100,10 +101,10 @@ export async function createMessageContent(
|
||||||
const bodyPrefix = (isReplyAndEditing && getTextReplyFallback(editedEvent)) || "";
|
const bodyPrefix = (isReplyAndEditing && getTextReplyFallback(editedEvent)) || "";
|
||||||
const formattedBodyPrefix = (isReplyAndEditing && getHtmlReplyFallback(editedEvent)) || "";
|
const formattedBodyPrefix = (isReplyAndEditing && getHtmlReplyFallback(editedEvent)) || "";
|
||||||
|
|
||||||
const content: IContent = {
|
const content = {
|
||||||
msgtype: isEmote ? MsgType.Emote : MsgType.Text,
|
msgtype: isEmote ? MsgType.Emote : MsgType.Text,
|
||||||
body: isEditing ? `${bodyPrefix} * ${body}` : body,
|
body: isEditing ? `${bodyPrefix} * ${body}` : body,
|
||||||
};
|
} as RoomMessageTextEventContent & ReplacementEvent<RoomMessageTextEventContent>;
|
||||||
|
|
||||||
// TODO markdown support
|
// TODO markdown support
|
||||||
|
|
||||||
|
|
|
@ -14,18 +14,22 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IContent } from "matrix-js-sdk/src/matrix";
|
import { RoomMessageEventContent, RoomMessageTextEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
|
import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
|
||||||
|
|
||||||
export function isContentModified(newContent: IContent, editorStateTransfer: EditorStateTransfer): boolean {
|
export function isContentModified(
|
||||||
|
newContent: RoomMessageEventContent,
|
||||||
|
editorStateTransfer: EditorStateTransfer,
|
||||||
|
): boolean {
|
||||||
// if nothing has changed then bail
|
// if nothing has changed then bail
|
||||||
const oldContent = editorStateTransfer.getEvent().getContent();
|
const oldContent = editorStateTransfer.getEvent().getContent<RoomMessageEventContent>();
|
||||||
if (
|
if (
|
||||||
oldContent["msgtype"] === newContent["msgtype"] &&
|
oldContent["msgtype"] === newContent["msgtype"] &&
|
||||||
oldContent["body"] === newContent["body"] &&
|
oldContent["body"] === newContent["body"] &&
|
||||||
oldContent["format"] === newContent["format"] &&
|
(<RoomMessageTextEventContent>oldContent)["format"] === (<RoomMessageTextEventContent>newContent)["format"] &&
|
||||||
oldContent["formatted_body"] === newContent["formatted_body"]
|
(<RoomMessageTextEventContent>oldContent)["formatted_body"] ===
|
||||||
|
(<RoomMessageTextEventContent>newContent)["formatted_body"]
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,13 @@ limitations under the License.
|
||||||
|
|
||||||
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
|
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
|
||||||
import {
|
import {
|
||||||
IContent,
|
|
||||||
IEventRelation,
|
IEventRelation,
|
||||||
MatrixEvent,
|
MatrixEvent,
|
||||||
ISendEventResponse,
|
ISendEventResponse,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
THREAD_RELATION_TYPE,
|
THREAD_RELATION_TYPE,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
|
import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import { PosthogAnalytics } from "../../../../../PosthogAnalytics";
|
import { PosthogAnalytics } from "../../../../../PosthogAnalytics";
|
||||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||||
|
@ -82,7 +82,7 @@ export async function sendMessage(
|
||||||
}*/
|
}*/
|
||||||
PosthogAnalytics.instance.trackEvent<ComposerEvent>(posthogEvent);
|
PosthogAnalytics.instance.trackEvent<ComposerEvent>(posthogEvent);
|
||||||
|
|
||||||
let content: IContent | null = null;
|
let content: RoomMessageEventContent | null = null;
|
||||||
|
|
||||||
// Slash command handling here approximates what can be found in SendMessageComposer.sendMessage()
|
// Slash command handling here approximates what can be found in SendMessageComposer.sendMessage()
|
||||||
// but note that the /me and // special cases are handled by the call to createMessageContent
|
// but note that the /me and // special cases are handled by the call to createMessageContent
|
||||||
|
@ -145,7 +145,7 @@ export async function sendMessage(
|
||||||
|
|
||||||
const prom = doMaybeLocalRoomAction(
|
const prom = doMaybeLocalRoomAction(
|
||||||
roomId,
|
roomId,
|
||||||
(actualRoomId: string) => mxClient.sendMessage(actualRoomId, threadId, content as IContent),
|
(actualRoomId: string) => mxClient.sendMessage(actualRoomId, threadId, content!),
|
||||||
mxClient,
|
mxClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ export async function editMessage(
|
||||||
this.editorRef.current?.replaceEmoticon(position, REGEX_EMOTICON);
|
this.editorRef.current?.replaceEmoticon(position, REGEX_EMOTICON);
|
||||||
}*/
|
}*/
|
||||||
const editContent = await createMessageContent(html, true, { editedEvent });
|
const editContent = await createMessageContent(html, true, { editedEvent });
|
||||||
const newContent = editContent["m.new_content"];
|
const newContent = editContent["m.new_content"]!;
|
||||||
|
|
||||||
const shouldSend = true;
|
const shouldSend = true;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { IContent, MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import EditorModel from "./model";
|
import EditorModel from "./model";
|
||||||
import { Type } from "./parts";
|
import { Type } from "./parts";
|
||||||
|
@ -63,9 +64,9 @@ export async function runSlashCommand(
|
||||||
args: string | undefined,
|
args: string | undefined,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
threadId: string | null,
|
threadId: string | null,
|
||||||
): Promise<[content: IContent | null, success: boolean]> {
|
): Promise<[content: RoomMessageEventContent | null, success: boolean]> {
|
||||||
const result = cmd.run(matrixClient, roomId, threadId, args);
|
const result = cmd.run(matrixClient, roomId, threadId, args);
|
||||||
let messageContent: IContent | null = null;
|
let messageContent: RoomMessageEventContent | null = null;
|
||||||
let error: any = result.error;
|
let error: any = result.error;
|
||||||
if (result.promise) {
|
if (result.promise) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IContent } from "matrix-js-sdk/src/matrix";
|
import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import { _td } from "../languageHandler";
|
import { _td } from "../languageHandler";
|
||||||
import { XOR } from "../@types/common";
|
import { XOR } from "../@types/common";
|
||||||
|
@ -31,4 +31,4 @@ export const CommandCategories = {
|
||||||
other: _td("slash_command|category_other"),
|
other: _td("slash_command|category_other"),
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunResult = XOR<{ error: Error }, { promise: Promise<IContent | undefined> }>;
|
export type RunResult = XOR<{ error: Error }, { promise: Promise<RoomMessageEventContent | undefined> }>;
|
||||||
|
|
|
@ -44,6 +44,7 @@ import {
|
||||||
Direction,
|
Direction,
|
||||||
THREAD_RELATION_TYPE,
|
THREAD_RELATION_TYPE,
|
||||||
StateEvents,
|
StateEvents,
|
||||||
|
TimelineEvents,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import {
|
import {
|
||||||
|
@ -248,13 +249,13 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||||
stateKey?: string,
|
stateKey?: string,
|
||||||
targetRoomId?: string,
|
targetRoomId?: string,
|
||||||
): Promise<ISendEventDetails>;
|
): Promise<ISendEventDetails>;
|
||||||
public async sendEvent(
|
public async sendEvent<K extends keyof TimelineEvents>(
|
||||||
eventType: Exclude<EventType, keyof StateEvents>,
|
eventType: K,
|
||||||
content: IContent,
|
content: TimelineEvents[K],
|
||||||
stateKey: null,
|
stateKey: null,
|
||||||
targetRoomId?: string,
|
targetRoomId?: string,
|
||||||
): Promise<ISendEventDetails>;
|
): Promise<ISendEventDetails>;
|
||||||
public async sendEvent<K extends keyof StateEvents>(
|
public async sendEvent(
|
||||||
eventType: string,
|
eventType: string,
|
||||||
content: IContent,
|
content: IContent,
|
||||||
stateKey?: string | null,
|
stateKey?: string | null,
|
||||||
|
@ -268,13 +269,22 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||||
let r: { event_id: string } | null;
|
let r: { event_id: string } | null;
|
||||||
if (stateKey !== null) {
|
if (stateKey !== null) {
|
||||||
// state event
|
// state event
|
||||||
r = await client.sendStateEvent(roomId, eventType as K, content as StateEvents[K], stateKey);
|
r = await client.sendStateEvent(
|
||||||
|
roomId,
|
||||||
|
eventType as keyof StateEvents,
|
||||||
|
content as StateEvents[keyof StateEvents],
|
||||||
|
stateKey,
|
||||||
|
);
|
||||||
} else if (eventType === EventType.RoomRedaction) {
|
} else if (eventType === EventType.RoomRedaction) {
|
||||||
// special case: extract the `redacts` property and call redact
|
// special case: extract the `redacts` property and call redact
|
||||||
r = await client.redactEvent(roomId, content["redacts"]);
|
r = await client.redactEvent(roomId, content["redacts"]);
|
||||||
} else {
|
} else {
|
||||||
// message event
|
// message event
|
||||||
r = await client.sendEvent(roomId, eventType, content);
|
r = await client.sendEvent(
|
||||||
|
roomId,
|
||||||
|
eventType as keyof TimelineEvents,
|
||||||
|
content as TimelineEvents[keyof TimelineEvents],
|
||||||
|
);
|
||||||
|
|
||||||
if (eventType === EventType.RoomMessage) {
|
if (eventType === EventType.RoomMessage) {
|
||||||
CHAT_EFFECTS.forEach((effect) => {
|
CHAT_EFFECTS.forEach((effect) => {
|
||||||
|
|
|
@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IContent, IEncryptedFile, MsgType } from "matrix-js-sdk/src/matrix";
|
import { IEncryptedFile, MsgType } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} mxc MXC URL of the file
|
* @param {string} mxc MXC URL of the file
|
||||||
|
@ -31,7 +32,7 @@ export const createVoiceMessageContent = (
|
||||||
size: number,
|
size: number,
|
||||||
file?: IEncryptedFile,
|
file?: IEncryptedFile,
|
||||||
waveform?: number[],
|
waveform?: number[],
|
||||||
): IContent => {
|
): RoomMessageEventContent => {
|
||||||
return {
|
return {
|
||||||
"body": "Voice message",
|
"body": "Voice message",
|
||||||
//"msgtype": "org.matrix.msc2516.voice",
|
//"msgtype": "org.matrix.msc2516.voice",
|
||||||
|
|
|
@ -26,7 +26,7 @@ import {
|
||||||
RelationType,
|
RelationType,
|
||||||
TypedEventEmitter,
|
TypedEventEmitter,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { EncryptedFile } from "matrix-js-sdk/src/types";
|
import { AudioContent, EncryptedFile } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChunkRecordedPayload,
|
ChunkRecordedPayload,
|
||||||
|
@ -387,7 +387,7 @@ export class VoiceBroadcastRecording
|
||||||
rel_type: RelationType.Reference,
|
rel_type: RelationType.Reference,
|
||||||
event_id: this.infoEventId,
|
event_id: this.infoEventId,
|
||||||
};
|
};
|
||||||
content["io.element.voice_broadcast_chunk"] = {
|
(<AudioContent>content)["io.element.voice_broadcast_chunk"] = {
|
||||||
sequence,
|
sequence,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
M_TEXT,
|
M_TEXT,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
||||||
|
import { ReplacementEvent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import { getMockClientWithEventEmitter } from "../../../test-utils";
|
import { getMockClientWithEventEmitter } from "../../../test-utils";
|
||||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
|
@ -273,7 +274,9 @@ describe("PollCreateDialog", () => {
|
||||||
const [, , eventType, sentEventContent] = mockClient.sendEvent.mock.calls[0];
|
const [, , eventType, sentEventContent] = mockClient.sendEvent.mock.calls[0];
|
||||||
expect(M_POLL_START.matches(eventType)).toBeTruthy();
|
expect(M_POLL_START.matches(eventType)).toBeTruthy();
|
||||||
// didnt change
|
// didnt change
|
||||||
expect(sentEventContent["m.new_content"][M_POLL_START.name].kind).toEqual(M_POLL_KIND_DISCLOSED.name);
|
expect((sentEventContent as ReplacementEvent<any>)["m.new_content"][M_POLL_START.name].kind).toEqual(
|
||||||
|
M_POLL_KIND_DISCLOSED.name,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import React from "react";
|
||||||
import { fireEvent, render, screen } from "@testing-library/react";
|
import { fireEvent, render, screen } from "@testing-library/react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { Room } from "matrix-js-sdk/src/matrix";
|
import { Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { ReplacementEvent, RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import EditMessageComposerWithMatrixClient, {
|
import EditMessageComposerWithMatrixClient, {
|
||||||
createEditContent,
|
createEditContent,
|
||||||
|
@ -296,11 +297,12 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// both content.mentions and new_content.mentions are empty
|
// both content.mentions and new_content.mentions are empty
|
||||||
expect(messageContent["m.mentions"]).toEqual({});
|
expect(messageContent["m.mentions"]).toEqual({});
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({});
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should retain mentions in the original message that are not removed by the edit", async () => {
|
it("should retain mentions in the original message that are not removed by the edit", async () => {
|
||||||
|
@ -312,12 +314,13 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// no new mentions were added, so nothing in top level mentions
|
// no new mentions were added, so nothing in top level mentions
|
||||||
expect(messageContent["m.mentions"]).toEqual({});
|
expect(messageContent["m.mentions"]).toEqual({});
|
||||||
// bob is still mentioned, charlie removed
|
// bob is still mentioned, charlie removed
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({
|
||||||
user_ids: ["@bob:server.org"],
|
user_ids: ["@bob:server.org"],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -331,12 +334,13 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// no new mentions were added, so nothing in top level mentions
|
// no new mentions were added, so nothing in top level mentions
|
||||||
expect(messageContent["m.mentions"]).toEqual({});
|
expect(messageContent["m.mentions"]).toEqual({});
|
||||||
// bob is not longer mentioned in the edited message, so empty mentions in new_content
|
// bob is not longer mentioned in the edited message, so empty mentions in new_content
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({});
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add mentions that were added in the edit", async () => {
|
it("should add mentions that were added in the edit", async () => {
|
||||||
|
@ -352,13 +356,14 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// new mention in the edit
|
// new mention in the edit
|
||||||
expect(messageContent["m.mentions"]).toEqual({
|
expect(messageContent["m.mentions"]).toEqual({
|
||||||
user_ids: ["@dan:server.org"],
|
user_ids: ["@dan:server.org"],
|
||||||
});
|
});
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({
|
||||||
user_ids: ["@dan:server.org"],
|
user_ids: ["@dan:server.org"],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -377,14 +382,15 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// new mention in the edit
|
// new mention in the edit
|
||||||
expect(messageContent["m.mentions"]).toEqual({
|
expect(messageContent["m.mentions"]).toEqual({
|
||||||
user_ids: ["@dan:server.org"],
|
user_ids: ["@dan:server.org"],
|
||||||
});
|
});
|
||||||
// all mentions in the edited version of the event
|
// all mentions in the edited version of the event
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({
|
||||||
user_ids: ["@bob:server.org", "@dan:server.org"],
|
user_ids: ["@bob:server.org", "@dan:server.org"],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -454,12 +460,13 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// no new mentions from edit
|
// no new mentions from edit
|
||||||
expect(messageContent["m.mentions"]).toEqual({});
|
expect(messageContent["m.mentions"]).toEqual({});
|
||||||
// edited reply still mentions the parent event sender
|
// edited reply still mentions the parent event sender
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({
|
||||||
user_ids: [originalEvent.getSender()],
|
user_ids: [originalEvent.getSender()],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -475,7 +482,8 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// new mention in edit
|
// new mention in edit
|
||||||
expect(messageContent["m.mentions"]).toEqual({
|
expect(messageContent["m.mentions"]).toEqual({
|
||||||
|
@ -483,7 +491,7 @@ describe("<EditMessageComposer/>", () => {
|
||||||
});
|
});
|
||||||
// edited reply still mentions the parent event sender
|
// edited reply still mentions the parent event sender
|
||||||
// plus new mention @dan
|
// plus new mention @dan
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({
|
||||||
user_ids: [originalEvent.getSender(), "@dan:server.org"],
|
user_ids: [originalEvent.getSender(), "@dan:server.org"],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -496,13 +504,14 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// no mentions in edit
|
// no mentions in edit
|
||||||
expect(messageContent["m.mentions"]).toEqual({});
|
expect(messageContent["m.mentions"]).toEqual({});
|
||||||
// edited reply still mentions the parent event sender
|
// edited reply still mentions the parent event sender
|
||||||
// existing @bob mention removed
|
// existing @bob mention removed
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({
|
||||||
user_ids: [originalEvent.getSender()],
|
user_ids: [originalEvent.getSender()],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -536,12 +545,13 @@ describe("<EditMessageComposer/>", () => {
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Save"));
|
fireEvent.click(screen.getByText("Save"));
|
||||||
|
|
||||||
const messageContent = mockClient.sendMessage.mock.calls[0][2];
|
const messageContent = mockClient.sendMessage.mock.calls[0][2] as RoomMessageEventContent &
|
||||||
|
ReplacementEvent<RoomMessageEventContent>;
|
||||||
|
|
||||||
// no mentions in edit
|
// no mentions in edit
|
||||||
expect(messageContent["m.mentions"]).toEqual({});
|
expect(messageContent["m.mentions"]).toEqual({});
|
||||||
// edited reply still mentions the parent event sender
|
// edited reply still mentions the parent event sender
|
||||||
expect(messageContent["m.new_content"]["m.mentions"]).toEqual({
|
expect(messageContent["m.new_content"]!["m.mentions"]).toEqual({
|
||||||
user_ids: [originalEvent.getSender()],
|
user_ids: [originalEvent.getSender()],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventStatus, IEventRelation } from "matrix-js-sdk/src/matrix";
|
import { EventStatus, IEventRelation, MsgType } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { IRoomState } from "../../../../../../src/components/structures/RoomView";
|
import { IRoomState } from "../../../../../../src/components/structures/RoomView";
|
||||||
import { editMessage, sendMessage } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/message";
|
import { editMessage, sendMessage } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/message";
|
||||||
|
@ -272,7 +272,10 @@ describe("message", () => {
|
||||||
it("returns undefined when the command is not successful", async () => {
|
it("returns undefined when the command is not successful", async () => {
|
||||||
// When
|
// When
|
||||||
const validCommand = "/spoiler";
|
const validCommand = "/spoiler";
|
||||||
jest.spyOn(Commands, "runSlashCommand").mockResolvedValueOnce([{ content: "mock content" }, false]);
|
jest.spyOn(Commands, "runSlashCommand").mockResolvedValueOnce([
|
||||||
|
{ body: "mock content", msgtype: MsgType.Text },
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
|
||||||
const result = await sendMessage(validCommand, true, {
|
const result = await sendMessage(validCommand, true, {
|
||||||
roomContext: defaultRoomContext,
|
roomContext: defaultRoomContext,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue