Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/19010
Conflicts: src/components/structures/SpaceRoomView.tsx src/components/views/rooms/MemberList.tsx src/components/views/rooms/RoomList.tsx
This commit is contained in:
commit
020eca6922
177 changed files with 6615 additions and 2121 deletions
|
@ -28,7 +28,6 @@ import { parseEvent } from '../../../editor/deserialize';
|
|||
import { CommandPartCreator, Part, PartCreator, Type } from '../../../editor/parts';
|
||||
import EditorStateTransfer from '../../../utils/EditorStateTransfer';
|
||||
import BasicMessageComposer, { REGEX_EMOTICON } from "./BasicMessageComposer";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { Command, CommandCategories, getCommand } from '../../../SlashCommands';
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import CountlyAnalytics from "../../../CountlyAnalytics";
|
||||
|
@ -36,7 +35,7 @@ import { getKeyBindingsManager, MessageComposerAction } from '../../../KeyBindin
|
|||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import SendHistoryManager from '../../../SendHistoryManager';
|
||||
import Modal from '../../../Modal';
|
||||
import { MsgType } from 'matrix-js-sdk/src/@types/event';
|
||||
import { MsgType, UNSTABLE_ELEMENT_REPLY_IN_THREAD } from 'matrix-js-sdk/src/@types/event';
|
||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||
import ErrorDialog from "../dialogs/ErrorDialog";
|
||||
import QuestionDialog from "../dialogs/QuestionDialog";
|
||||
|
@ -46,6 +45,8 @@ import { createRedactEventDialog } from '../dialogs/ConfirmRedactDialog';
|
|||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { withMatrixClientHOC, MatrixClientProps } from '../../../contexts/MatrixClientContext';
|
||||
import RoomContext, { TimelineRenderingType } from '../../../contexts/RoomContext';
|
||||
|
||||
function getHtmlReplyFallback(mxEvent: MatrixEvent): string {
|
||||
const html = mxEvent.getContent().formatted_body;
|
||||
|
@ -66,7 +67,11 @@ function getTextReplyFallback(mxEvent: MatrixEvent): string {
|
|||
return "";
|
||||
}
|
||||
|
||||
function createEditContent(model: EditorModel, editedEvent: MatrixEvent): IContent {
|
||||
function createEditContent(
|
||||
model: EditorModel,
|
||||
editedEvent: MatrixEvent,
|
||||
renderingContext?: TimelineRenderingType,
|
||||
): IContent {
|
||||
const isEmote = containsEmote(model);
|
||||
if (isEmote) {
|
||||
model = stripEmoteCommand(model);
|
||||
|
@ -99,41 +104,49 @@ function createEditContent(model: EditorModel, editedEvent: MatrixEvent): IConte
|
|||
contentBody.formatted_body = `${htmlPrefix} * ${formattedBody}`;
|
||||
}
|
||||
|
||||
return Object.assign({
|
||||
const relation = {
|
||||
"m.new_content": newContent,
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": editedEvent.getId(),
|
||||
},
|
||||
}, contentBody);
|
||||
};
|
||||
|
||||
if (renderingContext === TimelineRenderingType.Thread) {
|
||||
relation['m.relates_to'][UNSTABLE_ELEMENT_REPLY_IN_THREAD.name] = true;
|
||||
}
|
||||
|
||||
return Object.assign(relation, contentBody);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
interface IEditMessageComposerProps extends MatrixClientProps {
|
||||
editState: EditorStateTransfer;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
saveDisabled: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.rooms.EditMessageComposer")
|
||||
export default class EditMessageComposer extends React.Component<IProps, IState> {
|
||||
static contextType = MatrixClientContext;
|
||||
context!: React.ContextType<typeof MatrixClientContext>;
|
||||
class EditMessageComposer extends React.Component<IEditMessageComposerProps, IState> {
|
||||
static contextType = RoomContext;
|
||||
context!: React.ContextType<typeof RoomContext>;
|
||||
|
||||
private readonly editorRef = createRef<BasicMessageComposer>();
|
||||
private readonly dispatcherRef: string;
|
||||
private model: EditorModel = null;
|
||||
|
||||
constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||
constructor(props: IEditMessageComposerProps, context: React.ContextType<typeof RoomContext>) {
|
||||
super(props);
|
||||
this.context = context; // otherwise React will only set it prior to render due to type def above
|
||||
|
||||
const isRestored = this.createEditorModel();
|
||||
const ev = this.props.editState.getEvent();
|
||||
|
||||
const renderingContext = this.context.timelineRenderingType;
|
||||
const editContent = createEditContent(this.model, ev, renderingContext);
|
||||
this.state = {
|
||||
saveDisabled: !isRestored || !this.isContentModified(createEditContent(this.model, ev)["m.new_content"]),
|
||||
saveDisabled: !isRestored || !this.isContentModified(editContent["m.new_content"]),
|
||||
};
|
||||
|
||||
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
||||
|
@ -141,7 +154,7 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
}
|
||||
|
||||
private getRoom(): Room {
|
||||
return this.context.getRoom(this.props.editState.getEvent().getRoomId());
|
||||
return this.props.mxClient.getRoom(this.props.editState.getEvent().getRoomId());
|
||||
}
|
||||
|
||||
private onKeyDown = (event: KeyboardEvent): void => {
|
||||
|
@ -162,10 +175,17 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
if (this.editorRef.current?.isModified() || !this.editorRef.current?.isCaretAtStart()) {
|
||||
return;
|
||||
}
|
||||
const previousEvent = findEditableEvent(this.getRoom(), false,
|
||||
this.props.editState.getEvent().getId());
|
||||
const previousEvent = findEditableEvent({
|
||||
events: this.events,
|
||||
isForward: false,
|
||||
fromEventId: this.props.editState.getEvent().getId(),
|
||||
});
|
||||
if (previousEvent) {
|
||||
dis.dispatch({ action: 'edit_event', event: previousEvent });
|
||||
dis.dispatch({
|
||||
action: Action.EditEvent,
|
||||
event: previousEvent,
|
||||
timelineRenderingType: this.context.timelineRenderingType,
|
||||
});
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
|
@ -174,12 +194,24 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
if (this.editorRef.current?.isModified() || !this.editorRef.current?.isCaretAtEnd()) {
|
||||
return;
|
||||
}
|
||||
const nextEvent = findEditableEvent(this.getRoom(), true, this.props.editState.getEvent().getId());
|
||||
const nextEvent = findEditableEvent({
|
||||
events: this.events,
|
||||
isForward: true,
|
||||
fromEventId: this.props.editState.getEvent().getId(),
|
||||
});
|
||||
if (nextEvent) {
|
||||
dis.dispatch({ action: 'edit_event', event: nextEvent });
|
||||
dis.dispatch({
|
||||
action: Action.EditEvent,
|
||||
event: nextEvent,
|
||||
timelineRenderingType: this.context.timelineRenderingType,
|
||||
});
|
||||
} else {
|
||||
this.clearStoredEditorState();
|
||||
dis.dispatch({ action: 'edit_event', event: null });
|
||||
dis.dispatch({
|
||||
action: Action.EditEvent,
|
||||
event: null,
|
||||
timelineRenderingType: this.context.timelineRenderingType,
|
||||
});
|
||||
dis.fire(Action.FocusSendMessageComposer);
|
||||
}
|
||||
event.preventDefault();
|
||||
|
@ -189,16 +221,27 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
private get editorRoomKey(): string {
|
||||
return `mx_edit_room_${this.getRoom().roomId}`;
|
||||
return `mx_edit_room_${this.getRoom().roomId}_${this.context.timelineRenderingType}`;
|
||||
}
|
||||
|
||||
private get editorStateKey(): string {
|
||||
return `mx_edit_state_${this.props.editState.getEvent().getId()}`;
|
||||
}
|
||||
|
||||
private get events(): MatrixEvent[] {
|
||||
const liveTimelineEvents = this.context.liveTimeline.getEvents();
|
||||
const pendingEvents = this.getRoom().getPendingEvents();
|
||||
const isInThread = Boolean(this.props.editState.getEvent().getThread());
|
||||
return liveTimelineEvents.concat(isInThread ? [] : pendingEvents);
|
||||
}
|
||||
|
||||
private cancelEdit = (): void => {
|
||||
this.clearStoredEditorState();
|
||||
dis.dispatch({ action: "edit_event", event: null });
|
||||
dis.dispatch({
|
||||
action: Action.EditEvent,
|
||||
event: null,
|
||||
timelineRenderingType: this.context.timelineRenderingType,
|
||||
});
|
||||
dis.fire(Action.FocusSendMessageComposer);
|
||||
};
|
||||
|
||||
|
@ -326,8 +369,8 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
const position = this.model.positionForOffset(caret.offset, caret.atNodeEnd);
|
||||
this.editorRef.current?.replaceEmoticon(position, REGEX_EMOTICON);
|
||||
}
|
||||
|
||||
const editContent = createEditContent(this.model, editedEvent);
|
||||
const renderingContext = this.context.timelineRenderingType;
|
||||
const editContent = createEditContent(this.model, editedEvent, renderingContext);
|
||||
const newContent = editContent["m.new_content"];
|
||||
|
||||
let shouldSend = true;
|
||||
|
@ -381,7 +424,7 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
}
|
||||
if (shouldSend) {
|
||||
this.cancelPreviousPendingEdit();
|
||||
const prom = this.context.sendMessage(roomId, editContent);
|
||||
const prom = this.props.mxClient.sendMessage(roomId, editContent);
|
||||
this.clearStoredEditorState();
|
||||
dis.dispatch({ action: "message_sent" });
|
||||
CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, true, false, editContent);
|
||||
|
@ -389,7 +432,11 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
}
|
||||
|
||||
// close the event editing and focus composer
|
||||
dis.dispatch({ action: "edit_event", event: null });
|
||||
dis.dispatch({
|
||||
action: Action.EditEvent,
|
||||
event: null,
|
||||
timelineRenderingType: this.context.timelineRenderingType,
|
||||
});
|
||||
dis.fire(Action.FocusSendMessageComposer);
|
||||
};
|
||||
|
||||
|
@ -400,7 +447,7 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
previousEdit.status === EventStatus.QUEUED ||
|
||||
previousEdit.status === EventStatus.NOT_SENT
|
||||
)) {
|
||||
this.context.cancelPendingEvent(previousEdit);
|
||||
this.props.mxClient.cancelPendingEvent(previousEdit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -428,7 +475,7 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
private createEditorModel(): boolean {
|
||||
const { editState } = this.props;
|
||||
const room = this.getRoom();
|
||||
const partCreator = new CommandPartCreator(room, this.context);
|
||||
const partCreator = new CommandPartCreator(room, this.props.mxClient);
|
||||
|
||||
let parts;
|
||||
let isRestored = false;
|
||||
|
@ -493,3 +540,6 @@ export default class EditMessageComposer extends React.Component<IProps, IState>
|
|||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
const EditMessageComposerWithMatrixClient = withMatrixClientHOC(EditMessageComposer);
|
||||
export default EditMessageComposerWithMatrixClient;
|
||||
|
|
|
@ -53,7 +53,7 @@ import SenderProfile from '../messages/SenderProfile';
|
|||
import MessageTimestamp from '../messages/MessageTimestamp';
|
||||
import TooltipButton from '../elements/TooltipButton';
|
||||
import ReadReceiptMarker from "./ReadReceiptMarker";
|
||||
import MessageActionBar, { ActionBarRenderingContext } from "../messages/MessageActionBar";
|
||||
import MessageActionBar from "../messages/MessageActionBar";
|
||||
import ReactionsRow from '../messages/ReactionsRow';
|
||||
import { getEventDisplayInfo } from '../../../utils/EventUtils';
|
||||
import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
|
||||
|
@ -264,6 +264,8 @@ interface IProps {
|
|||
// for now.
|
||||
tileShape?: TileShape;
|
||||
|
||||
forExport?: boolean;
|
||||
|
||||
// show twelve hour timestamps
|
||||
isTwelveHour?: boolean;
|
||||
|
||||
|
@ -340,6 +342,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
static defaultProps = {
|
||||
// no-op function because onHeightChanged is optional yet some sub-components assume its existence
|
||||
onHeightChanged: function() {},
|
||||
forExport: false,
|
||||
layout: Layout.Group,
|
||||
};
|
||||
|
||||
|
@ -382,7 +385,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
* or 'sent' receipt, for example.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private get isEligibleForSpecialReceipt() {
|
||||
private get isEligibleForSpecialReceipt(): boolean {
|
||||
// First, if there are other read receipts then just short-circuit this.
|
||||
if (this.props.readReceipts && this.props.readReceipts.length > 0) return false;
|
||||
if (!this.props.mxEvent) return false;
|
||||
|
@ -453,16 +456,18 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
componentDidMount() {
|
||||
this.suppressReadReceiptAnimation = false;
|
||||
const client = this.context;
|
||||
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
client.on("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||
this.props.mxEvent.on("Event.decrypted", this.onDecrypted);
|
||||
if (this.props.showReactions) {
|
||||
this.props.mxEvent.on("Event.relationsCreated", this.onReactionsCreated);
|
||||
}
|
||||
if (!this.props.forExport) {
|
||||
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||
client.on("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||
this.props.mxEvent.on("Event.decrypted", this.onDecrypted);
|
||||
if (this.props.showReactions) {
|
||||
this.props.mxEvent.on("Event.relationsCreated", this.onReactionsCreated);
|
||||
}
|
||||
|
||||
if (this.shouldShowSentReceipt || this.shouldShowSendingReceipt) {
|
||||
client.on("Room.receipt", this.onRoomReceipt);
|
||||
this.isListeningForReceipts = true;
|
||||
if (this.shouldShowSentReceipt || this.shouldShowSendingReceipt) {
|
||||
client.on("Room.receipt", this.onRoomReceipt);
|
||||
this.isListeningForReceipts = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (SettingsStore.getValue("feature_thread")) {
|
||||
|
@ -698,6 +703,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
shouldHighlight() {
|
||||
if (this.props.forExport) return false;
|
||||
const actions = this.context.getPushActionsForEvent(this.props.mxEvent.replacingEvent() || this.props.mxEvent);
|
||||
if (!actions || !actions.tweaks) { return false; }
|
||||
|
||||
|
@ -1056,17 +1062,14 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
const renderingContext = this.props.tileShape === TileShape.Thread
|
||||
? ActionBarRenderingContext.Thread
|
||||
: ActionBarRenderingContext.Room;
|
||||
const actionBar = !isEditing ? <MessageActionBar
|
||||
const showMessageActionBar = !isEditing && !this.props.forExport;
|
||||
const actionBar = showMessageActionBar ? <MessageActionBar
|
||||
mxEvent={this.props.mxEvent}
|
||||
reactions={this.state.reactions}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
getTile={this.getTile}
|
||||
getReplyThread={this.getReplyThread}
|
||||
onFocusChange={this.onActionBarFocusChange}
|
||||
renderingContext={renderingContext}
|
||||
isQuoteExpanded={isQuoteExpanded}
|
||||
toggleThreadExpanded={() => this.setQuoteExpanded(!isQuoteExpanded)}
|
||||
/> : undefined;
|
||||
|
@ -1171,6 +1174,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
showUrlPreview={this.props.showUrlPreview}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
tileShape={this.props.tileShape}
|
||||
editState={this.props.editState}
|
||||
/>
|
||||
</div>,
|
||||
]);
|
||||
|
@ -1204,6 +1208,8 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
showUrlPreview={this.props.showUrlPreview}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
tileShape={this.props.tileShape}
|
||||
editState={this.props.editState}
|
||||
replacingEventId={this.props.replacingEventId}
|
||||
/>
|
||||
{ actionBar }
|
||||
</div>,
|
||||
|
@ -1224,6 +1230,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
showUrlPreview={this.props.showUrlPreview}
|
||||
tileShape={this.props.tileShape}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
editState={this.props.editState}
|
||||
/>
|
||||
</div>,
|
||||
<a
|
||||
|
@ -1247,6 +1254,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
parentEv={this.props.mxEvent}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
ref={this.replyThread}
|
||||
forExport={this.props.forExport}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
layout={this.props.layout}
|
||||
alwaysShowTimestamps={this.props.alwaysShowTimestamps || this.state.hover}
|
||||
|
@ -1280,6 +1288,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
{ thread }
|
||||
<EventTileType ref={this.tile}
|
||||
mxEvent={this.props.mxEvent}
|
||||
forExport={this.props.forExport}
|
||||
replacingEventId={this.props.replacingEventId}
|
||||
editState={this.props.editState}
|
||||
highlights={this.props.highlights}
|
||||
|
@ -1305,7 +1314,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
|
||||
// XXX this'll eventually be dynamic based on the fields once we have extensible event types
|
||||
const messageTypes = ['m.room.message', 'm.sticker'];
|
||||
function isMessageEvent(ev) {
|
||||
function isMessageEvent(ev: MatrixEvent): boolean {
|
||||
return (messageTypes.includes(ev.getType()));
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ import MemberTile from "./MemberTile";
|
|||
import BaseAvatar from '../avatars/BaseAvatar';
|
||||
import { throttle } from 'lodash';
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../settings/UIFeature";
|
||||
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||
|
||||
const getSearchQueryLSKey = (roomId: string) => `mx_MemberList_searchQuarry_${roomId}`;
|
||||
|
@ -535,7 +537,7 @@ export default class MemberList extends React.Component<IProps, IState> {
|
|||
const room = cli.getRoom(this.props.roomId);
|
||||
let inviteButton;
|
||||
|
||||
if (room && room.getMyMembership() === 'join') {
|
||||
if (room?.getMyMembership() === 'join' && shouldShowComponent(UIComponent.InviteUsers)) {
|
||||
let inviteButtonText = _t("Invite to this room");
|
||||
const chat = CommunityPrototypeStore.instance.getSelectedCommunityGeneralChat();
|
||||
if (chat && chat.roomId === this.props.roomId) {
|
||||
|
|
|
@ -45,13 +45,15 @@ import { RecordingState } from "../../../audio/VoiceRecording";
|
|||
import Tooltip, { Alignment } from "../elements/Tooltip";
|
||||
import ResizeNotifier from "../../../utils/ResizeNotifier";
|
||||
import { E2EStatus } from '../../../utils/ShieldUtils';
|
||||
import SendMessageComposer from "./SendMessageComposer";
|
||||
import SendMessageComposer, { SendMessageComposer as SendMessageComposerClass } from "./SendMessageComposer";
|
||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import EditorModel from "../../../editor/model";
|
||||
import EmojiPicker from '../emojipicker/EmojiPicker';
|
||||
import MemberStatusMessageAvatar from "../avatars/MemberStatusMessageAvatar";
|
||||
import UIStore, { UI_EVENTS } from '../../../stores/UIStore';
|
||||
import Modal from "../../../Modal";
|
||||
import InfoDialog from "../dialogs/InfoDialog";
|
||||
|
||||
let instanceCount = 0;
|
||||
const NARROW_MODE_BREAKPOINT = 500;
|
||||
|
@ -193,6 +195,31 @@ class UploadButton extends React.Component<IUploadButtonProps> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: [polls] Make this component actually do something
|
||||
class PollButton extends React.PureComponent {
|
||||
private onCreateClick = () => {
|
||||
Modal.createTrackedDialog('Polls', 'Not Yet Implemented', InfoDialog, {
|
||||
// XXX: Deliberately not translated given this dialog is meant to be replaced and we don't
|
||||
// want to clutter the language files with short-lived strings.
|
||||
title: "Polls are currently in development",
|
||||
description: "" +
|
||||
"Thanks for testing polls! We haven't quite gotten a chance to write the feature yet " +
|
||||
"though. Check back later for updates.",
|
||||
hasCloseButton: true,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AccessibleTooltipButton
|
||||
className="mx_MessageComposer_button mx_MessageComposer_poll"
|
||||
onClick={this.onCreateClick}
|
||||
title={_t('Create poll')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
|
@ -219,8 +246,8 @@ interface IState {
|
|||
@replaceableComponent("views.rooms.MessageComposer")
|
||||
export default class MessageComposer extends React.Component<IProps, IState> {
|
||||
private dispatcherRef: string;
|
||||
private messageComposerInput: SendMessageComposer;
|
||||
private voiceRecordingButton: VoiceRecordComposerTile;
|
||||
private messageComposerInput = createRef<SendMessageComposerClass>();
|
||||
private voiceRecordingButton = createRef<VoiceRecordComposerTile>();
|
||||
private ref: React.RefObject<HTMLDivElement> = createRef();
|
||||
private instanceId: number;
|
||||
|
||||
|
@ -378,14 +405,14 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
private sendMessage = async () => {
|
||||
if (this.state.haveRecording && this.voiceRecordingButton) {
|
||||
if (this.state.haveRecording && this.voiceRecordingButton.current) {
|
||||
// There shouldn't be any text message to send when a voice recording is active, so
|
||||
// just send out the voice recording.
|
||||
await this.voiceRecordingButton.send();
|
||||
await this.voiceRecordingButton.current?.send();
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageComposerInput.sendMessage();
|
||||
this.messageComposerInput.current?.sendMessage();
|
||||
};
|
||||
|
||||
private onChange = (model: EditorModel) => {
|
||||
|
@ -432,6 +459,11 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
private renderButtons(menuPosition): JSX.Element | JSX.Element[] {
|
||||
const buttons: JSX.Element[] = [];
|
||||
if (!this.state.haveRecording) {
|
||||
if (SettingsStore.getValue("feature_polls")) {
|
||||
buttons.push(
|
||||
<PollButton key="polls" />,
|
||||
);
|
||||
}
|
||||
buttons.push(
|
||||
<UploadButton key="controls_upload" roomId={this.props.room.roomId} />,
|
||||
);
|
||||
|
@ -460,7 +492,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
buttons.push(
|
||||
<AccessibleTooltipButton
|
||||
className="mx_MessageComposer_button mx_MessageComposer_voiceMessage"
|
||||
onClick={() => this.voiceRecordingButton?.onRecordStartEndClick()}
|
||||
onClick={() => this.voiceRecordingButton.current?.onRecordStartEndClick()}
|
||||
title={_t("Send voice message")}
|
||||
/>,
|
||||
);
|
||||
|
@ -521,7 +553,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
if (!this.state.tombstone && this.state.canSendMessages) {
|
||||
controls.push(
|
||||
<SendMessageComposer
|
||||
ref={(c) => this.messageComposerInput = c}
|
||||
ref={this.messageComposerInput}
|
||||
key="controls_input"
|
||||
room={this.props.room}
|
||||
placeholder={this.renderPlaceholderText()}
|
||||
|
@ -535,7 +567,7 @@ export default class MessageComposer extends React.Component<IProps, IState> {
|
|||
|
||||
controls.push(<VoiceRecordComposerTile
|
||||
key="controls_voice_record"
|
||||
ref={c => this.voiceRecordingButton = c}
|
||||
ref={this.voiceRecordingButton}
|
||||
room={this.props.room} />);
|
||||
} else if (this.state.tombstone) {
|
||||
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
|
||||
|
|
|
@ -28,15 +28,17 @@ import AccessibleButton from "../elements/AccessibleButton";
|
|||
import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import { showSpaceInvite } from "../../../utils/space";
|
||||
import { privateShouldBeEncrypted } from "../../../createRoom";
|
||||
import EventTileBubble from "../messages/EventTileBubble";
|
||||
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../settings/UIFeature";
|
||||
|
||||
function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room): boolean {
|
||||
const isEncrypted: boolean = matrixClient.isRoomEncrypted(room.roomId);
|
||||
|
@ -150,7 +152,7 @@ const NewRoomIntro = () => {
|
|||
{ _t("Invite to just this room") }
|
||||
</AccessibleButton> }
|
||||
</div>;
|
||||
} else if (room.canInvite(cli.getUserId())) {
|
||||
} else if (room.canInvite(cli.getUserId()) && shouldShowComponent(UIComponent.InviteUsers)) {
|
||||
buttons = <div className="mx_NewRoomIntro_buttons">
|
||||
<AccessibleButton
|
||||
className="mx_NewRoomIntro_inviteButton"
|
||||
|
|
|
@ -49,6 +49,8 @@ import { showAddExistingRooms, showCreateNewRoom, showSpaceInvite } from "../../
|
|||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../settings/UIFeature";
|
||||
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||
|
||||
interface IProps {
|
||||
|
@ -134,32 +136,38 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
|
|||
MatrixClientPeg.get().getUserId());
|
||||
|
||||
return <IconizedContextMenuOptionList first>
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Create new room")}
|
||||
iconClassName="mx_RoomList_iconPlus"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onFinished();
|
||||
showCreateNewRoom(SpaceStore.instance.activeSpace);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={canAddRooms ? undefined
|
||||
: _t("You do not have permissions to create new rooms in this space")}
|
||||
/>
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Add existing room")}
|
||||
iconClassName="mx_RoomList_iconHash"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onFinished();
|
||||
showAddExistingRooms(SpaceStore.instance.activeSpace);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={canAddRooms ? undefined
|
||||
: _t("You do not have permissions to add rooms to this space")}
|
||||
/>
|
||||
{
|
||||
shouldShowComponent(UIComponent.CreateRooms)
|
||||
? (<>
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Create new room")}
|
||||
iconClassName="mx_RoomList_iconPlus"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onFinished();
|
||||
showCreateNewRoom(SpaceStore.instance.activeSpace);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={canAddRooms ? undefined
|
||||
: _t("You do not have permissions to create new rooms in this space")}
|
||||
/>
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Add existing room")}
|
||||
iconClassName="mx_RoomList_iconHash"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onFinished();
|
||||
showAddExistingRooms(SpaceStore.instance.activeSpace);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={canAddRooms ? undefined
|
||||
: _t("You do not have permissions to add rooms to this space")}
|
||||
/>
|
||||
</>)
|
||||
: null
|
||||
}
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Explore rooms")}
|
||||
iconClassName="mx_RoomList_iconBrowse"
|
||||
|
@ -450,8 +458,8 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
|
||||
private renderSublists(): React.ReactElement[] {
|
||||
// show a skeleton UI if the user is in no rooms and they are not filtering
|
||||
const showSkeleton = !this.state.isNameFiltering &&
|
||||
// show a skeleton UI if the user is in no rooms and they are not filtering and have no suggested rooms
|
||||
const showSkeleton = !this.state.isNameFiltering && !this.state.suggestedRooms?.length &&
|
||||
Object.values(RoomListStore.instance.unfilteredLists).every(list => !list?.length);
|
||||
|
||||
return TAG_ORDER.reduce((tags, tagId) => {
|
||||
|
|
|
@ -35,6 +35,8 @@ import InviteReason from "../elements/InviteReason";
|
|||
import { IOOBData } from "../../../stores/ThreepidInviteStore";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import { UIFeature } from "../../../settings/UIFeature";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
const MemberEventHtmlReasonField = "io.element.html_reason";
|
||||
|
||||
|
@ -339,8 +341,10 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
|||
}
|
||||
case MessageCase.NotLoggedIn: {
|
||||
title = _t("Join the conversation with an account");
|
||||
primaryActionLabel = _t("Sign Up");
|
||||
primaryActionHandler = this.onRegisterClick;
|
||||
if (SettingsStore.getValue(UIFeature.Registration)) {
|
||||
primaryActionLabel = _t("Sign Up");
|
||||
primaryActionHandler = this.onRegisterClick;
|
||||
}
|
||||
secondaryActionLabel = _t("Sign In");
|
||||
secondaryActionHandler = this.onLoginClick;
|
||||
if (this.props.previewLoading) {
|
||||
|
|
|
@ -55,6 +55,8 @@ import { ListNotificationState } from "../../../stores/notifications/ListNotific
|
|||
import IconizedContextMenu from "../context_menus/IconizedContextMenu";
|
||||
import { getKeyBindingsManager, RoomListAction } from "../../../KeyBindingsManager";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../settings/UIFeature";
|
||||
|
||||
const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS
|
||||
const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS
|
||||
|
@ -675,7 +677,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
);
|
||||
|
||||
let addRoomButton = null;
|
||||
if (!!this.props.onAddRoom) {
|
||||
if (!!this.props.onAddRoom && shouldShowComponent(UIComponent.CreateRooms)) {
|
||||
addRoomButton = (
|
||||
<AccessibleTooltipButton
|
||||
tabIndex={tabIndex}
|
||||
|
@ -687,6 +689,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
/>
|
||||
);
|
||||
} else if (this.props.addRoomContextMenu) {
|
||||
// We assume that shouldShowComponent() is checked by the context menu itself.
|
||||
addRoomButton = (
|
||||
<ContextMenuTooltipButton
|
||||
tabIndex={tabIndex}
|
||||
|
|
|
@ -29,10 +29,9 @@ import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextM
|
|||
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||
import { MessagePreviewStore } from "../../../stores/room-list/MessagePreviewStore";
|
||||
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||
import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE } from "../../../RoomNotifs";
|
||||
import { RoomNotifState } from "../../../RoomNotifs";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import NotificationBadge from "./NotificationBadge";
|
||||
import { Volume } from "../../../RoomNotifsTypes";
|
||||
import RoomListStore from "../../../stores/room-list/RoomListStore";
|
||||
import RoomListActions from "../../../actions/RoomListActions";
|
||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
|
@ -364,7 +363,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
this.setState({ generalMenuPosition: null }); // hide the menu
|
||||
};
|
||||
|
||||
private async saveNotifState(ev: ButtonEvent, newState: Volume) {
|
||||
private async saveNotifState(ev: ButtonEvent, newState: RoomNotifState) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
if (MatrixClientPeg.get().isGuest()) return;
|
||||
|
@ -378,10 +377,10 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
private onClickAllNotifs = ev => this.saveNotifState(ev, ALL_MESSAGES);
|
||||
private onClickAlertMe = ev => this.saveNotifState(ev, ALL_MESSAGES_LOUD);
|
||||
private onClickMentions = ev => this.saveNotifState(ev, MENTIONS_ONLY);
|
||||
private onClickMute = ev => this.saveNotifState(ev, MUTE);
|
||||
private onClickAllNotifs = ev => this.saveNotifState(ev, RoomNotifState.AllMessages);
|
||||
private onClickAlertMe = ev => this.saveNotifState(ev, RoomNotifState.AllMessagesLoud);
|
||||
private onClickMentions = ev => this.saveNotifState(ev, RoomNotifState.MentionsOnly);
|
||||
private onClickMute = ev => this.saveNotifState(ev, RoomNotifState.Mute);
|
||||
|
||||
private renderNotificationsMenu(isActive: boolean): React.ReactElement {
|
||||
if (MatrixClientPeg.get().isGuest() || this.props.tag === DefaultTagID.Archived ||
|
||||
|
@ -404,25 +403,25 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
<IconizedContextMenuOptionList first>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("Use default")}
|
||||
active={state === ALL_MESSAGES}
|
||||
active={state === RoomNotifState.AllMessages}
|
||||
iconClassName="mx_RoomTile_iconBell"
|
||||
onClick={this.onClickAllNotifs}
|
||||
/>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("All messages")}
|
||||
active={state === ALL_MESSAGES_LOUD}
|
||||
active={state === RoomNotifState.AllMessagesLoud}
|
||||
iconClassName="mx_RoomTile_iconBellDot"
|
||||
onClick={this.onClickAlertMe}
|
||||
/>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("Mentions & Keywords")}
|
||||
active={state === MENTIONS_ONLY}
|
||||
active={state === RoomNotifState.MentionsOnly}
|
||||
iconClassName="mx_RoomTile_iconBellMentions"
|
||||
onClick={this.onClickMentions}
|
||||
/>
|
||||
<IconizedContextMenuRadio
|
||||
label={_t("None")}
|
||||
active={state === MUTE}
|
||||
active={state === RoomNotifState.Mute}
|
||||
iconClassName="mx_RoomTile_iconBellCrossed"
|
||||
onClick={this.onClickMute}
|
||||
/>
|
||||
|
@ -432,14 +431,14 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
|||
|
||||
const classes = classNames("mx_RoomTile_notificationsButton", {
|
||||
// Show bell icon for the default case too.
|
||||
mx_RoomTile_iconBell: state === ALL_MESSAGES,
|
||||
mx_RoomTile_iconBellDot: state === ALL_MESSAGES_LOUD,
|
||||
mx_RoomTile_iconBellMentions: state === MENTIONS_ONLY,
|
||||
mx_RoomTile_iconBellCrossed: state === MUTE,
|
||||
mx_RoomTile_iconBell: state === RoomNotifState.AllMessages,
|
||||
mx_RoomTile_iconBellDot: state === RoomNotifState.AllMessagesLoud,
|
||||
mx_RoomTile_iconBellMentions: state === RoomNotifState.MentionsOnly,
|
||||
mx_RoomTile_iconBellCrossed: state === RoomNotifState.Mute,
|
||||
|
||||
// Only show the icon by default if the room is overridden to muted.
|
||||
// TODO: [FTUE Notifications] Probably need to detect global mute state
|
||||
mx_RoomTile_notificationsButton_show: state === MUTE,
|
||||
mx_RoomTile_notificationsButton_show: state === RoomNotifState.Mute,
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -19,6 +19,7 @@ import EMOJI_REGEX from 'emojibase-regex';
|
|||
import { IContent, MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||
import { DebouncedFunc, throttle } from 'lodash';
|
||||
import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import EditorModel from '../../../editor/model';
|
||||
|
@ -40,7 +41,7 @@ import { Command, CommandCategories, getCommand } from '../../../SlashCommands';
|
|||
import Modal from '../../../Modal';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import ContentMessages from '../../../ContentMessages';
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { withMatrixClientHOC, MatrixClientProps } from "../../../contexts/MatrixClientContext";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { containsEmoji } from "../../../effects/utils";
|
||||
import { CHAT_EFFECTS } from '../../../effects';
|
||||
|
@ -55,8 +56,7 @@ import ErrorDialog from "../dialogs/ErrorDialog";
|
|||
import QuestionDialog from "../dialogs/QuestionDialog";
|
||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
import { decorateStartSendingTime, sendRoundTripMetric } from "../../../sendTimePerformanceMetrics";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import RoomContext from '../../../contexts/RoomContext';
|
||||
|
||||
function addReplyToMessageContent(
|
||||
content: IContent,
|
||||
|
@ -130,7 +130,7 @@ export function isQuickReaction(model: EditorModel): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
interface ISendMessageComposerProps extends MatrixClientProps {
|
||||
room: Room;
|
||||
placeholder?: string;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
|
@ -141,10 +141,8 @@ interface IProps {
|
|||
}
|
||||
|
||||
@replaceableComponent("views.rooms.SendMessageComposer")
|
||||
export default class SendMessageComposer extends React.Component<IProps> {
|
||||
static contextType = MatrixClientContext;
|
||||
context!: React.ContextType<typeof MatrixClientContext>;
|
||||
|
||||
export class SendMessageComposer extends React.Component<ISendMessageComposerProps> {
|
||||
static contextType = RoomContext;
|
||||
private readonly prepareToEncrypt?: DebouncedFunc<() => void>;
|
||||
private readonly editorRef = createRef<BasicMessageComposer>();
|
||||
private model: EditorModel = null;
|
||||
|
@ -152,26 +150,25 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
private dispatcherRef: string;
|
||||
private sendHistoryManager: SendHistoryManager;
|
||||
|
||||
constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
|
||||
constructor(props: ISendMessageComposerProps, context: React.ContextType<typeof RoomContext>) {
|
||||
super(props);
|
||||
this.context = context; // otherwise React will only set it prior to render due to type def above
|
||||
if (this.context.isCryptoEnabled() && this.context.isRoomEncrypted(this.props.room.roomId)) {
|
||||
if (this.props.mxClient.isCryptoEnabled() && this.props.mxClient.isRoomEncrypted(this.props.room.roomId)) {
|
||||
this.prepareToEncrypt = throttle(() => {
|
||||
this.context.prepareToEncrypt(this.props.room);
|
||||
this.props.mxClient.prepareToEncrypt(this.props.room);
|
||||
}, 60000, { leading: true, trailing: false });
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", this.saveStoredEditorState);
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: IProps): void {
|
||||
public componentDidUpdate(prevProps: ISendMessageComposerProps): void {
|
||||
const replyToEventChanged = this.props.replyInThread && (this.props.replyToEvent !== prevProps.replyToEvent);
|
||||
if (replyToEventChanged) {
|
||||
this.model.reset([]);
|
||||
}
|
||||
|
||||
if (this.props.replyInThread && this.props.replyToEvent && (!prevProps.replyToEvent || replyToEventChanged)) {
|
||||
const partCreator = new CommandPartCreator(this.props.room, this.context);
|
||||
const partCreator = new CommandPartCreator(this.props.room, this.props.mxClient);
|
||||
const parts = this.restoreStoredEditorState(partCreator) || [];
|
||||
this.model.reset(parts);
|
||||
this.editorRef.current?.focus();
|
||||
|
@ -202,13 +199,20 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
case MessageComposerAction.EditPrevMessage:
|
||||
// selection must be collapsed and caret at start
|
||||
if (this.editorRef.current?.isSelectionCollapsed() && this.editorRef.current?.isCaretAtStart()) {
|
||||
const editEvent = findEditableEvent(this.props.room, false);
|
||||
const events =
|
||||
this.context.liveTimeline.getEvents()
|
||||
.concat(this.props.replyInThread ? [] : this.props.room.getPendingEvents());
|
||||
const editEvent = findEditableEvent({
|
||||
events,
|
||||
isForward: false,
|
||||
});
|
||||
if (editEvent) {
|
||||
// We're selecting history, so prevent the key event from doing anything else
|
||||
event.preventDefault();
|
||||
dis.dispatch({
|
||||
action: 'edit_event',
|
||||
action: Action.EditEvent,
|
||||
event: editEvent,
|
||||
timelineRenderingType: this.context.timelineRenderingType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +279,7 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
}
|
||||
|
||||
private sendQuickReaction(): void {
|
||||
const timeline = this.props.room.getLiveTimeline();
|
||||
const timeline = this.context.liveTimeline();
|
||||
const events = timeline.getEvents();
|
||||
const reaction = this.model.parts[1].text;
|
||||
for (let i = events.length - 1; i >= 0; i--) {
|
||||
|
@ -448,7 +452,7 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
decorateStartSendingTime(content);
|
||||
}
|
||||
|
||||
const prom = this.context.sendMessage(roomId, content);
|
||||
const prom = this.props.mxClient.sendMessage(roomId, content);
|
||||
if (replyToEvent) {
|
||||
// Clear reply_to_event as we put the message into the queue
|
||||
// if the send fails, retry will handle resending.
|
||||
|
@ -465,7 +469,7 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
});
|
||||
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
||||
prom.then(resp => {
|
||||
sendRoundTripMetric(this.context, roomId, resp.event_id);
|
||||
sendRoundTripMetric(this.props.mxClient, roomId, resp.event_id);
|
||||
});
|
||||
}
|
||||
CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, false, !!replyToEvent, content);
|
||||
|
@ -490,7 +494,7 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
|
||||
// TODO: [REACT-WARNING] Move this to constructor
|
||||
UNSAFE_componentWillMount() { // eslint-disable-line
|
||||
const partCreator = new CommandPartCreator(this.props.room, this.context);
|
||||
const partCreator = new CommandPartCreator(this.props.room, this.props.mxClient);
|
||||
const parts = this.restoreStoredEditorState(partCreator) || [];
|
||||
this.model = new EditorModel(parts, partCreator);
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
|
@ -577,7 +581,7 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
// it puts the filename in as text/plain which we want to ignore.
|
||||
if (clipboardData.files.length && !clipboardData.types.includes("text/rtf")) {
|
||||
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||
Array.from(clipboardData.files), this.props.room.roomId, this.context,
|
||||
Array.from(clipboardData.files), this.props.room.roomId, this.props.mxClient,
|
||||
);
|
||||
return true; // to skip internal onPaste handler
|
||||
}
|
||||
|
@ -608,3 +612,6 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SendMessageComposerWithMatrixClient = withMatrixClientHOC(SendMessageComposer);
|
||||
export default SendMessageComposerWithMatrixClient;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue