Remove reply fallbacks as per merged MSC2781 (#28406)
* Remove reply fallbacks as per merged MSC2781 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
73db771ff3
commit
349c9b0c26
14 changed files with 75 additions and 462 deletions
|
@ -536,9 +536,7 @@ export default class ContentMessages {
|
|||
attachMentions(matrixClient.getSafeUserId(), content, null, replyToEvent);
|
||||
attachRelation(content, relation);
|
||||
if (replyToEvent) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
includeLegacyFallback: false,
|
||||
});
|
||||
addReplyToMessageContent(content, replyToEvent);
|
||||
}
|
||||
|
||||
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
||||
|
|
|
@ -414,7 +414,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
|||
this.messageComposerInput.current?.sendMessage();
|
||||
|
||||
if (this.state.isWysiwygLabEnabled) {
|
||||
const { permalinkCreator, relation, replyToEvent } = this.props;
|
||||
const { relation, replyToEvent } = this.props;
|
||||
const composerContent = this.state.composerContent;
|
||||
this.setState({ composerContent: "", initialComposerContent: "" });
|
||||
dis.dispatch({
|
||||
|
@ -424,7 +424,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
|||
await sendMessage(composerContent, this.state.isRichTextEnabled, {
|
||||
mxClient: this.props.mxClient,
|
||||
roomContext: this.context,
|
||||
permalinkCreator,
|
||||
relation,
|
||||
replyToEvent,
|
||||
});
|
||||
|
@ -582,7 +581,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
|||
key="controls_input"
|
||||
room={this.props.room}
|
||||
placeholder={this.renderPlaceholderText()}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
relation={this.props.relation}
|
||||
replyToEvent={this.props.replyToEvent}
|
||||
onChange={this.onChange}
|
||||
|
@ -597,7 +595,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
|||
key="controls_voice_record"
|
||||
ref={this.voiceRecordingButton}
|
||||
room={this.props.room}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
relation={this.props.relation}
|
||||
replyToEvent={this.props.replyToEvent}
|
||||
/>,
|
||||
|
@ -642,8 +639,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
let recordingTooltip: JSX.Element | undefined;
|
||||
|
||||
const isTooltipOpen = Boolean(this.state.recordingTimeLeftSeconds);
|
||||
const secondsLeft = this.state.recordingTimeLeftSeconds ? Math.round(this.state.recordingTimeLeftSeconds) : 0;
|
||||
|
||||
|
@ -673,7 +668,6 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
|||
return (
|
||||
<Tooltip open={isTooltipOpen} description={formatTimeLeft(secondsLeft)} placement="bottom">
|
||||
<div className={classes} ref={this.ref} role="region" aria-label={_t("a11y|message_composer")}>
|
||||
{recordingTooltip}
|
||||
<div className="mx_MessageComposer_wrapper">
|
||||
<ReplyPreview
|
||||
replyToEvent={this.props.replyToEvent}
|
||||
|
|
|
@ -47,7 +47,6 @@ import { CHAT_EFFECTS } from "../../../effects";
|
|||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
import { decorateStartSendingTime, sendRoundTripMetric } from "../../../sendTimePerformanceMetrics";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
|
||||
|
@ -177,8 +176,6 @@ export function createMessageContent(
|
|||
model: EditorModel,
|
||||
replyToEvent: MatrixEvent | undefined,
|
||||
relation: IEventRelation | undefined,
|
||||
permalinkCreator?: RoomPermalinkCreator,
|
||||
includeReplyLegacyFallback = true,
|
||||
): RoomMessageEventContent {
|
||||
const isEmote = containsEmote(model);
|
||||
if (isEmote) {
|
||||
|
@ -209,10 +206,7 @@ export function createMessageContent(
|
|||
|
||||
attachRelation(content, relation);
|
||||
if (replyToEvent) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
permalinkCreator,
|
||||
includeLegacyFallback: includeReplyLegacyFallback,
|
||||
});
|
||||
addReplyToMessageContent(content, replyToEvent);
|
||||
}
|
||||
|
||||
return content;
|
||||
|
@ -238,12 +232,10 @@ export function isQuickReaction(model: EditorModel): boolean {
|
|||
interface ISendMessageComposerProps extends MatrixClientProps {
|
||||
room: Room;
|
||||
placeholder?: string;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
disabled?: boolean;
|
||||
onChange?(model: EditorModel): void;
|
||||
includeReplyLegacyFallback?: boolean;
|
||||
toggleStickerPickerOpen: () => void;
|
||||
}
|
||||
|
||||
|
@ -258,10 +250,6 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
|||
private dispatcherRef?: string;
|
||||
private sendHistoryManager: SendHistoryManager;
|
||||
|
||||
public static defaultProps = {
|
||||
includeReplyLegacyFallback: true,
|
||||
};
|
||||
|
||||
public constructor(props: ISendMessageComposerProps, context: React.ContextType<typeof RoomContext>) {
|
||||
super(props, context);
|
||||
|
||||
|
@ -500,11 +488,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
|||
attachMentions(this.props.mxClient.getSafeUserId(), content, model, replyToEvent);
|
||||
attachRelation(content, this.props.relation);
|
||||
if (replyToEvent) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
permalinkCreator: this.props.permalinkCreator,
|
||||
// Exclude the legacy fallback for custom event types such as those used by /fireworks
|
||||
includeLegacyFallback: content.msgtype?.startsWith("m.") ?? true,
|
||||
});
|
||||
addReplyToMessageContent(content, replyToEvent);
|
||||
}
|
||||
} else {
|
||||
shouldSend = false;
|
||||
|
@ -534,8 +518,6 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
|||
model,
|
||||
replyToEvent,
|
||||
this.props.relation,
|
||||
this.props.permalinkCreator,
|
||||
this.props.includeReplyLegacyFallback,
|
||||
);
|
||||
}
|
||||
// don't bother sending an empty message
|
||||
|
|
|
@ -31,7 +31,6 @@ import { doMaybeLocalRoomAction } from "../../../utils/local-room";
|
|||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import { attachMentions, attachRelation } from "./SendMessageComposer";
|
||||
import { addReplyToMessageContent } from "../../../utils/Reply";
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
import RoomContext from "../../../contexts/RoomContext";
|
||||
import { IUpload, VoiceMessageRecording } from "../../../audio/VoiceMessageRecording";
|
||||
import { createVoiceMessageContent } from "../../../utils/createVoiceMessageContent";
|
||||
|
@ -39,7 +38,6 @@ import AccessibleButton from "../elements/AccessibleButton";
|
|||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
}
|
||||
|
@ -93,7 +91,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
|||
throw new Error("No recording started - cannot send anything");
|
||||
}
|
||||
|
||||
const { replyToEvent, relation, permalinkCreator } = this.props;
|
||||
const { replyToEvent, relation } = this.props;
|
||||
|
||||
await this.state.recorder.stop();
|
||||
|
||||
|
@ -124,10 +122,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
|||
attachMentions(MatrixClientPeg.safeGet().getSafeUserId(), content, null, replyToEvent);
|
||||
attachRelation(content, relation);
|
||||
if (replyToEvent) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
permalinkCreator,
|
||||
includeLegacyFallback: true,
|
||||
});
|
||||
addReplyToMessageContent(content, replyToEvent);
|
||||
// Clear reply_to_event as we put the message into the queue
|
||||
// if the send fails, retry will handle resending.
|
||||
defaultDispatcher.dispatch({
|
||||
|
|
|
@ -11,7 +11,7 @@ import { IContent, IEventRelation, MatrixEvent, MsgType } from "matrix-js-sdk/sr
|
|||
import { ReplacementEvent, RoomMessageEventContent, RoomMessageTextEventContent } from "matrix-js-sdk/src/types";
|
||||
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { parsePermalink, RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
|
||||
import { parsePermalink } from "../../../../../utils/permalinks/Permalinks";
|
||||
import { addReplyToMessageContent } from "../../../../../utils/Reply";
|
||||
import { isNotNull } from "../../../../../Typeguards";
|
||||
|
||||
|
@ -52,8 +52,6 @@ function getTextReplyFallback(mxEvent: MatrixEvent): string {
|
|||
interface CreateMessageContentParams {
|
||||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
includeReplyLegacyFallback?: boolean;
|
||||
editedEvent?: MatrixEvent;
|
||||
}
|
||||
|
||||
|
@ -62,13 +60,7 @@ const isMatrixEvent = (e: MatrixEvent | undefined): e is MatrixEvent => e instan
|
|||
export async function createMessageContent(
|
||||
message: string,
|
||||
isHTML: boolean,
|
||||
{
|
||||
relation,
|
||||
replyToEvent,
|
||||
permalinkCreator,
|
||||
includeReplyLegacyFallback = true,
|
||||
editedEvent,
|
||||
}: CreateMessageContentParams,
|
||||
{ relation, replyToEvent, editedEvent }: CreateMessageContentParams,
|
||||
): Promise<RoomMessageEventContent> {
|
||||
const isEditing = isMatrixEvent(editedEvent);
|
||||
const isReply = isEditing ? Boolean(editedEvent.replyEventId) : isMatrixEvent(replyToEvent);
|
||||
|
@ -126,11 +118,8 @@ export async function createMessageContent(
|
|||
// TODO Handle editing?
|
||||
attachRelation(content, newRelation);
|
||||
|
||||
if (!isEditing && replyToEvent && permalinkCreator) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
permalinkCreator,
|
||||
includeLegacyFallback: includeReplyLegacyFallback,
|
||||
});
|
||||
if (!isEditing && replyToEvent) {
|
||||
addReplyToMessageContent(content, replyToEvent);
|
||||
}
|
||||
|
||||
return content;
|
||||
|
|
|
@ -19,7 +19,6 @@ import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
|||
import { PosthogAnalytics } from "../../../../../PosthogAnalytics";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { decorateStartSendingTime, sendRoundTripMetric } from "../../../../../sendTimePerformanceMetrics";
|
||||
import { RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
|
||||
import { doMaybeLocalRoomAction } from "../../../../../utils/local-room";
|
||||
import { CHAT_EFFECTS } from "../../../../../effects";
|
||||
import { containsEmoji } from "../../../../../effects/utils";
|
||||
|
@ -41,8 +40,6 @@ export interface SendMessageParams {
|
|||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
roomContext: IRoomState;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
includeReplyLegacyFallback?: boolean;
|
||||
}
|
||||
|
||||
export async function sendMessage(
|
||||
|
@ -50,7 +47,7 @@ export async function sendMessage(
|
|||
isHTML: boolean,
|
||||
{ roomContext, mxClient, ...params }: SendMessageParams,
|
||||
): Promise<ISendEventResponse | undefined> {
|
||||
const { relation, replyToEvent, permalinkCreator } = params;
|
||||
const { relation, replyToEvent } = params;
|
||||
const { room } = roomContext;
|
||||
const roomId = room?.roomId;
|
||||
|
||||
|
@ -95,11 +92,7 @@ export async function sendMessage(
|
|||
) {
|
||||
attachRelation(content, relation);
|
||||
if (replyToEvent) {
|
||||
addReplyToMessageContent(content, replyToEvent, {
|
||||
permalinkCreator,
|
||||
// Exclude the legacy fallback for custom event types such as those used by /fireworks
|
||||
includeLegacyFallback: content.msgtype?.startsWith("m.") ?? true,
|
||||
});
|
||||
addReplyToMessageContent(content, replyToEvent);
|
||||
}
|
||||
} else {
|
||||
// instead of setting shouldSend to false as in SendMessageComposer, just return
|
||||
|
|
|
@ -7,23 +7,10 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import {
|
||||
IContent,
|
||||
IEventRelation,
|
||||
MatrixEvent,
|
||||
MsgType,
|
||||
THREAD_RELATION_TYPE,
|
||||
M_BEACON_INFO,
|
||||
M_POLL_END,
|
||||
M_POLL_START,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { IContent, IEventRelation, MatrixEvent, THREAD_RELATION_TYPE } from "matrix-js-sdk/src/matrix";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import escapeHtml from "escape-html";
|
||||
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
||||
|
||||
import { PERMITTED_URL_SCHEMES } from "./UrlUtils";
|
||||
import { makeUserPermalink, RoomPermalinkCreator } from "./permalinks/Permalinks";
|
||||
import { isSelfLocation } from "./location";
|
||||
|
||||
export function getParentEventId(ev?: MatrixEvent): string | undefined {
|
||||
if (!ev || ev.isRedacted()) return;
|
||||
|
@ -62,137 +49,6 @@ export function stripHTMLReply(html: string): string {
|
|||
});
|
||||
}
|
||||
|
||||
// Part of Replies fallback support
|
||||
export function getNestedReplyText(
|
||||
ev: MatrixEvent,
|
||||
permalinkCreator?: RoomPermalinkCreator,
|
||||
): { body: string; html: string } | null {
|
||||
if (!ev) return null;
|
||||
|
||||
let {
|
||||
body,
|
||||
formatted_body: html,
|
||||
msgtype,
|
||||
} = ev.getContent<{
|
||||
body: string;
|
||||
msgtype?: string;
|
||||
formatted_body?: string;
|
||||
}>();
|
||||
if (getParentEventId(ev)) {
|
||||
if (body) body = stripPlainReply(body);
|
||||
}
|
||||
|
||||
if (!body) body = ""; // Always ensure we have a body, for reasons.
|
||||
|
||||
if (html) {
|
||||
// sanitize the HTML before we put it in an <mx-reply>
|
||||
html = stripHTMLReply(html);
|
||||
} else {
|
||||
// Escape the body to use as HTML below.
|
||||
// We also run a nl2br over the result to fix the fallback representation. We do this
|
||||
// after converting the text to safe HTML to avoid user-provided BR's from being converted.
|
||||
html = escapeHtml(body).replace(/\n/g, "<br/>");
|
||||
}
|
||||
|
||||
// dev note: do not rely on `body` being safe for HTML usage below.
|
||||
|
||||
const evLink = permalinkCreator?.forEvent(ev.getId()!);
|
||||
const userLink = makeUserPermalink(ev.getSender()!);
|
||||
const mxid = ev.getSender();
|
||||
|
||||
if (M_BEACON_INFO.matches(ev.getType())) {
|
||||
const aTheir = isSelfLocation(ev.getContent()) ? "their" : "a";
|
||||
return {
|
||||
html:
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>shared ${aTheir} live location.</blockquote></mx-reply>`,
|
||||
body: `> <${mxid}> shared ${aTheir} live location.\n\n`,
|
||||
};
|
||||
}
|
||||
|
||||
if (M_POLL_START.matches(ev.getType())) {
|
||||
const extensibleEvent = ev.unstableExtensibleEvent as PollStartEvent;
|
||||
const question = extensibleEvent?.question?.text;
|
||||
return {
|
||||
html:
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>Poll: ${question}</blockquote></mx-reply>`,
|
||||
body: `> <${mxid}> started poll: ${question}\n\n`,
|
||||
};
|
||||
}
|
||||
if (M_POLL_END.matches(ev.getType())) {
|
||||
return {
|
||||
html:
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>Ended poll</blockquote></mx-reply>`,
|
||||
body: `> <${mxid}>Ended poll\n\n`,
|
||||
};
|
||||
}
|
||||
|
||||
// This fallback contains text that is explicitly EN.
|
||||
switch (msgtype) {
|
||||
case MsgType.Text:
|
||||
case MsgType.Notice: {
|
||||
html =
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>${html}</blockquote></mx-reply>`;
|
||||
const lines = body.trim().split("\n");
|
||||
if (lines.length > 0) {
|
||||
lines[0] = `<${mxid}> ${lines[0]}`;
|
||||
body = lines.map((line) => `> ${line}`).join("\n") + "\n\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MsgType.Image:
|
||||
html =
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>sent an image.</blockquote></mx-reply>`;
|
||||
body = `> <${mxid}> sent an image.\n\n`;
|
||||
break;
|
||||
case MsgType.Video:
|
||||
html =
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>sent a video.</blockquote></mx-reply>`;
|
||||
body = `> <${mxid}> sent a video.\n\n`;
|
||||
break;
|
||||
case MsgType.Audio:
|
||||
html =
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>sent an audio file.</blockquote></mx-reply>`;
|
||||
body = `> <${mxid}> sent an audio file.\n\n`;
|
||||
break;
|
||||
case MsgType.File:
|
||||
html =
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>sent a file.</blockquote></mx-reply>`;
|
||||
body = `> <${mxid}> sent a file.\n\n`;
|
||||
break;
|
||||
case MsgType.Location: {
|
||||
const aTheir = isSelfLocation(ev.getContent()) ? "their" : "a";
|
||||
html =
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> <a href="${userLink}">${mxid}</a>` +
|
||||
`<br>shared ${aTheir} location.</blockquote></mx-reply>`;
|
||||
body = `> <${mxid}> shared ${aTheir} location.\n\n`;
|
||||
break;
|
||||
}
|
||||
case MsgType.Emote: {
|
||||
html =
|
||||
`<mx-reply><blockquote><a href="${evLink}">In reply to</a> * ` +
|
||||
`<a href="${userLink}">${mxid}</a><br>${html}</blockquote></mx-reply>`;
|
||||
const lines = body.trim().split("\n");
|
||||
if (lines.length > 0) {
|
||||
lines[0] = `* <${mxid}> ${lines[0]}`;
|
||||
body = lines.map((line) => `> ${line}`).join("\n") + "\n\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return { body, html };
|
||||
}
|
||||
|
||||
export function makeReplyMixIn(ev?: MatrixEvent): IEventRelation {
|
||||
if (!ev) return {};
|
||||
|
||||
|
@ -227,34 +83,9 @@ export function shouldDisplayReply(event: MatrixEvent): boolean {
|
|||
return !!inReplyTo.event_id;
|
||||
}
|
||||
|
||||
interface AddReplyOpts {
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
includeLegacyFallback: false;
|
||||
}
|
||||
|
||||
interface IncludeLegacyFeedbackOpts {
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
includeLegacyFallback: true;
|
||||
}
|
||||
|
||||
export function addReplyToMessageContent(
|
||||
content: IContent,
|
||||
replyToEvent: MatrixEvent,
|
||||
opts: AddReplyOpts | IncludeLegacyFeedbackOpts,
|
||||
): void {
|
||||
export function addReplyToMessageContent(content: IContent, replyToEvent: MatrixEvent): void {
|
||||
content["m.relates_to"] = {
|
||||
...(content["m.relates_to"] || {}),
|
||||
...makeReplyMixIn(replyToEvent),
|
||||
};
|
||||
|
||||
if (opts.includeLegacyFallback) {
|
||||
// Part of Replies fallback support - prepend the text we're sending with the text we're replying to
|
||||
const nestedReply = getNestedReplyText(replyToEvent, opts.permalinkCreator);
|
||||
if (nestedReply) {
|
||||
if (content.formatted_body) {
|
||||
content.formatted_body = nestedReply.html + content.formatted_body;
|
||||
}
|
||||
content.body = nestedReply.body + content.body;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue