Add support for MD / HTML in room topics (#8215)

* Add support for MD / HTML in room topics

Setting MD / HTML supported:
- /topic command
- Room settings overlay
- Space settings overlay

Display of MD / HTML supported:
- /topic command
- Room header
- Space home

Based on extensible events as defined in [MSC1767]

Fixes: vector-im/element-web#5180
Signed-off-by: Johannes Marbach <johannesm@element.io>

[MSC1767]: matrix-org/matrix-spec-proposals#1767

* Fix build error

* Add comment to explain origin of styles

Co-authored-by: Travis Ralston <travpc@gmail.com>

* Empty commit to retrigger build

* Fix import grouping

* Fix useTopic test

* Add tests for HtmlUtils

* Add slash command test

* Add further serialize test

* Fix ternary formatting

Co-authored-by: Travis Ralston <travpc@gmail.com>

* Add blank line

Co-authored-by: Travis Ralston <travpc@gmail.com>

* Properly mock SettingsStore access

* Remove trailing space

* Assert on HTML content and add test for plain text in HTML parameter

* Appease the linter

* Fix JSDoc comment

* Fix toEqual call formatting

* Repurpose test for literal HTML case

* Empty commit to fix CI

Co-authored-by: Travis Ralston <travpc@gmail.com>
Co-authored-by: Travis Ralston <travisr@matrix.org>
This commit is contained in:
Johannes Marbach 2022-06-07 22:20:32 +02:00 committed by GitHub
parent 8036985204
commit abd39c61b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 298 additions and 19 deletions

View file

@ -31,6 +31,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import AccessibleButton from "./AccessibleButton";
import { Linkify } from "./Linkify";
import TooltipTarget from "./TooltipTarget";
import { topicToHtml } from "../../../HtmlUtils";
interface IProps extends React.HTMLProps<HTMLDivElement> {
room?: Room;
@ -44,6 +45,7 @@ export default function RoomTopic({
const ref = useRef<HTMLDivElement>();
const topic = useTopic(room);
const body = topicToHtml(topic?.text, topic?.html, ref);
const onClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
props.onClick?.(e);
@ -62,6 +64,7 @@ export default function RoomTopic({
useDispatcher(dis, (payload) => {
if (payload.action === Action.ShowRoomTopic) {
const canSetTopic = room.currentState.maySendStateEvent(EventType.RoomTopic, client.getUserId());
const body = topicToHtml(topic?.text, topic?.html, ref, true);
const modal = Modal.createDialog(InfoDialog, {
title: room.name,
@ -74,7 +77,7 @@ export default function RoomTopic({
}
}}
>
{ topic }
{ body }
</Linkify>
{ canSetTopic && <AccessibleButton
kind="primary_outline"
@ -101,7 +104,7 @@ export default function RoomTopic({
>
<TooltipTarget label={_t("Click to read topic")} alignment={Alignment.Bottom} ignoreHover={ignoreHover}>
<Linkify>
{ topic }
{ body }
</Linkify>
</TooltipTarget>
</div>;

View file

@ -22,6 +22,7 @@ import Field from "../elements/Field";
import { mediaFromMxc } from "../../../customisations/Media";
import AccessibleButton from "../elements/AccessibleButton";
import AvatarSetting from "../settings/AvatarSetting";
import { htmlSerializeFromMdIfNeeded } from '../../../editor/serialize';
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
interface IProps {
@ -142,7 +143,8 @@ export default class RoomProfileSettings extends React.Component<IProps, IState>
}
if (this.state.originalTopic !== this.state.topic) {
await client.setRoomTopic(this.props.roomId, this.state.topic);
const html = htmlSerializeFromMdIfNeeded(this.state.topic, { forceHTML: false });
await client.setRoomTopic(this.props.roomId, this.state.topic, html);
newState.originalTopic = this.state.topic;
}

View file

@ -25,6 +25,7 @@ import AccessibleButton from "../elements/AccessibleButton";
import SpaceBasicSettings from "./SpaceBasicSettings";
import { avatarUrlForRoom } from "../../../Avatar";
import { IDialogProps } from "../dialogs/IDialogProps";
import { htmlSerializeFromMdIfNeeded } from "../../../editor/serialize";
import { leaveSpace } from "../../../utils/leave-behaviour";
import { getTopic } from "../../../hooks/room/useTopic";
@ -47,7 +48,7 @@ const SpaceSettingsGeneralTab = ({ matrixClient: cli, space, onFinished }: IProp
const canSetName = space.currentState.maySendStateEvent(EventType.RoomName, userId);
const nameChanged = name !== space.name;
const currentTopic = getTopic(space);
const currentTopic = getTopic(space).text;
const [topic, setTopic] = useState<string>(currentTopic);
const canSetTopic = space.currentState.maySendStateEvent(EventType.RoomTopic, userId);
const topicChanged = topic !== currentTopic;
@ -77,7 +78,8 @@ const SpaceSettingsGeneralTab = ({ matrixClient: cli, space, onFinished }: IProp
}
if (topicChanged) {
promises.push(cli.setRoomTopic(space.roomId, topic));
const htmlTopic = htmlSerializeFromMdIfNeeded(topic, { forceHTML: false });
promises.push(cli.setRoomTopic(space.roomId, topic, htmlTopic));
}
const results = await Promise.allSettled(promises);