Allow quote-reply in thread view element-web (#6959)
This commit is contained in:
parent
d39002338d
commit
694ec946e2
11 changed files with 84 additions and 88 deletions
|
@ -779,7 +779,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'reply_to_event':
|
case 'reply_to_event':
|
||||||
if (this.state.searchResults && payload.event.getRoomId() === this.state.roomId && !this.unmounted) {
|
if (this.state.searchResults
|
||||||
|
&& payload.event.getRoomId() === this.state.roomId
|
||||||
|
&& !this.unmounted
|
||||||
|
&& payload.context === TimelineRenderingType.Room) {
|
||||||
this.onCancelSearchClick();
|
this.onCancelSearchClick();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
|
||||||
import { Thread, ThreadEvent } from 'matrix-js-sdk/src/models/thread';
|
import { Thread, ThreadEvent } from 'matrix-js-sdk/src/models/thread';
|
||||||
import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set';
|
import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set';
|
||||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||||
|
@ -24,7 +23,6 @@ import BaseCard from "../views/right_panel/BaseCard";
|
||||||
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
|
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
|
||||||
|
|
||||||
import ResizeNotifier from '../../utils/ResizeNotifier';
|
import ResizeNotifier from '../../utils/ResizeNotifier';
|
||||||
import EventTile, { TileShape } from '../views/rooms/EventTile';
|
|
||||||
import MatrixClientContext from '../../contexts/MatrixClientContext';
|
import MatrixClientContext from '../../contexts/MatrixClientContext';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import { ContextMenuButton } from '../../accessibility/context_menu/ContextMenuButton';
|
import { ContextMenuButton } from '../../accessibility/context_menu/ContextMenuButton';
|
||||||
|
@ -34,6 +32,7 @@ import TimelinePanel from './TimelinePanel';
|
||||||
import { Layout } from '../../settings/Layout';
|
import { Layout } from '../../settings/Layout';
|
||||||
import { useEventEmitter } from '../../hooks/useEventEmitter';
|
import { useEventEmitter } from '../../hooks/useEventEmitter';
|
||||||
import AccessibleButton from '../views/elements/AccessibleButton';
|
import AccessibleButton from '../views/elements/AccessibleButton';
|
||||||
|
import { TileShape } from '../views/rooms/EventTile';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
@ -41,18 +40,6 @@ interface IProps {
|
||||||
resizeNotifier: ResizeNotifier;
|
resizeNotifier: ResizeNotifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ThreadPanelItem: React.FC<{ event: MatrixEvent }> = ({ event }) => {
|
|
||||||
return <EventTile
|
|
||||||
key={event.getId()}
|
|
||||||
mxEvent={event}
|
|
||||||
enableFlair={false}
|
|
||||||
showReadReceipts={false}
|
|
||||||
as="div"
|
|
||||||
tileShape={TileShape.Thread}
|
|
||||||
alwaysShowTimestamps={true}
|
|
||||||
/>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum ThreadFilterType {
|
export enum ThreadFilterType {
|
||||||
"My",
|
"My",
|
||||||
"All"
|
"All"
|
||||||
|
@ -230,7 +217,7 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose }) => {
|
||||||
showReactions={true}
|
showReactions={true}
|
||||||
className="mx_RoomView_messagePanel mx_GroupLayout"
|
className="mx_RoomView_messagePanel mx_GroupLayout"
|
||||||
membersLoaded={true}
|
membersLoaded={true}
|
||||||
tileShape={TileShape.ThreadPanel}
|
tileShape={TileShape.Thread}
|
||||||
/>
|
/>
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
</RoomContext.Provider>
|
</RoomContext.Provider>
|
||||||
|
|
|
@ -50,6 +50,7 @@ interface IProps {
|
||||||
interface IState {
|
interface IState {
|
||||||
thread?: Thread;
|
thread?: Thread;
|
||||||
editState?: EditorStateTransfer;
|
editState?: EditorStateTransfer;
|
||||||
|
replyToEvent?: MatrixEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("structures.ThreadView")
|
@replaceableComponent("structures.ThreadView")
|
||||||
|
@ -114,6 +115,13 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'reply_to_event':
|
||||||
|
if (payload.context === TimelineRenderingType.Thread) {
|
||||||
|
this.setState({
|
||||||
|
replyToEvent: payload.event,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -199,7 +207,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
||||||
rel_type: RelationType.Thread,
|
rel_type: RelationType.Thread,
|
||||||
event_id: this.state.thread.id,
|
event_id: this.state.thread.id,
|
||||||
}}
|
}}
|
||||||
showReplyPreview={false}
|
replyToEvent={this.state.replyToEvent}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
e2eStatus={this.props.e2eStatus}
|
e2eStatus={this.props.e2eStatus}
|
||||||
compact={true}
|
compact={true}
|
||||||
|
|
|
@ -63,7 +63,7 @@ const OptionsButton: React.FC<IOptionsButtonProps> =
|
||||||
let contextMenu;
|
let contextMenu;
|
||||||
if (menuDisplayed) {
|
if (menuDisplayed) {
|
||||||
const tile = getTile && getTile();
|
const tile = getTile && getTile();
|
||||||
const replyThread = getReplyChain && getReplyChain();
|
const replyChain = getReplyChain && getReplyChain();
|
||||||
|
|
||||||
const buttonRect = button.current.getBoundingClientRect();
|
const buttonRect = button.current.getBoundingClientRect();
|
||||||
contextMenu = <MessageContextMenu
|
contextMenu = <MessageContextMenu
|
||||||
|
@ -71,7 +71,7 @@ const OptionsButton: React.FC<IOptionsButtonProps> =
|
||||||
mxEvent={mxEvent}
|
mxEvent={mxEvent}
|
||||||
permalinkCreator={permalinkCreator}
|
permalinkCreator={permalinkCreator}
|
||||||
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
|
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
|
||||||
collapseReplyChain={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
|
collapseReplyChain={replyChain && replyChain.canCollapse() ? replyChain.collapse : undefined}
|
||||||
onFinished={closeMenu}
|
onFinished={closeMenu}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
@ -191,6 +191,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'reply_to_event',
|
action: 'reply_to_event',
|
||||||
event: this.props.mxEvent,
|
event: this.props.mxEvent,
|
||||||
|
context: this.context.timelineRenderingType,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -289,7 +290,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
// Like the resend button, the react and reply buttons need to appear before the edit.
|
// Like the resend button, the react and reply buttons need to appear before the edit.
|
||||||
// The only catch is we do the reply button first so that we can make sure the react
|
// The only catch is we do the reply button first so that we can make sure the react
|
||||||
// button is the very first button without having to do length checks for `splice()`.
|
// button is the very first button without having to do length checks for `splice()`.
|
||||||
if (this.context.canReply && this.context.timelineRenderingType !== TimelineRenderingType.Thread) {
|
if (this.context.canReply) {
|
||||||
toolbarOpts.splice(0, 0, <>
|
toolbarOpts.splice(0, 0, <>
|
||||||
<RovingAccessibleTooltipButton
|
<RovingAccessibleTooltipButton
|
||||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_replyButton"
|
className="mx_MessageActionBar_maskButton mx_MessageActionBar_replyButton"
|
||||||
|
@ -297,7 +298,8 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
||||||
onClick={this.onReplyClick}
|
onClick={this.onReplyClick}
|
||||||
key="reply"
|
key="reply"
|
||||||
/>
|
/>
|
||||||
{ SettingsStore.getValue("feature_thread") && (
|
{ (SettingsStore.getValue("feature_thread")
|
||||||
|
&& this.context.timelineRenderingType !== TimelineRenderingType.Thread) && (
|
||||||
<RovingAccessibleTooltipButton
|
<RovingAccessibleTooltipButton
|
||||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
|
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
|
||||||
title={_t("Thread")}
|
title={_t("Thread")}
|
||||||
|
|
|
@ -339,7 +339,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
private isListeningForReceipts: boolean;
|
private isListeningForReceipts: boolean;
|
||||||
// TODO: Types
|
// TODO: Types
|
||||||
private tile = React.createRef<unknown>();
|
private tile = React.createRef<unknown>();
|
||||||
private replyThread = React.createRef<ReplyChain>();
|
private replyChain = React.createRef<ReplyChain>();
|
||||||
|
|
||||||
public readonly ref = createRef<HTMLElement>();
|
public readonly ref = createRef<HTMLElement>();
|
||||||
|
|
||||||
|
@ -933,7 +933,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
// TODO: Types
|
// TODO: Types
|
||||||
getTile: () => any | null = () => this.tile.current;
|
getTile: () => any | null = () => this.tile.current;
|
||||||
|
|
||||||
getReplyChain = () => this.replyThread.current;
|
getReplyChain = () => this.replyChain.current;
|
||||||
|
|
||||||
getReactions = () => {
|
getReactions = () => {
|
||||||
if (
|
if (
|
||||||
|
@ -1214,12 +1214,26 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
case TileShape.Thread: {
|
case TileShape.Thread: {
|
||||||
|
const thread = haveTileForEvent(this.props.mxEvent) &&
|
||||||
|
ReplyChain.hasReply(this.props.mxEvent) ? (
|
||||||
|
<ReplyChain
|
||||||
|
parentEv={this.props.mxEvent}
|
||||||
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
|
ref={this.replyChain}
|
||||||
|
forExport={this.props.forExport}
|
||||||
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
|
layout={this.props.layout}
|
||||||
|
alwaysShowTimestamps={this.props.alwaysShowTimestamps || this.state.hover}
|
||||||
|
isQuoteExpanded={isQuoteExpanded}
|
||||||
|
setQuoteExpanded={this.setQuoteExpanded}
|
||||||
|
/>) : null;
|
||||||
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
||||||
return React.createElement(this.props.as || "li", {
|
return React.createElement(this.props.as || "li", {
|
||||||
"className": classes,
|
"className": classes,
|
||||||
"aria-live": ariaLive,
|
"aria-live": ariaLive,
|
||||||
"aria-atomic": true,
|
"aria-atomic": true,
|
||||||
"data-scroll-tokens": scrollToken,
|
"data-scroll-tokens": scrollToken,
|
||||||
|
"data-has-reply": !!thread,
|
||||||
}, [
|
}, [
|
||||||
<div className="mx_EventTile_roomName" key="mx_EventTile_roomName">
|
<div className="mx_EventTile_roomName" key="mx_EventTile_roomName">
|
||||||
<RoomAvatar room={room} width={28} height={28} />
|
<RoomAvatar room={room} width={28} height={28} />
|
||||||
|
@ -1235,6 +1249,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
</a>
|
</a>
|
||||||
</div>,
|
</div>,
|
||||||
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
<div className="mx_EventTile_line" key="mx_EventTile_line">
|
||||||
|
{ thread }
|
||||||
<EventTileType ref={this.tile}
|
<EventTileType ref={this.tile}
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={this.props.mxEvent}
|
||||||
highlights={this.props.highlights}
|
highlights={this.props.highlights}
|
||||||
|
@ -1287,7 +1302,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
<ReplyChain
|
<ReplyChain
|
||||||
parentEv={this.props.mxEvent}
|
parentEv={this.props.mxEvent}
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
ref={this.replyThread}
|
ref={this.replyChain}
|
||||||
forExport={this.props.forExport}
|
forExport={this.props.forExport}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
layout={this.props.layout}
|
layout={this.props.layout}
|
||||||
|
|
|
@ -55,6 +55,7 @@ import UIStore, { UI_EVENTS } from '../../../stores/UIStore';
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import InfoDialog from "../dialogs/InfoDialog";
|
import InfoDialog from "../dialogs/InfoDialog";
|
||||||
import { RelationType } from 'matrix-js-sdk/src/@types/event';
|
import { RelationType } from 'matrix-js-sdk/src/@types/event';
|
||||||
|
import RoomContext from '../../../contexts/RoomContext';
|
||||||
|
|
||||||
let instanceCount = 0;
|
let instanceCount = 0;
|
||||||
const NARROW_MODE_BREAKPOINT = 500;
|
const NARROW_MODE_BREAKPOINT = 500;
|
||||||
|
@ -227,7 +228,6 @@ interface IProps {
|
||||||
permalinkCreator: RoomPermalinkCreator;
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
replyToEvent?: MatrixEvent;
|
replyToEvent?: MatrixEvent;
|
||||||
relation?: IEventRelation;
|
relation?: IEventRelation;
|
||||||
showReplyPreview?: boolean;
|
|
||||||
e2eStatus?: E2EStatus;
|
e2eStatus?: E2EStatus;
|
||||||
compact?: boolean;
|
compact?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -252,8 +252,9 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
private ref: React.RefObject<HTMLDivElement> = createRef();
|
private ref: React.RefObject<HTMLDivElement> = createRef();
|
||||||
private instanceId: number;
|
private instanceId: number;
|
||||||
|
|
||||||
|
public static contextType = RoomContext;
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
showReplyPreview: true,
|
|
||||||
compact: false,
|
compact: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -294,7 +295,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload) => {
|
||||||
if (payload.action === 'reply_to_event') {
|
if (payload.action === 'reply_to_event' && payload.context === this.context.timelineRenderingType) {
|
||||||
// add a timeout for the reply preview to be rendered, so
|
// add a timeout for the reply preview to be rendered, so
|
||||||
// that the ScrollPanel listening to the resizeNotifier can
|
// that the ScrollPanel listening to the resizeNotifier can
|
||||||
// correctly measure it's new height and scroll down to keep
|
// correctly measure it's new height and scroll down to keep
|
||||||
|
@ -633,9 +634,9 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
<div className={classes} ref={this.ref}>
|
<div className={classes} ref={this.ref}>
|
||||||
{ recordingTooltip }
|
{ recordingTooltip }
|
||||||
<div className="mx_MessageComposer_wrapper">
|
<div className="mx_MessageComposer_wrapper">
|
||||||
{ this.props.showReplyPreview && (
|
<ReplyPreview
|
||||||
<ReplyPreview permalinkCreator={this.props.permalinkCreator} />
|
replyToEvent={this.props.replyToEvent}
|
||||||
) }
|
permalinkCreator={this.props.permalinkCreator} />
|
||||||
<div className="mx_MessageComposer_row">
|
<div className="mx_MessageComposer_row">
|
||||||
{ controls }
|
{ controls }
|
||||||
{ this.renderButtons(menuPosition) }
|
{ this.renderButtons(menuPosition) }
|
||||||
|
|
|
@ -17,63 +17,31 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
|
||||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import ReplyTile from './ReplyTile';
|
import ReplyTile from './ReplyTile';
|
||||||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
import { EventSubscription } from 'fbemitter';
|
import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext';
|
||||||
|
|
||||||
function cancelQuoting() {
|
function cancelQuoting(context: TimelineRenderingType) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'reply_to_event',
|
action: 'reply_to_event',
|
||||||
event: null,
|
event: null,
|
||||||
|
context,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
permalinkCreator: RoomPermalinkCreator;
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
}
|
replyToEvent: MatrixEvent;
|
||||||
|
|
||||||
interface IState {
|
|
||||||
event: MatrixEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.ReplyPreview")
|
@replaceableComponent("views.rooms.ReplyPreview")
|
||||||
export default class ReplyPreview extends React.Component<IProps, IState> {
|
export default class ReplyPreview extends React.Component<IProps> {
|
||||||
private unmounted = false;
|
public static contextType = RoomContext;
|
||||||
private readonly roomStoreToken: EventSubscription;
|
|
||||||
|
|
||||||
constructor(props) {
|
public render(): JSX.Element {
|
||||||
super(props);
|
if (!this.props.replyToEvent) return null;
|
||||||
|
|
||||||
this.state = {
|
|
||||||
event: RoomViewStore.getQuotingEvent(),
|
|
||||||
};
|
|
||||||
|
|
||||||
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.unmounted = true;
|
|
||||||
|
|
||||||
// Remove RoomStore listener
|
|
||||||
if (this.roomStoreToken) {
|
|
||||||
this.roomStoreToken.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onRoomViewStoreUpdate = (): void => {
|
|
||||||
if (this.unmounted) return;
|
|
||||||
|
|
||||||
const event = RoomViewStore.getQuotingEvent();
|
|
||||||
if (this.state.event !== event) {
|
|
||||||
this.setState({ event });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (!this.state.event) return null;
|
|
||||||
|
|
||||||
return <div className="mx_ReplyPreview">
|
return <div className="mx_ReplyPreview">
|
||||||
<div className="mx_ReplyPreview_section">
|
<div className="mx_ReplyPreview_section">
|
||||||
|
@ -86,13 +54,13 @@ export default class ReplyPreview extends React.Component<IProps, IState> {
|
||||||
src={require("../../../../res/img/cancel.svg")}
|
src={require("../../../../res/img/cancel.svg")}
|
||||||
width="18"
|
width="18"
|
||||||
height="18"
|
height="18"
|
||||||
onClick={cancelQuoting}
|
onClick={() => cancelQuoting(this.context.timelineRenderingType)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_ReplyPreview_clear" />
|
<div className="mx_ReplyPreview_clear" />
|
||||||
<div className="mx_ReplyPreview_tile">
|
<div className="mx_ReplyPreview_tile">
|
||||||
<ReplyTile
|
<ReplyTile
|
||||||
mxEvent={this.state.event}
|
mxEvent={this.props.replyToEvent}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -238,6 +238,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'reply_to_event',
|
action: 'reply_to_event',
|
||||||
event: null,
|
event: null,
|
||||||
|
context: this.context.timelineRenderingType,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -269,6 +270,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'reply_to_event',
|
action: 'reply_to_event',
|
||||||
event: replyEventId ? this.props.room.findEventById(replyEventId) : null,
|
event: replyEventId ? this.props.room.findEventById(replyEventId) : null,
|
||||||
|
context: this.context.timelineRenderingType,
|
||||||
});
|
});
|
||||||
if (parts) {
|
if (parts) {
|
||||||
this.model.reset(parts);
|
this.model.reset(parts);
|
||||||
|
@ -479,6 +481,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'reply_to_event',
|
action: 'reply_to_event',
|
||||||
event: null,
|
event: null,
|
||||||
|
context: this.context.timelineRenderingType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
dis.dispatch({ action: "message_sent" });
|
dis.dispatch({ action: "message_sent" });
|
||||||
|
@ -552,6 +555,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'reply_to_event',
|
action: 'reply_to_event',
|
||||||
event: this.props.room.findEventById(replyEventId),
|
event: this.props.room.findEventById(replyEventId),
|
||||||
|
context: this.context.timelineRenderingType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return parts;
|
return parts;
|
||||||
|
@ -583,7 +587,9 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'reply_to_event':
|
case 'reply_to_event':
|
||||||
case Action.FocusSendMessageComposer:
|
case Action.FocusSendMessageComposer:
|
||||||
|
if (payload.context === this.context.timelineRenderingType) {
|
||||||
this.editorRef.current?.focus();
|
this.editorRef.current?.focus();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "send_composer_insert":
|
case "send_composer_insert":
|
||||||
if (payload.userId) {
|
if (payload.userId) {
|
||||||
|
|
|
@ -20,11 +20,11 @@ import { IRoomState } from "../components/structures/RoomView";
|
||||||
import { Layout } from "../settings/Layout";
|
import { Layout } from "../settings/Layout";
|
||||||
|
|
||||||
export enum TimelineRenderingType {
|
export enum TimelineRenderingType {
|
||||||
Room,
|
Room = "Room",
|
||||||
Thread,
|
Thread = "Thread",
|
||||||
ThreadsList,
|
ThreadsList = "ThreadsList",
|
||||||
File,
|
File = "File",
|
||||||
Notification,
|
Notification = "Notification",
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomContext = createContext<IRoomState>({
|
const RoomContext = createContext<IRoomState>({
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { retry } from "../utils/promise";
|
||||||
import CountlyAnalytics from "../CountlyAnalytics";
|
import CountlyAnalytics from "../CountlyAnalytics";
|
||||||
|
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
import { TimelineRenderingType } from "../contexts/RoomContext";
|
||||||
|
|
||||||
const NUM_JOIN_RETRY = 5;
|
const NUM_JOIN_RETRY = 5;
|
||||||
|
|
||||||
|
@ -153,7 +154,9 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||||
case 'reply_to_event':
|
case 'reply_to_event':
|
||||||
// If currently viewed room does not match the room in which we wish to reply then change rooms
|
// If currently viewed room does not match the room in which we wish to reply then change rooms
|
||||||
// this can happen when performing a search across all rooms
|
// this can happen when performing a search across all rooms
|
||||||
if (payload.event && payload.event.getRoomId() !== this.state.roomId) {
|
if (payload.context === TimelineRenderingType.Room) {
|
||||||
|
if (payload.event
|
||||||
|
&& payload.event.getRoomId() !== this.state.roomId) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: payload.event.getRoomId(),
|
room_id: payload.event.getRoomId(),
|
||||||
|
@ -164,6 +167,7 @@ class RoomViewStore extends Store<ActionPayload> {
|
||||||
replyingToEvent: payload.event,
|
replyingToEvent: payload.event,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'open_room_settings': {
|
case 'open_room_settings': {
|
||||||
// FIXME: Using an import will result in test failures
|
// FIXME: Using an import will result in test failures
|
||||||
|
|
|
@ -216,6 +216,7 @@ describe('<SendMessageComposer/>', () => {
|
||||||
expect(spyDispatcher).toHaveBeenCalledWith({
|
expect(spyDispatcher).toHaveBeenCalledWith({
|
||||||
action: "reply_to_event",
|
action: "reply_to_event",
|
||||||
event: mockEvent,
|
event: mockEvent,
|
||||||
|
context: TimelineRenderingType.Room,
|
||||||
});
|
});
|
||||||
|
|
||||||
// now try with localStorage wiped out
|
// now try with localStorage wiped out
|
||||||
|
@ -277,6 +278,7 @@ describe('<SendMessageComposer/>', () => {
|
||||||
expect(spyDispatcher).toHaveBeenCalledWith({
|
expect(spyDispatcher).toHaveBeenCalledWith({
|
||||||
action: "reply_to_event",
|
action: "reply_to_event",
|
||||||
event: null,
|
event: null,
|
||||||
|
context: TimelineRenderingType.Room,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(wrapper.text()).toBe("");
|
expect(wrapper.text()).toBe("");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue