Start DM on first message (#8612)
parent
0e0be08781
commit
ed8ccb5d80
|
@ -57,7 +57,7 @@ const startDMWithBob = function(this: CryptoTestContext) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const testMessages = function(this: CryptoTestContext) {
|
const testMessages = function(this: CryptoTestContext) {
|
||||||
cy.get(".mx_BasicMessageComposer_input").should("have.focus").type("Hey!{enter}");
|
// check the invite message
|
||||||
cy.contains(".mx_EventTile_body", "Hey!")
|
cy.contains(".mx_EventTile_body", "Hey!")
|
||||||
.closest(".mx_EventTile")
|
.closest(".mx_EventTile")
|
||||||
.should("not.have.descendants", ".mx_EventTile_e2eIcon_warning")
|
.should("not.have.descendants", ".mx_EventTile_e2eIcon_warning")
|
||||||
|
@ -150,6 +150,11 @@ describe("Cryptography", function() {
|
||||||
it("creating a DM should work, being e2e-encrypted / user verification", function(this: CryptoTestContext) {
|
it("creating a DM should work, being e2e-encrypted / user verification", function(this: CryptoTestContext) {
|
||||||
cy.bootstrapCrossSigning();
|
cy.bootstrapCrossSigning();
|
||||||
startDMWithBob.call(this);
|
startDMWithBob.call(this);
|
||||||
|
// send first message
|
||||||
|
cy.get(".mx_BasicMessageComposer_input")
|
||||||
|
.click()
|
||||||
|
.should("have.focus")
|
||||||
|
.type("Hey!{enter}");
|
||||||
checkDMRoom();
|
checkDMRoom();
|
||||||
bobJoin.call(this);
|
bobJoin.call(this);
|
||||||
testMessages.call(this);
|
testMessages.call(this);
|
||||||
|
|
|
@ -118,10 +118,13 @@ Cypress.Commands.add("startDM", (name: string) => {
|
||||||
cy.spotlightResults().should("have.length", 1);
|
cy.spotlightResults().should("have.length", 1);
|
||||||
cy.spotlightResults().eq(0).should("contain", name);
|
cy.spotlightResults().eq(0).should("contain", name);
|
||||||
cy.spotlightResults().eq(0).click();
|
cy.spotlightResults().eq(0).click();
|
||||||
}).then(() => {
|
|
||||||
cy.roomHeaderName().should("contain", name);
|
|
||||||
cy.get(".mx_RoomSublist[aria-label=People]").should("contain", name);
|
|
||||||
});
|
});
|
||||||
|
// send first message to start DM
|
||||||
|
cy.get(".mx_BasicMessageComposer_input")
|
||||||
|
.should("have.focus")
|
||||||
|
.type("Hey!{enter}");
|
||||||
|
cy.contains(".mx_EventTile_body", "Hey!");
|
||||||
|
cy.get(".mx_RoomSublist[aria-label=People]").should("contain", name);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Spotlight", () => {
|
describe("Spotlight", () => {
|
||||||
|
@ -324,11 +327,19 @@ describe("Spotlight", () => {
|
||||||
cy.spotlightResults().should("have.length", 1);
|
cy.spotlightResults().should("have.length", 1);
|
||||||
cy.spotlightResults().eq(0).should("contain", bot2Name);
|
cy.spotlightResults().eq(0).should("contain", bot2Name);
|
||||||
cy.spotlightResults().eq(0).click();
|
cy.spotlightResults().eq(0).click();
|
||||||
}).then(() => {
|
|
||||||
cy.roomHeaderName().should("contain", bot2Name);
|
|
||||||
cy.get(".mx_RoomSublist[aria-label=People]").should("contain", bot2Name);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Send first message to actually start DM
|
||||||
|
cy.roomHeaderName().should("contain", bot2Name);
|
||||||
|
cy.get(".mx_BasicMessageComposer_input")
|
||||||
|
.click()
|
||||||
|
.should("have.focus")
|
||||||
|
.type("Hey!{enter}");
|
||||||
|
|
||||||
|
// Assert DM exists by checking for the first message and the room being in the room list
|
||||||
|
cy.contains(".mx_EventTile_body", "Hey!");
|
||||||
|
cy.get(".mx_RoomSublist[aria-label=People]").should("contain", bot2Name);
|
||||||
|
|
||||||
// Invite BotBob into existing DM with ByteBot
|
// Invite BotBob into existing DM with ByteBot
|
||||||
cy.getDmRooms(bot2.getUserId()).then(dmRooms => dmRooms[0])
|
cy.getDmRooms(bot2.getUserId()).then(dmRooms => dmRooms[0])
|
||||||
.then(groupDmId => cy.inviteUser(groupDmId, bot1.getUserId()))
|
.then(groupDmId => cy.inviteUser(groupDmId, bot1.getUserId()))
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
@import "./structures/_GenericErrorPage.pcss";
|
@import "./structures/_GenericErrorPage.pcss";
|
||||||
@import "./structures/_HeaderButtons.pcss";
|
@import "./structures/_HeaderButtons.pcss";
|
||||||
@import "./structures/_HomePage.pcss";
|
@import "./structures/_HomePage.pcss";
|
||||||
|
@import "./structures/_LargeLoader.pcss";
|
||||||
@import "./structures/_LeftPanel.pcss";
|
@import "./structures/_LeftPanel.pcss";
|
||||||
@import "./structures/_MainSplit.pcss";
|
@import "./structures/_MainSplit.pcss";
|
||||||
@import "./structures/_MatrixChat.pcss";
|
@import "./structures/_MatrixChat.pcss";
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_LargeLoader {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.mx_Spinner {
|
||||||
|
flex: unset;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
margin-top: 33vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LargeLoader_text {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0 16px;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,13 @@ limitations under the License.
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MainSplit_timeline,
|
||||||
|
.mx_RoomView--local {
|
||||||
|
.mx_MessageComposer_wrapper {
|
||||||
|
margin: $spacing-8 $spacing-16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomView_auxPanel {
|
.mx_RoomView_auxPanel {
|
||||||
min-width: 0px;
|
min-width: 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -183,6 +190,10 @@ limitations under the License.
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomView--local .mx_ScrollPanel .mx_RoomView_MessageList {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomView_MessageList li {
|
.mx_RoomView_MessageList li {
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
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 Spinner from "../views/elements/Spinner";
|
||||||
|
|
||||||
|
interface LargeLoaderProps {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loader component that displays a (almost centered) spinner and loading message.
|
||||||
|
*/
|
||||||
|
export const LargeLoader: React.FC<LargeLoaderProps> = ({ text }) => {
|
||||||
|
return (
|
||||||
|
<div className="mx_LargeLoader">
|
||||||
|
<Spinner w={45} h={45} />
|
||||||
|
<div className="mx_LargeLoader_text">
|
||||||
|
{ text }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -20,7 +20,7 @@ limitations under the License.
|
||||||
// TODO: This component is enormous! There's several things which could stand-alone:
|
// TODO: This component is enormous! There's several things which could stand-alone:
|
||||||
// - Search results component
|
// - Search results component
|
||||||
|
|
||||||
import React, { createRef } from 'react';
|
import React, { createRef, ReactElement, ReactNode, RefObject, useContext } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { IRecommendedVersion, NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
import { IRecommendedVersion, NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||||
import { IThreadBundledRelationship, MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
|
import { IThreadBundledRelationship, MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
@ -46,7 +46,7 @@ import ResizeNotifier from '../../utils/ResizeNotifier';
|
||||||
import ContentMessages from '../../ContentMessages';
|
import ContentMessages from '../../ContentMessages';
|
||||||
import Modal from '../../Modal';
|
import Modal from '../../Modal';
|
||||||
import CallHandler, { CallHandlerEvent } from '../../CallHandler';
|
import CallHandler, { CallHandlerEvent } from '../../CallHandler';
|
||||||
import dis from '../../dispatcher/dispatcher';
|
import dis, { defaultDispatcher } from '../../dispatcher/dispatcher';
|
||||||
import * as Rooms from '../../Rooms';
|
import * as Rooms from '../../Rooms';
|
||||||
import eventSearch, { searchPagination } from '../../Searching';
|
import eventSearch, { searchPagination } from '../../Searching';
|
||||||
import MainSplit from './MainSplit';
|
import MainSplit from './MainSplit';
|
||||||
|
@ -110,7 +110,15 @@ import FileDropTarget from './FileDropTarget';
|
||||||
import Measured from '../views/elements/Measured';
|
import Measured from '../views/elements/Measured';
|
||||||
import { FocusComposerPayload } from '../../dispatcher/payloads/FocusComposerPayload';
|
import { FocusComposerPayload } from '../../dispatcher/payloads/FocusComposerPayload';
|
||||||
import { haveRendererForEvent } from "../../events/EventTileFactory";
|
import { haveRendererForEvent } from "../../events/EventTileFactory";
|
||||||
|
import { LocalRoom, LocalRoomState } from '../../models/LocalRoom';
|
||||||
|
import { createRoomFromLocalRoom } from '../../utils/direct-messages';
|
||||||
|
import NewRoomIntro from '../views/rooms/NewRoomIntro';
|
||||||
|
import EncryptionEvent from '../views/messages/EncryptionEvent';
|
||||||
|
import { StaticNotificationState } from '../../stores/notifications/StaticNotificationState';
|
||||||
|
import { isLocalRoom } from '../../utils/localRoom/isLocalRoom';
|
||||||
import { ShowThreadPayload } from "../../dispatcher/payloads/ShowThreadPayload";
|
import { ShowThreadPayload } from "../../dispatcher/payloads/ShowThreadPayload";
|
||||||
|
import { RoomStatusBarUnsentMessages } from './RoomStatusBarUnsentMessages';
|
||||||
|
import { LargeLoader } from './LargeLoader';
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
let debuglog = function(msg: string) {};
|
let debuglog = function(msg: string) {};
|
||||||
|
@ -222,6 +230,137 @@ export interface IRoomState {
|
||||||
narrow: boolean;
|
narrow: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LocalRoomViewProps {
|
||||||
|
resizeNotifier: ResizeNotifier;
|
||||||
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
|
roomView: RefObject<HTMLElement>;
|
||||||
|
onFileDrop: (dataTransfer: DataTransfer) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local room view. Uses only the bits necessary to display a local room view like room header or composer.
|
||||||
|
*
|
||||||
|
* @param {LocalRoomViewProps} props Room view props
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
function LocalRoomView(props: LocalRoomViewProps): ReactElement {
|
||||||
|
const context = useContext(RoomContext);
|
||||||
|
const room = context.room as LocalRoom;
|
||||||
|
const encryptionEvent = context.room.currentState.getStateEvents(EventType.RoomEncryption)[0];
|
||||||
|
let encryptionTile: ReactNode;
|
||||||
|
|
||||||
|
if (encryptionEvent) {
|
||||||
|
encryptionTile = <EncryptionEvent mxEvent={encryptionEvent} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onRetryClicked = () => {
|
||||||
|
room.state = LocalRoomState.NEW;
|
||||||
|
defaultDispatcher.dispatch({
|
||||||
|
action: "local_room_event",
|
||||||
|
roomId: room.roomId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let statusBar: ReactElement;
|
||||||
|
let composer: ReactElement;
|
||||||
|
|
||||||
|
if (room.isError) {
|
||||||
|
const buttons = (
|
||||||
|
<AccessibleButton onClick={onRetryClicked} className="mx_RoomStatusBar_unsentRetry">
|
||||||
|
{ _t("Retry") }
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
statusBar = <RoomStatusBarUnsentMessages
|
||||||
|
title={_t("Some of your messages have not been sent")}
|
||||||
|
notificationState={StaticNotificationState.RED_EXCLAMATION}
|
||||||
|
buttons={buttons}
|
||||||
|
/>;
|
||||||
|
} else {
|
||||||
|
composer = <MessageComposer
|
||||||
|
room={context.room}
|
||||||
|
resizeNotifier={props.resizeNotifier}
|
||||||
|
permalinkCreator={props.permalinkCreator}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_RoomView mx_RoomView--local">
|
||||||
|
<ErrorBoundary>
|
||||||
|
<RoomHeader
|
||||||
|
room={context.room}
|
||||||
|
searchInfo={null}
|
||||||
|
inRoom={true}
|
||||||
|
onSearchClick={null}
|
||||||
|
onInviteClick={null}
|
||||||
|
onForgetClick={null}
|
||||||
|
e2eStatus={E2EStatus.Normal}
|
||||||
|
onAppsClick={null}
|
||||||
|
appsShown={false}
|
||||||
|
onCallPlaced={null}
|
||||||
|
excludedRightPanelPhaseButtons={[]}
|
||||||
|
showButtons={false}
|
||||||
|
enableRoomOptionsMenu={false}
|
||||||
|
/>
|
||||||
|
<main className="mx_RoomView_body" ref={props.roomView}>
|
||||||
|
<FileDropTarget parent={props.roomView.current} onFileDrop={props.onFileDrop} />
|
||||||
|
<div className="mx_RoomView_timeline">
|
||||||
|
<ScrollPanel
|
||||||
|
className="mx_RoomView_messagePanel"
|
||||||
|
resizeNotifier={props.resizeNotifier}
|
||||||
|
>
|
||||||
|
{ encryptionTile }
|
||||||
|
<NewRoomIntro />
|
||||||
|
</ScrollPanel>
|
||||||
|
</div>
|
||||||
|
{ statusBar }
|
||||||
|
{ composer }
|
||||||
|
</main>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ILocalRoomCreateLoaderProps {
|
||||||
|
names: string;
|
||||||
|
resizeNotifier: ResizeNotifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Room create loader view displaying a message and a spinner.
|
||||||
|
*
|
||||||
|
* @param {ILocalRoomCreateLoaderProps} props Room view props
|
||||||
|
* @return {ReactElement}
|
||||||
|
*/
|
||||||
|
function LocalRoomCreateLoader(props: ILocalRoomCreateLoaderProps): ReactElement {
|
||||||
|
const context = useContext(RoomContext);
|
||||||
|
const text = _t("We're creating a room with %(names)s", { names: props.names });
|
||||||
|
return (
|
||||||
|
<div className="mx_RoomView mx_RoomView--local">
|
||||||
|
<ErrorBoundary>
|
||||||
|
<RoomHeader
|
||||||
|
room={context.room}
|
||||||
|
searchInfo={null}
|
||||||
|
inRoom={true}
|
||||||
|
onSearchClick={null}
|
||||||
|
onInviteClick={null}
|
||||||
|
onForgetClick={null}
|
||||||
|
e2eStatus={E2EStatus.Normal}
|
||||||
|
onAppsClick={null}
|
||||||
|
appsShown={false}
|
||||||
|
onCallPlaced={null}
|
||||||
|
excludedRightPanelPhaseButtons={[]}
|
||||||
|
showButtons={false}
|
||||||
|
enableRoomOptionsMenu={false}
|
||||||
|
/>
|
||||||
|
<div className="mx_RoomView_body">
|
||||||
|
<LargeLoader text={text} />
|
||||||
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
private readonly dispatcherRef: string;
|
private readonly dispatcherRef: string;
|
||||||
private readonly roomStoreToken: EventSubscription;
|
private readonly roomStoreToken: EventSubscription;
|
||||||
|
@ -765,6 +904,11 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
for (const watcher of this.settingWatchers) {
|
for (const watcher of this.settingWatchers) {
|
||||||
SettingsStore.unwatchSetting(watcher);
|
SettingsStore.unwatchSetting(watcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.viewsLocalRoom) {
|
||||||
|
// clean up if this was a local room
|
||||||
|
this.props.mxClient.store.removeRoom(this.state.room.roomId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRightPanelStoreUpdate = () => {
|
private onRightPanelStoreUpdate = () => {
|
||||||
|
@ -873,6 +1017,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
this.onSearchClick();
|
this.onSearchClick();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'local_room_event':
|
||||||
|
this.onLocalRoomEvent(payload.roomId);
|
||||||
|
break;
|
||||||
|
|
||||||
case Action.EditEvent: {
|
case Action.EditEvent: {
|
||||||
// Quit early if we're trying to edit events in wrong rendering context
|
// Quit early if we're trying to edit events in wrong rendering context
|
||||||
if (payload.timelineRenderingType !== this.state.timelineRenderingType) return;
|
if (payload.timelineRenderingType !== this.state.timelineRenderingType) return;
|
||||||
|
@ -925,6 +1073,11 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onLocalRoomEvent(roomId: string) {
|
||||||
|
if (roomId !== this.state.room.roomId) return;
|
||||||
|
createRoomFromLocalRoom(this.props.mxClient, this.state.room as LocalRoom);
|
||||||
|
}
|
||||||
|
|
||||||
private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data) => {
|
private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
|
@ -1494,7 +1647,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
searchResult={result}
|
searchResult={result}
|
||||||
searchHighlights={this.state.searchHighlights}
|
searchHighlights={this.state.searchHighlights}
|
||||||
resultLink={resultLink}
|
resultLink={resultLink}
|
||||||
permalinkCreator={this.getPermalinkCreatorForRoom(room)}
|
permalinkCreator={this.permalinkCreator}
|
||||||
onHeightChanged={onHeightChanged}
|
onHeightChanged={onHeightChanged}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
@ -1769,7 +1922,44 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
this.setState({ narrow });
|
this.setState({ narrow });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private get viewsLocalRoom(): boolean {
|
||||||
|
return isLocalRoom(this.state.room);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get permalinkCreator(): RoomPermalinkCreator {
|
||||||
|
return this.getPermalinkCreatorForRoom(this.state.room);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderLocalRoomCreateLoader(): ReactElement {
|
||||||
|
const names = this.state.room.getDefaultRoomName(this.props.mxClient.getUserId());
|
||||||
|
return <RoomContext.Provider value={this.state}>
|
||||||
|
<LocalRoomCreateLoader
|
||||||
|
names={names}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
/>
|
||||||
|
</RoomContext.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderLocalRoomView(): ReactElement {
|
||||||
|
return <RoomContext.Provider value={this.state}>
|
||||||
|
<LocalRoomView
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
permalinkCreator={this.permalinkCreator}
|
||||||
|
roomView={this.roomView}
|
||||||
|
onFileDrop={this.onFileDrop}
|
||||||
|
/>
|
||||||
|
</RoomContext.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.state.room instanceof LocalRoom) {
|
||||||
|
if (this.state.room.state === LocalRoomState.CREATING) {
|
||||||
|
return this.renderLocalRoomCreateLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.renderLocalRoomView();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.state.room) {
|
if (!this.state.room) {
|
||||||
const loading = !this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading;
|
const loading = !this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading;
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
@ -2027,7 +2217,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
e2eStatus={this.state.e2eStatus}
|
e2eStatus={this.state.e2eStatus}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
replyToEvent={this.state.replyToEvent}
|
replyToEvent={this.state.replyToEvent}
|
||||||
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
|
permalinkCreator={this.permalinkCreator}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2093,7 +2283,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
showUrlPreview={this.state.showUrlPreview}
|
showUrlPreview={this.state.showUrlPreview}
|
||||||
className={this.messagePanelClassNames}
|
className={this.messagePanelClassNames}
|
||||||
membersLoaded={this.state.membersLoaded}
|
membersLoaded={this.state.membersLoaded}
|
||||||
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
|
permalinkCreator={this.permalinkCreator}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
showReactions={true}
|
showReactions={true}
|
||||||
layout={this.state.layout}
|
layout={this.state.layout}
|
||||||
|
@ -2123,7 +2313,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
? <RightPanel
|
? <RightPanel
|
||||||
room={this.state.room}
|
room={this.state.room}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
|
permalinkCreator={this.permalinkCreator}
|
||||||
e2eStatus={this.state.e2eStatus} />
|
e2eStatus={this.state.e2eStatus} />
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
@ -2237,6 +2427,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
appsShown={this.state.showApps}
|
appsShown={this.state.showApps}
|
||||||
onCallPlaced={onCallPlaced}
|
onCallPlaced={onCallPlaced}
|
||||||
excludedRightPanelPhaseButtons={excludedRightPanelPhaseButtons}
|
excludedRightPanelPhaseButtons={excludedRightPanelPhaseButtons}
|
||||||
|
showButtons={!this.viewsLocalRoom}
|
||||||
|
enableRoomOptionsMenu={!this.viewsLocalRoom}
|
||||||
/>
|
/>
|
||||||
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
||||||
<div className={mainSplitContentClasses} ref={this.roomViewBody} data-layout={this.state.layout}>
|
<div className={mainSplitContentClasses} ref={this.roomViewBody} data-layout={this.state.layout}>
|
||||||
|
|
|
@ -62,11 +62,16 @@ import CopyableText from "../elements/CopyableText";
|
||||||
import { ScreenName } from '../../../PosthogTrackers';
|
import { ScreenName } from '../../../PosthogTrackers';
|
||||||
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
||||||
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
|
import { getKeyBindingsManager } from "../../../KeyBindingsManager";
|
||||||
import { DirectoryMember, IDMUserTileProps, Member, ThreepidMember } from "../../../utils/direct-messages";
|
import {
|
||||||
|
DirectoryMember,
|
||||||
|
IDMUserTileProps,
|
||||||
|
Member,
|
||||||
|
startDmOnFirstMessage,
|
||||||
|
ThreepidMember,
|
||||||
|
} from "../../../utils/direct-messages";
|
||||||
import { AnyInviteKind, KIND_CALL_TRANSFER, KIND_DM, KIND_INVITE } from './InviteDialogTypes';
|
import { AnyInviteKind, KIND_CALL_TRANSFER, KIND_DM, KIND_INVITE } from './InviteDialogTypes';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import { startDm } from '../../../utils/dm/startDm';
|
|
||||||
|
|
||||||
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
|
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
@ -446,11 +451,10 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
}
|
}
|
||||||
|
|
||||||
private startDm = async () => {
|
private startDm = async () => {
|
||||||
this.setState({ busy: true });
|
|
||||||
try {
|
try {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const targets = this.convertFilter();
|
const targets = this.convertFilter();
|
||||||
await startDm(cli, targets);
|
startDmOnFirstMessage(cli, targets);
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
@ -458,8 +462,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
|
||||||
busy: false,
|
busy: false,
|
||||||
errorText: _t("We couldn't create your DM."),
|
errorText: _t("We couldn't create your DM."),
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
this.setState({ busy: false });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ import { RecentAlgorithm } from "../../../../stores/room-list/algorithms/tag-sor
|
||||||
import { RoomViewStore } from "../../../../stores/RoomViewStore";
|
import { RoomViewStore } from "../../../../stores/RoomViewStore";
|
||||||
import { getMetaSpaceName } from "../../../../stores/spaces";
|
import { getMetaSpaceName } from "../../../../stores/spaces";
|
||||||
import SpaceStore from "../../../../stores/spaces/SpaceStore";
|
import SpaceStore from "../../../../stores/spaces/SpaceStore";
|
||||||
import { DirectoryMember, Member } from "../../../../utils/direct-messages";
|
import { DirectoryMember, Member, startDmOnFirstMessage } from "../../../../utils/direct-messages";
|
||||||
import DMRoomMap from "../../../../utils/DMRoomMap";
|
import DMRoomMap from "../../../../utils/DMRoomMap";
|
||||||
import { makeUserPermalink } from "../../../../utils/permalinks/Permalinks";
|
import { makeUserPermalink } from "../../../../utils/permalinks/Permalinks";
|
||||||
import { buildActivityScores, buildMemberScores, compareMembers } from "../../../../utils/SortMembers";
|
import { buildActivityScores, buildMemberScores, compareMembers } from "../../../../utils/SortMembers";
|
||||||
|
@ -92,7 +92,6 @@ import { RoomResultContextMenus } from "./RoomResultContextMenus";
|
||||||
import { RoomContextDetails } from "../../rooms/RoomContextDetails";
|
import { RoomContextDetails } from "../../rooms/RoomContextDetails";
|
||||||
import { TooltipOption } from "./TooltipOption";
|
import { TooltipOption } from "./TooltipOption";
|
||||||
import { isLocalRoom } from "../../../../utils/localRoom/isLocalRoom";
|
import { isLocalRoom } from "../../../../utils/localRoom/isLocalRoom";
|
||||||
import { startDm } from "../../../../utils/dm/startDm";
|
|
||||||
|
|
||||||
const MAX_RECENT_SEARCHES = 10;
|
const MAX_RECENT_SEARCHES = 10;
|
||||||
const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons
|
const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons
|
||||||
|
@ -593,7 +592,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
||||||
id={`mx_SpotlightDialog_button_result_${result.member.userId}`}
|
id={`mx_SpotlightDialog_button_result_${result.member.userId}`}
|
||||||
key={`${Section[result.section]}-${result.member.userId}`}
|
key={`${Section[result.section]}-${result.member.userId}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
startDm(cli, [result.member]);
|
startDmOnFirstMessage(cli, [result.member]);
|
||||||
onFinished();
|
onFinished();
|
||||||
}}
|
}}
|
||||||
aria-label={result.member instanceof RoomMember
|
aria-label={result.member instanceof RoomMember
|
||||||
|
|
|
@ -33,7 +33,6 @@ import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import createRoom from '../../../createRoom';
|
|
||||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||||
import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
|
import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
|
@ -78,8 +77,7 @@ import { IRightPanelCardState } from '../../../stores/right-panel/RightPanelStor
|
||||||
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
|
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
|
||||||
import PosthogTrackers from "../../../PosthogTrackers";
|
import PosthogTrackers from "../../../PosthogTrackers";
|
||||||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||||
import { privateShouldBeEncrypted } from "../../../utils/rooms";
|
import { DirectoryMember, startDmOnFirstMessage } from '../../../utils/direct-messages';
|
||||||
import { findDMForUser } from '../../../utils/dm/findDMForUser';
|
|
||||||
|
|
||||||
export interface IDevice {
|
export interface IDevice {
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
|
@ -124,38 +122,13 @@ export const getE2EStatus = (cli: MatrixClient, userId: string, devices: IDevice
|
||||||
return anyDeviceUnverified ? E2EStatus.Warning : E2EStatus.Verified;
|
return anyDeviceUnverified ? E2EStatus.Warning : E2EStatus.Verified;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function openDMForUser(matrixClient: MatrixClient, userId: string, viaKeyboard = false): Promise<void> {
|
async function openDMForUser(matrixClient: MatrixClient, user: RoomMember): Promise<void> {
|
||||||
const lastActiveRoom = findDMForUser(matrixClient, userId);
|
const startDMUser = new DirectoryMember({
|
||||||
|
user_id: user.userId,
|
||||||
if (lastActiveRoom) {
|
display_name: user.rawDisplayName,
|
||||||
dis.dispatch<ViewRoomPayload>({
|
avatar_url: user.getMxcAvatarUrl(),
|
||||||
action: Action.ViewRoom,
|
});
|
||||||
room_id: lastActiveRoom.roomId,
|
startDmOnFirstMessage(matrixClient, [startDMUser]);
|
||||||
metricsTrigger: "MessageUser",
|
|
||||||
metricsViaKeyboard: viaKeyboard,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createRoomOptions = {
|
|
||||||
dmUserId: userId,
|
|
||||||
encryption: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (privateShouldBeEncrypted()) {
|
|
||||||
// Check whether all users have uploaded device keys before.
|
|
||||||
// If so, enable encryption in the new room.
|
|
||||||
const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
|
|
||||||
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
|
|
||||||
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
|
|
||||||
return Object.keys(devices).length > 0;
|
|
||||||
});
|
|
||||||
if (allHaveDeviceKeys) {
|
|
||||||
createRoomOptions.encryption = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await createRoom(createRoomOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetUpdating = (updating: boolean) => void;
|
type SetUpdating = (updating: boolean) => void;
|
||||||
|
@ -328,17 +301,17 @@ function DevicesSection({ devices, userId, loading }: { devices: IDevice[], user
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessageButton = ({ userId }: { userId: string }) => {
|
const MessageButton = ({ member }: { member: RoomMember }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
kind="link"
|
kind="link"
|
||||||
onClick={async (ev) => {
|
onClick={async () => {
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
await openDMForUser(cli, userId, ev.type !== "click");
|
await openDMForUser(cli, member);
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}}
|
}}
|
||||||
className="mx_UserInfo_field"
|
className="mx_UserInfo_field"
|
||||||
|
@ -484,7 +457,7 @@ const UserOptionsSection: React.FC<{
|
||||||
|
|
||||||
let directMessageButton: JSX.Element;
|
let directMessageButton: JSX.Element;
|
||||||
if (!isMe) {
|
if (!isMe) {
|
||||||
directMessageButton = <MessageButton userId={member.userId} />;
|
directMessageButton = <MessageButton member={member} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -948,7 +948,6 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
|
||||||
isSeeingThroughMessageHiddenForModeration,
|
isSeeingThroughMessageHiddenForModeration,
|
||||||
} = getEventDisplayInfo(this.props.mxEvent, this.context.showHiddenEvents, this.shouldHideEvent());
|
} = getEventDisplayInfo(this.props.mxEvent, this.context.showHiddenEvents, this.shouldHideEvent());
|
||||||
const { isQuoteExpanded } = this.state;
|
const { isQuoteExpanded } = this.state;
|
||||||
|
|
||||||
// This shouldn't happen: the caller should check we support this type
|
// This shouldn't happen: the caller should check we support this type
|
||||||
// before trying to instantiate us
|
// before trying to instantiate us
|
||||||
if (!hasRenderer) {
|
if (!hasRenderer) {
|
||||||
|
|
|
@ -3156,6 +3156,7 @@
|
||||||
"You can select all or individual messages to retry or delete": "You can select all or individual messages to retry or delete",
|
"You can select all or individual messages to retry or delete": "You can select all or individual messages to retry or delete",
|
||||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||||
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
||||||
|
"We're creating a room with %(names)s": "We're creating a room with %(names)s",
|
||||||
"You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
|
"You seem to be uploading files, are you sure you want to quit?": "You seem to be uploading files, are you sure you want to quit?",
|
||||||
"You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
|
"You seem to be in a call, are you sure you want to quit?": "You seem to be in a call, are you sure you want to quit?",
|
||||||
"Search failed": "Search failed",
|
"Search failed": "Search failed",
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
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 { render, screen } from "@testing-library/react";
|
||||||
|
|
||||||
|
import { LargeLoader } from "../../../src/components/structures/LargeLoader";
|
||||||
|
|
||||||
|
describe("LargeLoader", () => {
|
||||||
|
const text = "test loading text";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
render(<LargeLoader text={text} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the text", () => {
|
||||||
|
screen.getByText(text);
|
||||||
|
});
|
||||||
|
});
|
|
@ -22,11 +22,13 @@ import { mocked, MockedObject } from "jest-mock";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { EventType } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { MEGOLM_ALGORITHM } from "matrix-js-sdk/src/crypto/olmlib";
|
||||||
|
|
||||||
import { stubClient, mockPlatformPeg, unmockPlatformPeg, wrapInMatrixClientContext } from "../../test-utils";
|
import { stubClient, mockPlatformPeg, unmockPlatformPeg, wrapInMatrixClientContext } from "../../test-utils";
|
||||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||||
import { Action } from "../../../src/dispatcher/actions";
|
import { Action } from "../../../src/dispatcher/actions";
|
||||||
import dis from "../../../src/dispatcher/dispatcher";
|
import { defaultDispatcher } from "../../../src/dispatcher/dispatcher";
|
||||||
import { ViewRoomPayload } from "../../../src/dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||||
import { RoomView as _RoomView } from "../../../src/components/structures/RoomView";
|
import { RoomView as _RoomView } from "../../../src/components/structures/RoomView";
|
||||||
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
|
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
|
||||||
|
@ -37,6 +39,9 @@ import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||||
import { NotificationState } from "../../../src/stores/notifications/NotificationState";
|
import { NotificationState } from "../../../src/stores/notifications/NotificationState";
|
||||||
import RightPanelStore from "../../../src/stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../../../src/stores/right-panel/RightPanelStore";
|
||||||
import { RightPanelPhases } from "../../../src/stores/right-panel/RightPanelStorePhases";
|
import { RightPanelPhases } from "../../../src/stores/right-panel/RightPanelStorePhases";
|
||||||
|
import { LocalRoom, LocalRoomState } from "../../../src/models/LocalRoom";
|
||||||
|
import { DirectoryMember } from "../../../src/utils/direct-messages";
|
||||||
|
import { createDmLocalRoom } from "../../../src/utils/dm/createDmLocalRoom";
|
||||||
|
|
||||||
const RoomView = wrapInMatrixClientContext(_RoomView);
|
const RoomView = wrapInMatrixClientContext(_RoomView);
|
||||||
|
|
||||||
|
@ -44,6 +49,7 @@ describe("RoomView", () => {
|
||||||
let cli: MockedObject<MatrixClient>;
|
let cli: MockedObject<MatrixClient>;
|
||||||
let room: Room;
|
let room: Room;
|
||||||
let roomCount = 0;
|
let roomCount = 0;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
mockPlatformPeg({ reload: () => {} });
|
mockPlatformPeg({ reload: () => {} });
|
||||||
stubClient();
|
stubClient();
|
||||||
|
@ -51,7 +57,7 @@ describe("RoomView", () => {
|
||||||
|
|
||||||
room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
||||||
room.getPendingEvents = () => [];
|
room.getPendingEvents = () => [];
|
||||||
cli.getRoom.mockReturnValue(room);
|
cli.getRoom.mockImplementation(() => room);
|
||||||
// Re-emit certain events on the mocked client
|
// Re-emit certain events on the mocked client
|
||||||
room.on(RoomEvent.Timeline, (...args) => cli.emit(RoomEvent.Timeline, ...args));
|
room.on(RoomEvent.Timeline, (...args) => cli.emit(RoomEvent.Timeline, ...args));
|
||||||
room.on(RoomEvent.TimelineReset, (...args) => cli.emit(RoomEvent.TimelineReset, ...args));
|
room.on(RoomEvent.TimelineReset, (...args) => cli.emit(RoomEvent.TimelineReset, ...args));
|
||||||
|
@ -76,7 +82,7 @@ describe("RoomView", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dis.dispatch<ViewRoomPayload>({
|
defaultDispatcher.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
room_id: room.roomId,
|
room_id: room.roomId,
|
||||||
metricsTrigger: null,
|
metricsTrigger: null,
|
||||||
|
@ -164,4 +170,82 @@ describe("RoomView", () => {
|
||||||
expect(RightPanelStore.instance.currentCard.phase).toEqual(RightPanelPhases.Timeline);
|
expect(RightPanelStore.instance.currentCard.phase).toEqual(RightPanelPhases.Timeline);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("for a local room", () => {
|
||||||
|
let localRoom: LocalRoom;
|
||||||
|
let roomView: ReactWrapper;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
localRoom = room = await createDmLocalRoom(cli, [new DirectoryMember({ user_id: "@user:example.com" })]);
|
||||||
|
cli.store.storeRoom(room);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should remove the room from the store on unmount", async () => {
|
||||||
|
roomView = await mountRoomView();
|
||||||
|
roomView.unmount();
|
||||||
|
expect(cli.store.removeRoom).toHaveBeenCalledWith(room.roomId);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("in state NEW", () => {
|
||||||
|
it("should match the snapshot", async () => {
|
||||||
|
roomView = await mountRoomView();
|
||||||
|
expect(roomView.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("that is encrypted", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocked(cli.isRoomEncrypted).mockReturnValue(true);
|
||||||
|
localRoom.encrypted = true;
|
||||||
|
localRoom.currentState.setStateEvents([
|
||||||
|
new MatrixEvent({
|
||||||
|
event_id: `~${localRoom.roomId}:${cli.makeTxnId()}`,
|
||||||
|
type: EventType.RoomEncryption,
|
||||||
|
content: {
|
||||||
|
algorithm: MEGOLM_ALGORITHM,
|
||||||
|
},
|
||||||
|
user_id: cli.getUserId(),
|
||||||
|
sender: cli.getUserId(),
|
||||||
|
state_key: "",
|
||||||
|
room_id: localRoom.roomId,
|
||||||
|
origin_server_ts: Date.now(),
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should match the snapshot", async () => {
|
||||||
|
const roomView = await mountRoomView();
|
||||||
|
expect(roomView.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("in state CREATING should match the snapshot", async () => {
|
||||||
|
localRoom.state = LocalRoomState.CREATING;
|
||||||
|
roomView = await mountRoomView();
|
||||||
|
expect(roomView.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("in state ERROR", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
localRoom.state = LocalRoomState.ERROR;
|
||||||
|
roomView = await mountRoomView();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should match the snapshot", async () => {
|
||||||
|
expect(roomView.html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clicking retry should set the room state to new dispatch a local room event", () => {
|
||||||
|
jest.spyOn(defaultDispatcher, "dispatch");
|
||||||
|
roomView.findWhere((w: ReactWrapper) => {
|
||||||
|
return w.hasClass("mx_RoomStatusBar_unsentRetry") && w.text() === "Retry";
|
||||||
|
}).first().simulate("click");
|
||||||
|
expect(localRoom.state).toBe(LocalRoomState.NEW);
|
||||||
|
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||||
|
action: "local_room_event",
|
||||||
|
roomId: room.roomId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`RoomView for a local room in state CREATING should match the snapshot 1`] = `"<div class=\\"mx_RoomView mx_RoomView--local\\"><header class=\\"mx_RoomHeader light-panel\\"><div class=\\"mx_RoomHeader_wrapper\\"><div class=\\"mx_RoomHeader_avatar\\"><div class=\\"mx_DecoratedRoomAvatar\\"><span class=\\"mx_BaseAvatar\\" role=\\"presentation\\"><span class=\\"mx_BaseAvatar_initial\\" aria-hidden=\\"true\\" style=\\"font-size: 15.600000000000001px; width: 24px; line-height: 24px;\\">U</span><img class=\\"mx_BaseAvatar_image\\" src=\\"data:image/png;base64,00\\" alt=\\"\\" style=\\"width: 24px; height: 24px;\\" aria-hidden=\\"true\\"></span></div></div><div class=\\"mx_RoomHeader_e2eIcon\\"><div class=\\"mx_E2EIcon mx_E2EIcon_normal\\"></div></div><div class=\\"mx_RoomHeader_name mx_RoomHeader_name--textonly\\"><div dir=\\"auto\\" class=\\"mx_RoomHeader_nametext\\" title=\\"@user:example.com\\" role=\\"heading\\" aria-level=\\"1\\">@user:example.com</div></div><div class=\\"mx_RoomHeader_topic mx_RoomTopic\\" dir=\\"auto\\"><div tabindex=\\"0\\"><div><span dir=\\"auto\\"></span></div></div></div></div></header><div class=\\"mx_RoomView_body\\"><div class=\\"mx_LargeLoader\\"><div class=\\"mx_Spinner\\"><div class=\\"mx_Spinner_icon\\" style=\\"width: 45px; height: 45px;\\" aria-label=\\"Loading...\\" role=\\"progressbar\\"></div></div><div class=\\"mx_LargeLoader_text\\">We're creating a room with @user:example.com</div></div></div></div>"`;
|
||||||
|
|
||||||
|
exports[`RoomView for a local room in state ERROR should match the snapshot 1`] = `"<div class=\\"mx_RoomView mx_RoomView--local\\"><header class=\\"mx_RoomHeader light-panel\\"><div class=\\"mx_RoomHeader_wrapper\\"><div class=\\"mx_RoomHeader_avatar\\"><div class=\\"mx_DecoratedRoomAvatar\\"><span class=\\"mx_BaseAvatar\\" role=\\"presentation\\"><span class=\\"mx_BaseAvatar_initial\\" aria-hidden=\\"true\\" style=\\"font-size: 15.600000000000001px; width: 24px; line-height: 24px;\\">U</span><img class=\\"mx_BaseAvatar_image\\" src=\\"data:image/png;base64,00\\" alt=\\"\\" style=\\"width: 24px; height: 24px;\\" aria-hidden=\\"true\\"></span></div></div><div class=\\"mx_RoomHeader_e2eIcon\\"><div class=\\"mx_E2EIcon mx_E2EIcon_normal\\"></div></div><div class=\\"mx_RoomHeader_name mx_RoomHeader_name--textonly\\"><div dir=\\"auto\\" class=\\"mx_RoomHeader_nametext\\" title=\\"@user:example.com\\" role=\\"heading\\" aria-level=\\"1\\">@user:example.com</div></div><div class=\\"mx_RoomHeader_topic mx_RoomTopic\\" dir=\\"auto\\"><div tabindex=\\"0\\"><div><span dir=\\"auto\\"></span></div></div></div></div></header><main class=\\"mx_RoomView_body\\"><div class=\\"mx_RoomView_timeline\\"><div class=\\"mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel\\" tabindex=\\"-1\\"><div class=\\"mx_RoomView_messageListWrapper\\"><ol class=\\"mx_RoomView_MessageList\\" aria-live=\\"polite\\" style=\\"height: 400px;\\"><li class=\\"mx_NewRoomIntro\\"><div class=\\"mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon_warning\\"><div class=\\"mx_EventTileBubble_title\\">End-to-end encryption isn't enabled</div><div class=\\"mx_EventTileBubble_subtitle\\"><span> Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. </span></div></div><span aria-label=\\"Avatar\\" aria-live=\\"off\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_BaseAvatar\\"><span class=\\"mx_BaseAvatar_initial\\" aria-hidden=\\"true\\" style=\\"font-size: 33.800000000000004px; width: 52px; line-height: 52px;\\">U</span><img class=\\"mx_BaseAvatar_image\\" src=\\"data:image/png;base64,00\\" alt=\\"\\" style=\\"width: 52px; height: 52px;\\" aria-hidden=\\"true\\"></span><h2>@user:example.com</h2><p><span>Send your first message to invite <b>@user:example.com</b> to chat</span></p></li></ol></div></div></div><div class=\\"mx_RoomStatusBar mx_RoomStatusBar_unsentMessages\\"><div role=\\"alert\\"><div class=\\"mx_RoomStatusBar_unsentBadge\\"><div class=\\"mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_highlighted mx_NotificationBadge_2char\\"><span class=\\"mx_NotificationBadge_count\\">!</span></div></div><div><div class=\\"mx_RoomStatusBar_unsentTitle\\">Some of your messages have not been sent</div></div><div class=\\"mx_RoomStatusBar_unsentButtonBar\\"><div role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_RoomStatusBar_unsentRetry\\">Retry</div></div></div></div></main></div>"`;
|
||||||
|
|
||||||
|
exports[`RoomView for a local room in state NEW should match the snapshot 1`] = `"<div class=\\"mx_RoomView mx_RoomView--local\\"><header class=\\"mx_RoomHeader light-panel\\"><div class=\\"mx_RoomHeader_wrapper\\"><div class=\\"mx_RoomHeader_avatar\\"><div class=\\"mx_DecoratedRoomAvatar\\"><span class=\\"mx_BaseAvatar\\" role=\\"presentation\\"><span class=\\"mx_BaseAvatar_initial\\" aria-hidden=\\"true\\" style=\\"font-size: 15.600000000000001px; width: 24px; line-height: 24px;\\">U</span><img class=\\"mx_BaseAvatar_image\\" src=\\"data:image/png;base64,00\\" alt=\\"\\" style=\\"width: 24px; height: 24px;\\" aria-hidden=\\"true\\"></span></div></div><div class=\\"mx_RoomHeader_e2eIcon\\"><div class=\\"mx_E2EIcon mx_E2EIcon_normal\\"></div></div><div class=\\"mx_RoomHeader_name mx_RoomHeader_name--textonly\\"><div dir=\\"auto\\" class=\\"mx_RoomHeader_nametext\\" title=\\"@user:example.com\\" role=\\"heading\\" aria-level=\\"1\\">@user:example.com</div></div><div class=\\"mx_RoomHeader_topic mx_RoomTopic\\" dir=\\"auto\\"><div tabindex=\\"0\\"><div><span dir=\\"auto\\"></span></div></div></div></div></header><main class=\\"mx_RoomView_body\\"><div class=\\"mx_RoomView_timeline\\"><div class=\\"mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel\\" tabindex=\\"-1\\"><div class=\\"mx_RoomView_messageListWrapper\\"><ol class=\\"mx_RoomView_MessageList\\" aria-live=\\"polite\\" style=\\"height: 400px;\\"><li class=\\"mx_NewRoomIntro\\"><div class=\\"mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon_warning\\"><div class=\\"mx_EventTileBubble_title\\">End-to-end encryption isn't enabled</div><div class=\\"mx_EventTileBubble_subtitle\\"><span> Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. </span></div></div><span aria-label=\\"Avatar\\" aria-live=\\"off\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_BaseAvatar\\"><span class=\\"mx_BaseAvatar_initial\\" aria-hidden=\\"true\\" style=\\"font-size: 33.800000000000004px; width: 52px; line-height: 52px;\\">U</span><img class=\\"mx_BaseAvatar_image\\" src=\\"data:image/png;base64,00\\" alt=\\"\\" style=\\"width: 52px; height: 52px;\\" aria-hidden=\\"true\\"></span><h2>@user:example.com</h2><p><span>Send your first message to invite <b>@user:example.com</b> to chat</span></p></li></ol></div></div></div><div class=\\"mx_MessageComposer\\"><div class=\\"mx_MessageComposer_wrapper\\"><div class=\\"mx_MessageComposer_row\\"><div class=\\"mx_SendMessageComposer\\"><div class=\\"mx_BasicMessageComposer\\"><div class=\\"mx_MessageComposerFormatBar\\"><button type=\\"button\\" aria-label=\\"Bold\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconBold\\"></button><button type=\\"button\\" aria-label=\\"Italics\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconItalic\\"></button><button type=\\"button\\" aria-label=\\"Strikethrough\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconStrikethrough\\"></button><button type=\\"button\\" aria-label=\\"Code block\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconCode\\"></button><button type=\\"button\\" aria-label=\\"Quote\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconQuote\\"></button><button type=\\"button\\" aria-label=\\"Insert link\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconInsertLink\\"></button></div><div class=\\"mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar mx_BasicMessageComposer_inputEmpty\\" contenteditable=\\"true\\" tabindex=\\"0\\" aria-label=\\"Send a message…\\" role=\\"textbox\\" aria-multiline=\\"true\\" aria-autocomplete=\\"list\\" aria-haspopup=\\"listbox\\" dir=\\"auto\\" aria-disabled=\\"false\\" style=\\"--placeholder: 'Send a message…';\\"><div><br></div></div></div></div><div aria-label=\\"Emoji\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_emoji\\"></div><div aria-label=\\"Attachment\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_upload\\"></div><div aria-label=\\"More options\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu\\"></div><input type=\\"file\\" style=\\"display: none;\\" multiple=\\"\\"></div></div></div></main></div>"`;
|
||||||
|
|
||||||
|
exports[`RoomView for a local room in state NEW that is encrypted should match the snapshot 1`] = `"<div class=\\"mx_RoomView mx_RoomView--local\\"><header class=\\"mx_RoomHeader light-panel\\"><div class=\\"mx_RoomHeader_wrapper\\"><div class=\\"mx_RoomHeader_avatar\\"><div class=\\"mx_DecoratedRoomAvatar\\"><span class=\\"mx_BaseAvatar\\" role=\\"presentation\\"><span class=\\"mx_BaseAvatar_initial\\" aria-hidden=\\"true\\" style=\\"font-size: 15.600000000000001px; width: 24px; line-height: 24px;\\">U</span><img class=\\"mx_BaseAvatar_image\\" src=\\"data:image/png;base64,00\\" alt=\\"\\" style=\\"width: 24px; height: 24px;\\" aria-hidden=\\"true\\"></span></div></div><div class=\\"mx_RoomHeader_e2eIcon\\"><div class=\\"mx_E2EIcon mx_E2EIcon_normal\\"></div></div><div class=\\"mx_RoomHeader_name mx_RoomHeader_name--textonly\\"><div dir=\\"auto\\" class=\\"mx_RoomHeader_nametext\\" title=\\"@user:example.com\\" role=\\"heading\\" aria-level=\\"1\\">@user:example.com</div></div><div class=\\"mx_RoomHeader_topic mx_RoomTopic\\" dir=\\"auto\\"><div tabindex=\\"0\\"><div><span dir=\\"auto\\"></span></div></div></div></div></header><main class=\\"mx_RoomView_body\\"><div class=\\"mx_RoomView_timeline\\"><div class=\\"mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel\\" tabindex=\\"-1\\"><div class=\\"mx_RoomView_messageListWrapper\\"><ol class=\\"mx_RoomView_MessageList\\" aria-live=\\"polite\\" style=\\"height: 400px;\\"><div class=\\"mx_EventTileBubble mx_cryptoEvent mx_cryptoEvent_icon\\"><div class=\\"mx_EventTileBubble_title\\">Encryption enabled</div><div class=\\"mx_EventTileBubble_subtitle\\">Messages in this chat will be end-to-end encrypted.</div></div><li class=\\"mx_NewRoomIntro\\"><span aria-label=\\"Avatar\\" aria-live=\\"off\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_BaseAvatar\\"><span class=\\"mx_BaseAvatar_initial\\" aria-hidden=\\"true\\" style=\\"font-size: 33.800000000000004px; width: 52px; line-height: 52px;\\">U</span><img class=\\"mx_BaseAvatar_image\\" src=\\"data:image/png;base64,00\\" alt=\\"\\" style=\\"width: 52px; height: 52px;\\" aria-hidden=\\"true\\"></span><h2>@user:example.com</h2><p><span>Send your first message to invite <b>@user:example.com</b> to chat</span></p></li></ol></div></div></div><div class=\\"mx_MessageComposer\\"><div class=\\"mx_MessageComposer_wrapper\\"><div class=\\"mx_MessageComposer_row\\"><div class=\\"mx_SendMessageComposer\\"><div class=\\"mx_BasicMessageComposer\\"><div class=\\"mx_MessageComposerFormatBar\\"><button type=\\"button\\" aria-label=\\"Bold\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconBold\\"></button><button type=\\"button\\" aria-label=\\"Italics\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconItalic\\"></button><button type=\\"button\\" aria-label=\\"Strikethrough\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconStrikethrough\\"></button><button type=\\"button\\" aria-label=\\"Code block\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconCode\\"></button><button type=\\"button\\" aria-label=\\"Quote\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconQuote\\"></button><button type=\\"button\\" aria-label=\\"Insert link\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconInsertLink\\"></button></div><div class=\\"mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar mx_BasicMessageComposer_inputEmpty\\" contenteditable=\\"true\\" tabindex=\\"0\\" aria-label=\\"Send a message…\\" role=\\"textbox\\" aria-multiline=\\"true\\" aria-autocomplete=\\"list\\" aria-haspopup=\\"listbox\\" dir=\\"auto\\" aria-disabled=\\"false\\" style=\\"--placeholder: 'Send a message…';\\"><div><br></div></div></div></div><div aria-label=\\"Emoji\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_emoji\\"></div><div aria-label=\\"Attachment\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_upload\\"></div><div aria-label=\\"More options\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu\\"></div><input type=\\"file\\" style=\\"display: none;\\" multiple=\\"\\"></div></div></div></main></div>"`;
|
|
@ -26,6 +26,7 @@ import sanitizeHtml from "sanitize-html";
|
||||||
import SpotlightDialog, { Filter } from "../../../../src/components/views/dialogs/spotlight/SpotlightDialog";
|
import SpotlightDialog, { Filter } from "../../../../src/components/views/dialogs/spotlight/SpotlightDialog";
|
||||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from "../../../../src/models/LocalRoom";
|
import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from "../../../../src/models/LocalRoom";
|
||||||
|
import { DirectoryMember, startDmOnFirstMessage } from "../../../../src/utils/direct-messages";
|
||||||
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
import DMRoomMap from "../../../../src/utils/DMRoomMap";
|
||||||
import { mkRoom, stubClient } from "../../../test-utils";
|
import { mkRoom, stubClient } from "../../../test-utils";
|
||||||
|
|
||||||
|
@ -346,4 +347,27 @@ describe("Spotlight Dialog", () => {
|
||||||
expect(options.first().text()).not.toContain(testLocalRoom.name);
|
expect(options.first().text()).not.toContain(testLocalRoom.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should start a DM when clicking a person", async () => {
|
||||||
|
const wrapper = mount(
|
||||||
|
<SpotlightDialog
|
||||||
|
initialFilter={Filter.People}
|
||||||
|
initialText={testPerson.display_name}
|
||||||
|
onFinished={() => null} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await sleep(200);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
const options = wrapper.find("div.mx_SpotlightDialog_option");
|
||||||
|
expect(options.length).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(options.first().text()).toContain(testPerson.display_name);
|
||||||
|
|
||||||
|
options.first().simulate("click");
|
||||||
|
expect(startDmOnFirstMessage).toHaveBeenCalledWith(mockedClient, [new DirectoryMember(testPerson)]);
|
||||||
|
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue