Add dialog to navigate long room topics (#8517)
This commit is contained in:
parent
e0415d0123
commit
fdd5494402
13 changed files with 347 additions and 41 deletions
|
@ -140,6 +140,10 @@ limitations under the License.
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomTopic {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_topic {
|
.mx_RoomHeader_topic {
|
||||||
$lineHeight: $font-16px;
|
$lineHeight: $font-16px;
|
||||||
$lines: 2;
|
$lines: 2;
|
||||||
|
@ -209,6 +213,7 @@ limitations under the License.
|
||||||
.mx_RoomHeader_appsButton::before {
|
.mx_RoomHeader_appsButton::before {
|
||||||
mask-image: url('$(res)/img/element-icons/room/apps.svg');
|
mask-image: url('$(res)/img/element-icons/room/apps.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader_appsButton_highlight::before {
|
.mx_RoomHeader_appsButton_highlight::before {
|
||||||
background-color: $accent;
|
background-color: $accent;
|
||||||
}
|
}
|
||||||
|
@ -239,6 +244,7 @@ limitations under the License.
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomHeader {
|
.mx_RoomHeader {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,13 +267,7 @@ const SpaceLanding = ({ space }: { space: Room }) => {
|
||||||
{ settingsButton }
|
{ settingsButton }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<RoomTopic room={space}>
|
<RoomTopic room={space} className="mx_SpaceRoomView_landing_topic" />
|
||||||
{ (topic, ref) => (
|
|
||||||
<div className="mx_SpaceRoomView_landing_topic" ref={ref}>
|
|
||||||
{ topic }
|
|
||||||
</div>
|
|
||||||
) }
|
|
||||||
</RoomTopic>
|
|
||||||
|
|
||||||
<SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
|
<SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
|
||||||
</div>;
|
</div>;
|
||||||
|
|
39
src/components/views/elements/Linkify.tsx
Normal file
39
src/components/views/elements/Linkify.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import linkifyElement from "linkify-element";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
as?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Linkify({
|
||||||
|
as = "div",
|
||||||
|
children,
|
||||||
|
}: Props): JSX.Element {
|
||||||
|
const ref = useRef();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
linkifyElement(ref.current);
|
||||||
|
}, [children]);
|
||||||
|
|
||||||
|
return React.createElement(as, {
|
||||||
|
children,
|
||||||
|
ref,
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
Copyright 2021 - 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,35 +14,87 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useCallback, useContext, useEffect, useRef } from "react";
|
||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
import classNames from "classnames";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
|
||||||
import { useTypedEventEmitter } from "../../../hooks/useEventEmitter";
|
|
||||||
import { linkifyElement } from "../../../HtmlUtils";
|
import { linkifyElement } from "../../../HtmlUtils";
|
||||||
|
import { useTopic } from "../../../hooks/room/useTopic";
|
||||||
|
import useHover from "../../../hooks/useHover";
|
||||||
|
import Tooltip, { Alignment } from "./Tooltip";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
|
import { Action } from "../../../dispatcher/actions";
|
||||||
|
import Modal from "../../../Modal";
|
||||||
|
import InfoDialog from "../dialogs/InfoDialog";
|
||||||
|
import { useDispatcher } from "../../../hooks/useDispatcher";
|
||||||
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import AccessibleButton from "./AccessibleButton";
|
||||||
|
import { Linkify } from "./Linkify";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps extends React.HTMLProps<HTMLDivElement> {
|
||||||
room?: Room;
|
room?: Room;
|
||||||
children?(topic: string, ref: (element: HTMLElement) => void): JSX.Element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTopic = room => room?.currentState?.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic;
|
export default function RoomTopic({
|
||||||
|
room,
|
||||||
|
...props
|
||||||
|
}: IProps) {
|
||||||
|
const client = useContext(MatrixClientContext);
|
||||||
|
const ref = useRef<HTMLDivElement>();
|
||||||
|
const hovered = useHover(ref);
|
||||||
|
|
||||||
const RoomTopic = ({ room, children }: IProps): JSX.Element => {
|
const topic = useTopic(room);
|
||||||
const [topic, setTopic] = useState(getTopic(room));
|
|
||||||
useTypedEventEmitter(room.currentState, RoomStateEvent.Events, (ev: MatrixEvent) => {
|
const onClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (ev.getType() !== EventType.RoomTopic) return;
|
props.onClick?.(e);
|
||||||
setTopic(getTopic(room));
|
const target = e.target as HTMLElement;
|
||||||
|
if (target.tagName.toUpperCase() === "A") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dis.fire(Action.ShowRoomTopic);
|
||||||
|
}, [props]);
|
||||||
|
|
||||||
|
useDispatcher(dis, (payload) => {
|
||||||
|
if (payload.action === Action.ShowRoomTopic) {
|
||||||
|
const canSetTopic = room.currentState.maySendStateEvent(EventType.RoomTopic, client.getUserId());
|
||||||
|
|
||||||
|
const modal = Modal.createDialog(InfoDialog, {
|
||||||
|
title: room.name,
|
||||||
|
description: <div>
|
||||||
|
<Linkify as="p">{ topic }</Linkify>
|
||||||
|
{ canSetTopic && <AccessibleButton
|
||||||
|
kind="primary_outline"
|
||||||
|
onClick={() => {
|
||||||
|
modal.close();
|
||||||
|
dis.dispatch({ action: "open_room_settings" });
|
||||||
|
}}>
|
||||||
|
{ _t("Edit topic") }
|
||||||
|
</AccessibleButton> }
|
||||||
|
</div>,
|
||||||
|
hasCloseButton: true,
|
||||||
|
button: false,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTopic(getTopic(room));
|
linkifyElement(ref.current);
|
||||||
}, [room]);
|
}, [topic]);
|
||||||
|
|
||||||
const ref = e => e && linkifyElement(e);
|
const className = classNames(props.className, "mx_RoomTopic");
|
||||||
if (children) return children(topic, ref);
|
|
||||||
return <span ref={ref}>{ topic }</span>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RoomTopic;
|
return <div {...props}
|
||||||
|
ref={ref}
|
||||||
|
onClick={onClick}
|
||||||
|
dir="auto"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
{ topic }
|
||||||
|
{ hovered && (
|
||||||
|
<Tooltip label={_t("Click to read topic")} alignment={Alignment.Bottom} />
|
||||||
|
) }
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
|
@ -186,11 +186,10 @@ export default class RoomHeader extends React.Component<IProps, IState> {
|
||||||
</ContextMenuTooltipButton>
|
</ContextMenuTooltipButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
const topicElement = <RoomTopic room={this.props.room}>
|
const topicElement = <RoomTopic
|
||||||
{ (topic, ref) => <div className="mx_RoomHeader_topic" ref={ref} title={topic} dir="auto">
|
room={this.props.room}
|
||||||
{ topic }
|
className="mx_RoomHeader_topic"
|
||||||
</div> }
|
/>;
|
||||||
</RoomTopic>;
|
|
||||||
|
|
||||||
let roomAvatar;
|
let roomAvatar;
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
|
|
|
@ -182,13 +182,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
|
||||||
<RoomName room={room} />
|
<RoomName room={room} />
|
||||||
</h1>
|
</h1>
|
||||||
<RoomInfoLine room={room} />
|
<RoomInfoLine room={room} />
|
||||||
<RoomTopic room={room}>
|
<RoomTopic room={room} className="mx_RoomPreviewCard_topic" />
|
||||||
{ (topic, ref) =>
|
|
||||||
topic ? <div className="mx_RoomPreviewCard_topic" ref={ref}>
|
|
||||||
{ topic }
|
|
||||||
</div> : null
|
|
||||||
}
|
|
||||||
</RoomTopic>
|
|
||||||
{ room.getJoinRule() === "public" && <RoomFacePile room={room} /> }
|
{ room.getJoinRule() === "public" && <RoomFacePile room={room} /> }
|
||||||
{ notice ? <div className="mx_RoomPreviewCard_notice">
|
{ notice ? <div className="mx_RoomPreviewCard_notice">
|
||||||
{ notice }
|
{ notice }
|
||||||
|
|
|
@ -25,8 +25,8 @@ import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import SpaceBasicSettings from "./SpaceBasicSettings";
|
import SpaceBasicSettings from "./SpaceBasicSettings";
|
||||||
import { avatarUrlForRoom } from "../../../Avatar";
|
import { avatarUrlForRoom } from "../../../Avatar";
|
||||||
import { IDialogProps } from "../dialogs/IDialogProps";
|
import { IDialogProps } from "../dialogs/IDialogProps";
|
||||||
import { getTopic } from "../elements/RoomTopic";
|
|
||||||
import { leaveSpace } from "../../../utils/leave-behaviour";
|
import { leaveSpace } from "../../../utils/leave-behaviour";
|
||||||
|
import { getTopic } from "../../../hooks/room/useTopic";
|
||||||
|
|
||||||
interface IProps extends IDialogProps {
|
interface IProps extends IDialogProps {
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
|
|
|
@ -313,4 +313,9 @@ export enum Action {
|
||||||
* logs. Fires with no payload.
|
* logs. Fires with no payload.
|
||||||
*/
|
*/
|
||||||
DumpDebugLogs = "dump_debug_logs",
|
DumpDebugLogs = "dump_debug_logs",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show current room topic
|
||||||
|
*/
|
||||||
|
ShowRoomTopic = "show_room_topic"
|
||||||
}
|
}
|
||||||
|
|
40
src/hooks/room/useTopic.ts
Normal file
40
src/hooks/room/useTopic.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||||
|
|
||||||
|
import { useTypedEventEmitter } from "../useEventEmitter";
|
||||||
|
|
||||||
|
export const getTopic = (room: Room) => {
|
||||||
|
return room?.currentState?.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useTopic(room: Room): string {
|
||||||
|
const [topic, setTopic] = useState(getTopic(room));
|
||||||
|
useTypedEventEmitter(room.currentState, RoomStateEvent.Events, (ev: MatrixEvent) => {
|
||||||
|
if (ev.getType() !== EventType.RoomTopic) return;
|
||||||
|
setTopic(getTopic(room));
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
setTopic(getTopic(room));
|
||||||
|
}, [room]);
|
||||||
|
|
||||||
|
return topic;
|
||||||
|
}
|
42
src/hooks/useHover.ts
Normal file
42
src/hooks/useHover.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export default function useHover(ref: React.MutableRefObject<HTMLElement>) {
|
||||||
|
const [hovered, setHoverState] = useState(false);
|
||||||
|
|
||||||
|
const handleMouseOver = () => setHoverState(true);
|
||||||
|
const handleMouseOut = () => setHoverState(false);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
const node = ref.current;
|
||||||
|
if (node) {
|
||||||
|
node.addEventListener("mouseover", handleMouseOver);
|
||||||
|
node.addEventListener("mouseout", handleMouseOut);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
node.removeEventListener("mouseover", handleMouseOver);
|
||||||
|
node.removeEventListener("mouseout", handleMouseOut);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ref],
|
||||||
|
);
|
||||||
|
|
||||||
|
return hovered;
|
||||||
|
}
|
|
@ -2370,6 +2370,8 @@
|
||||||
"Including %(commaSeparatedMembers)s": "Including %(commaSeparatedMembers)s",
|
"Including %(commaSeparatedMembers)s": "Including %(commaSeparatedMembers)s",
|
||||||
"%(count)s people you know have already joined|other": "%(count)s people you know have already joined",
|
"%(count)s people you know have already joined|other": "%(count)s people you know have already joined",
|
||||||
"%(count)s people you know have already joined|one": "%(count)s person you know has already joined",
|
"%(count)s people you know have already joined|one": "%(count)s person you know has already joined",
|
||||||
|
"Edit topic": "Edit topic",
|
||||||
|
"Click to read topic": "Click to read topic",
|
||||||
"Message search initialisation failed, check <a>your settings</a> for more information": "Message search initialisation failed, check <a>your settings</a> for more information",
|
"Message search initialisation failed, check <a>your settings</a> for more information": "Message search initialisation failed, check <a>your settings</a> for more information",
|
||||||
"Use the <a>Desktop app</a> to see all encrypted files": "Use the <a>Desktop app</a> to see all encrypted files",
|
"Use the <a>Desktop app</a> to see all encrypted files": "Use the <a>Desktop app</a> to see all encrypted files",
|
||||||
"Use the <a>Desktop app</a> to search encrypted messages": "Use the <a>Desktop app</a> to search encrypted messages",
|
"Use the <a>Desktop app</a> to search encrypted messages": "Use the <a>Desktop app</a> to search encrypted messages",
|
||||||
|
|
64
test/components/views/elements/Linkify-test.tsx
Normal file
64
test/components/views/elements/Linkify-test.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
|
||||||
|
import { Linkify } from "../../../../src/components/views/elements/Linkify";
|
||||||
|
|
||||||
|
describe("Linkify", () => {
|
||||||
|
it("linkifies the context", () => {
|
||||||
|
const wrapper = mount(<Linkify>
|
||||||
|
https://perdu.com
|
||||||
|
</Linkify>);
|
||||||
|
expect(wrapper.html()).toBe('<div><a href="https://perdu.com">https://perdu.com</a></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes the root tag name", () => {
|
||||||
|
const TAG_NAME = "p";
|
||||||
|
|
||||||
|
const wrapper = mount(<Linkify as={TAG_NAME}>
|
||||||
|
Hello world!
|
||||||
|
</Linkify>);
|
||||||
|
|
||||||
|
expect(wrapper.find("p")).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("relinkifies on update", () => {
|
||||||
|
function DummyTest() {
|
||||||
|
const [n, setN] = useState(0);
|
||||||
|
function onClick() {
|
||||||
|
setN(n + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// upon clicking the element, change the content, and expect
|
||||||
|
// linkify to update
|
||||||
|
return <div onClick={onClick}>
|
||||||
|
<Linkify>
|
||||||
|
{ n % 2 === 0
|
||||||
|
? "https://perdu.com"
|
||||||
|
: "https://matrix.org" }
|
||||||
|
</Linkify>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = mount(<DummyTest />);
|
||||||
|
|
||||||
|
expect(wrapper.html()).toBe('<div><div><a href="https://perdu.com">https://perdu.com</a></div></div>');
|
||||||
|
|
||||||
|
wrapper.find('div').at(0).simulate('click');
|
||||||
|
|
||||||
|
expect(wrapper.html()).toBe('<div><div><a href="https://matrix.org">https://matrix.org</a></div></div>');
|
||||||
|
});
|
||||||
|
});
|
69
test/useTopic-test.tsx
Normal file
69
test/useTopic-test.tsx
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import { act } from "react-dom/test-utils";
|
||||||
|
|
||||||
|
import { useTopic } from "../src/hooks/room/useTopic";
|
||||||
|
import { mkEvent, stubClient } from "./test-utils";
|
||||||
|
import { MatrixClientPeg } from "../src/MatrixClientPeg";
|
||||||
|
|
||||||
|
describe("useTopic", () => {
|
||||||
|
it("should display the room topic", () => {
|
||||||
|
stubClient();
|
||||||
|
const room = new Room("!TESTROOM", MatrixClientPeg.get(), "@alice:example.org");
|
||||||
|
const topic = mkEvent({
|
||||||
|
type: 'm.room.topic',
|
||||||
|
room: '!TESTROOM',
|
||||||
|
user: '@alice:example.org',
|
||||||
|
content: {
|
||||||
|
topic: 'Test topic',
|
||||||
|
},
|
||||||
|
ts: 123,
|
||||||
|
event: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
room.addLiveEvents([topic]);
|
||||||
|
|
||||||
|
function RoomTopic() {
|
||||||
|
const topic = useTopic(room);
|
||||||
|
return <p>{ topic }</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = mount(<RoomTopic />);
|
||||||
|
|
||||||
|
expect(wrapper.text()).toBe("Test topic");
|
||||||
|
|
||||||
|
const updatedTopic = mkEvent({
|
||||||
|
type: 'm.room.topic',
|
||||||
|
room: '!TESTROOM',
|
||||||
|
user: '@alice:example.org',
|
||||||
|
content: {
|
||||||
|
topic: 'New topic',
|
||||||
|
},
|
||||||
|
ts: 666,
|
||||||
|
event: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
room.addLiveEvents([updatedTopic]);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toBe("New topic");
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue