Improve switching between rich and plain editing modes (#9776)

* allows switching between modes that retains formatting
* updates rich text composer dependency to 0.13.0 (@matrix-org/matrix-wysiwyg)
* improves handling of enter keypresses when ctrlEnterTosend setting is true in plain text editor
* changes the message event content when using the new editor
* adds tests for the changes to the plain text editor
This commit is contained in:
alunturner 2023-01-04 12:57:09 +00:00 committed by GitHub
parent 3bcea5fb0b
commit 432ce3ca31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 336 additions and 94 deletions

View file

@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { richToPlain, plainToRich } from "@matrix-org/matrix-wysiwyg";
import { IContent, IEventRelation, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix";
import { htmlSerializeFromMdIfNeeded } from "../../../../../editor/serialize";
import SettingsStore from "../../../../../settings/SettingsStore";
import { RoomPermalinkCreator } from "../../../../../utils/permalinks/Permalinks";
import { addReplyToMessageContent } from "../../../../../utils/Reply";
import { htmlToPlainText } from "../../../../../utils/room/htmlToPlaintext";
// Merges favouring the given relation
function attachRelation(content: IContent, relation?: IEventRelation): void {
@ -62,7 +61,7 @@ interface CreateMessageContentParams {
editedEvent?: MatrixEvent;
}
export function createMessageContent(
export async function createMessageContent(
message: string,
isHTML: boolean,
{
@ -72,7 +71,7 @@ export function createMessageContent(
includeReplyLegacyFallback = true,
editedEvent,
}: CreateMessageContentParams,
): IContent {
): Promise<IContent> {
// TODO emote ?
const isEditing = Boolean(editedEvent);
@ -90,26 +89,22 @@ export function createMessageContent(
// const body = textSerialize(model);
// TODO remove this ugly hack for replace br tag
const body = (isHTML && htmlToPlainText(message)) || message.replace(/<br>/g, "\n");
// if we're editing rich text, the message content is pure html
// BUT if we're not, the message content will be plain text
const body = isHTML ? await richToPlain(message) : message;
const bodyPrefix = (isReplyAndEditing && getTextReplyFallback(editedEvent)) || "";
const formattedBodyPrefix = (isReplyAndEditing && getHtmlReplyFallback(editedEvent)) || "";
const content: IContent = {
// TODO emote
msgtype: MsgType.Text,
// TODO when available, use HTML --> Plain text conversion from wysiwyg rust model
body: isEditing ? `${bodyPrefix} * ${body}` : body,
};
// TODO markdown support
const isMarkdownEnabled = SettingsStore.getValue<boolean>("MessageComposerInput.useMarkdown");
const formattedBody = isHTML
? message
: isMarkdownEnabled
? htmlSerializeFromMdIfNeeded(message, { forceHTML: isReply })
: null;
const formattedBody = isHTML ? message : isMarkdownEnabled ? await plainToRich(message) : null;
if (formattedBody) {
content.format = "org.matrix.custom.html";

View file

@ -15,7 +15,7 @@ limitations under the License.
*/
import { Composer as ComposerEvent } from "@matrix-org/analytics-events/types/typescript/Composer";
import { IContent, IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event";
import { IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event";
import { ISendEventResponse, MatrixClient } from "matrix-js-sdk/src/matrix";
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
@ -34,7 +34,7 @@ import EditorStateTransfer from "../../../../../utils/EditorStateTransfer";
import { createMessageContent } from "./createMessageContent";
import { isContentModified } from "./isContentModified";
interface SendMessageParams {
export interface SendMessageParams {
mxClient: MatrixClient;
relation?: IEventRelation;
replyToEvent?: MatrixEvent;
@ -43,10 +43,18 @@ interface SendMessageParams {
includeReplyLegacyFallback?: boolean;
}
export function sendMessage(message: string, isHTML: boolean, { roomContext, mxClient, ...params }: SendMessageParams) {
export async function sendMessage(
message: string,
isHTML: boolean,
{ roomContext, mxClient, ...params }: SendMessageParams,
) {
const { relation, replyToEvent } = params;
const { room } = roomContext;
const { roomId } = room;
const roomId = room?.roomId;
if (!roomId) {
return;
}
const posthogEvent: ComposerEvent = {
eventName: "Composer",
@ -63,7 +71,7 @@ export function sendMessage(message: string, isHTML: boolean, { roomContext, mxC
}*/
PosthogAnalytics.instance.trackEvent<ComposerEvent>(posthogEvent);
let content: IContent;
const content = await createMessageContent(message, isHTML, params);
// TODO slash comment
@ -71,10 +79,6 @@ export function sendMessage(message: string, isHTML: boolean, { roomContext, mxC
// TODO quick reaction
if (!content) {
content = createMessageContent(message, isHTML, params);
}
// don't bother sending an empty message
if (!content.body.trim()) {
return;
@ -84,7 +88,7 @@ export function sendMessage(message: string, isHTML: boolean, { roomContext, mxC
decorateStartSendingTime(content);
}
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
const threadId = relation?.event_id && relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
const prom = doMaybeLocalRoomAction(
roomId,
@ -139,7 +143,7 @@ interface EditMessageParams {
editorStateTransfer: EditorStateTransfer;
}
export function editMessage(html: string, { roomContext, mxClient, editorStateTransfer }: EditMessageParams) {
export async function editMessage(html: string, { roomContext, mxClient, editorStateTransfer }: EditMessageParams) {
const editedEvent = editorStateTransfer.getEvent();
PosthogAnalytics.instance.trackEvent<ComposerEvent>({
@ -156,7 +160,7 @@ export function editMessage(html: string, { roomContext, mxClient, editorStateTr
const position = this.model.positionForOffset(caret.offset, caret.atNodeEnd);
this.editorRef.current?.replaceEmoticon(position, REGEX_EMOTICON);
}*/
const editContent = createMessageContent(html, true, { editedEvent });
const editContent = await createMessageContent(html, true, { editedEvent });
const newContent = editContent["m.new_content"];
const shouldSend = true;
@ -174,10 +178,10 @@ export function editMessage(html: string, { roomContext, mxClient, editorStateTr
let response: Promise<ISendEventResponse> | undefined;
// If content is modified then send an updated event into the room
if (isContentModified(newContent, editorStateTransfer)) {
const roomId = editedEvent.getRoomId();
const roomId = editedEvent.getRoomId();
// If content is modified then send an updated event into the room
if (isContentModified(newContent, editorStateTransfer) && roomId) {
// TODO Slash Commands
if (shouldSend) {