Start DM on first message (#8612)

This commit is contained in:
Michael Weimann 2022-08-04 08:19:52 +02:00 committed by GitHub
parent 0e0be08781
commit ed8ccb5d80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 482 additions and 65 deletions

View file

@ -20,7 +20,7 @@ limitations under the License.
// TODO: This component is enormous! There's several things which could stand-alone:
// - Search results component
import React, { createRef } from 'react';
import React, { createRef, ReactElement, ReactNode, RefObject, useContext } from 'react';
import classNames from 'classnames';
import { IRecommendedVersion, NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room";
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 Modal from '../../Modal';
import CallHandler, { CallHandlerEvent } from '../../CallHandler';
import dis from '../../dispatcher/dispatcher';
import dis, { defaultDispatcher } from '../../dispatcher/dispatcher';
import * as Rooms from '../../Rooms';
import eventSearch, { searchPagination } from '../../Searching';
import MainSplit from './MainSplit';
@ -110,7 +110,15 @@ import FileDropTarget from './FileDropTarget';
import Measured from '../views/elements/Measured';
import { FocusComposerPayload } from '../../dispatcher/payloads/FocusComposerPayload';
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 { RoomStatusBarUnsentMessages } from './RoomStatusBarUnsentMessages';
import { LargeLoader } from './LargeLoader';
const DEBUG = false;
let debuglog = function(msg: string) {};
@ -222,6 +230,137 @@ export interface IRoomState {
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> {
private readonly dispatcherRef: string;
private readonly roomStoreToken: EventSubscription;
@ -765,6 +904,11 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
for (const watcher of this.settingWatchers) {
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 = () => {
@ -873,6 +1017,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
this.onSearchClick();
break;
case 'local_room_event':
this.onLocalRoomEvent(payload.roomId);
break;
case Action.EditEvent: {
// Quit early if we're trying to edit events in wrong rendering context
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) => {
if (this.unmounted) return;
@ -1494,7 +1647,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
searchResult={result}
searchHighlights={this.state.searchHighlights}
resultLink={resultLink}
permalinkCreator={this.getPermalinkCreatorForRoom(room)}
permalinkCreator={this.permalinkCreator}
onHeightChanged={onHeightChanged}
/>);
}
@ -1769,7 +1922,44 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
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() {
if (this.state.room instanceof LocalRoom) {
if (this.state.room.state === LocalRoomState.CREATING) {
return this.renderLocalRoomCreateLoader();
}
return this.renderLocalRoomView();
}
if (!this.state.room) {
const loading = !this.state.matrixClientIsReady || this.state.roomLoading || this.state.peekLoading;
if (loading) {
@ -2027,7 +2217,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
e2eStatus={this.state.e2eStatus}
resizeNotifier={this.props.resizeNotifier}
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}
className={this.messagePanelClassNames}
membersLoaded={this.state.membersLoaded}
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
permalinkCreator={this.permalinkCreator}
resizeNotifier={this.props.resizeNotifier}
showReactions={true}
layout={this.state.layout}
@ -2123,7 +2313,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
? <RightPanel
room={this.state.room}
resizeNotifier={this.props.resizeNotifier}
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)}
permalinkCreator={this.permalinkCreator}
e2eStatus={this.state.e2eStatus} />
: null;
@ -2237,6 +2427,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
appsShown={this.state.showApps}
onCallPlaced={onCallPlaced}
excludedRightPanelPhaseButtons={excludedRightPanelPhaseButtons}
showButtons={!this.viewsLocalRoom}
enableRoomOptionsMenu={!this.viewsLocalRoom}
/>
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
<div className={mainSplitContentClasses} ref={this.roomViewBody} data-layout={this.state.layout}>