Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/18858
This commit is contained in:
commit
b052d0730d
24 changed files with 824 additions and 774 deletions
|
@ -15,43 +15,48 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, { createRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import MemberAvatar from '../avatars/MemberAvatar';
|
||||
import classNames from 'classnames';
|
||||
import StatusMessageContextMenu from "../context_menus/StatusMessageContextMenu";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { ContextMenu, ContextMenuButton } from "../../structures/ContextMenu";
|
||||
import { ChevronFace, ContextMenu, ContextMenuButton } from "../../structures/ContextMenu";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { ResizeMethod } from "matrix-js-sdk/src/@types/partials";
|
||||
|
||||
interface IProps {
|
||||
member: RoomMember;
|
||||
width?: number;
|
||||
height?: number;
|
||||
resizeMethod?: ResizeMethod;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
hasStatus: boolean;
|
||||
menuDisplayed: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.avatars.MemberStatusMessageAvatar")
|
||||
export default class MemberStatusMessageAvatar extends React.Component {
|
||||
static propTypes = {
|
||||
member: PropTypes.object.isRequired,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
resizeMethod: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
export default class MemberStatusMessageAvatar extends React.Component<IProps, IState> {
|
||||
public static defaultProps: Partial<IProps> = {
|
||||
width: 40,
|
||||
height: 40,
|
||||
resizeMethod: 'crop',
|
||||
};
|
||||
private button = createRef<HTMLDivElement>();
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
hasStatus: this.hasStatus,
|
||||
menuDisplayed: false,
|
||||
};
|
||||
|
||||
this._button = createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
if (this.props.member.userId !== MatrixClientPeg.get().getUserId()) {
|
||||
throw new Error("Cannot use MemberStatusMessageAvatar on anyone but the logged in user");
|
||||
}
|
||||
|
@ -62,44 +67,44 @@ export default class MemberStatusMessageAvatar extends React.Component {
|
|||
if (!user) {
|
||||
return;
|
||||
}
|
||||
user.on("User._unstable_statusMessage", this._onStatusMessageCommitted);
|
||||
user.on("User._unstable_statusMessage", this.onStatusMessageCommitted);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
const { user } = this.props.member;
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
user.removeListener(
|
||||
"User._unstable_statusMessage",
|
||||
this._onStatusMessageCommitted,
|
||||
this.onStatusMessageCommitted,
|
||||
);
|
||||
}
|
||||
|
||||
get hasStatus() {
|
||||
private get hasStatus(): boolean {
|
||||
const { user } = this.props.member;
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
return !!user._unstable_statusMessage;
|
||||
return !!user.unstable_statusMessage;
|
||||
}
|
||||
|
||||
_onStatusMessageCommitted = () => {
|
||||
private onStatusMessageCommitted = (): void => {
|
||||
// The `User` object has observed a status message change.
|
||||
this.setState({
|
||||
hasStatus: this.hasStatus,
|
||||
});
|
||||
};
|
||||
|
||||
openMenu = () => {
|
||||
private openMenu = (): void => {
|
||||
this.setState({ menuDisplayed: true });
|
||||
};
|
||||
|
||||
closeMenu = () => {
|
||||
private closeMenu = (): void => {
|
||||
this.setState({ menuDisplayed: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
const avatar = <MemberAvatar
|
||||
member={this.props.member}
|
||||
width={this.props.width}
|
||||
|
@ -118,7 +123,7 @@ export default class MemberStatusMessageAvatar extends React.Component {
|
|||
|
||||
let contextMenu;
|
||||
if (this.state.menuDisplayed) {
|
||||
const elementRect = this._button.current.getBoundingClientRect();
|
||||
const elementRect = this.button.current.getBoundingClientRect();
|
||||
|
||||
const chevronWidth = 16; // See .mx_ContextualMenu_chevron_bottom
|
||||
const chevronMargin = 1; // Add some spacing away from target
|
||||
|
@ -126,13 +131,13 @@ export default class MemberStatusMessageAvatar extends React.Component {
|
|||
contextMenu = (
|
||||
<ContextMenu
|
||||
chevronOffset={(elementRect.width - chevronWidth) / 2}
|
||||
chevronFace="bottom"
|
||||
chevronFace={ChevronFace.Bottom}
|
||||
left={elementRect.left + window.pageXOffset}
|
||||
top={elementRect.top + window.pageYOffset - chevronMargin}
|
||||
menuWidth={226}
|
||||
onFinished={this.closeMenu}
|
||||
>
|
||||
<StatusMessageContextMenu user={this.props.member.user} onFinished={this.closeMenu} />
|
||||
<StatusMessageContextMenu user={this.props.member.user} />
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
@ -140,7 +145,7 @@ export default class MemberStatusMessageAvatar extends React.Component {
|
|||
return <React.Fragment>
|
||||
<ContextMenuButton
|
||||
className={classes}
|
||||
inputRef={this._button}
|
||||
inputRef={this.button}
|
||||
onClick={this.openMenu}
|
||||
isExpanded={this.state.menuDisplayed}
|
||||
label={_t("User Status")}
|
|
@ -15,45 +15,41 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
/*
|
||||
interface IProps {
|
||||
element: React.ReactNode;
|
||||
// Function to be called when the parent window is resized
|
||||
// This can be used to reposition or close the menu on resize and
|
||||
// ensure that it is not displayed in a stale position.
|
||||
onResize?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This component can be used to display generic HTML content in a contextual
|
||||
* menu.
|
||||
*/
|
||||
|
||||
@replaceableComponent("views.context_menus.GenericElementContextMenu")
|
||||
export default class GenericElementContextMenu extends React.Component {
|
||||
static propTypes = {
|
||||
element: PropTypes.element.isRequired,
|
||||
// Function to be called when the parent window is resized
|
||||
// This can be used to reposition or close the menu on resize and
|
||||
// ensure that it is not displayed in a stale position.
|
||||
onResize: PropTypes.func,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
export default class GenericElementContextMenu extends React.Component<IProps> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.resize = this.resize.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.resize = this.resize.bind(this);
|
||||
public componentDidMount(): void {
|
||||
window.addEventListener("resize", this.resize);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
window.removeEventListener("resize", this.resize);
|
||||
}
|
||||
|
||||
resize() {
|
||||
private resize = (): void => {
|
||||
if (this.props.onResize) {
|
||||
this.props.onResize();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
return <div>{ this.props.element }</div>;
|
||||
}
|
||||
}
|
|
@ -15,16 +15,15 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
@replaceableComponent("views.context_menus.GenericTextContextMenu")
|
||||
export default class GenericTextContextMenu extends React.Component {
|
||||
static propTypes = {
|
||||
message: PropTypes.string.isRequired,
|
||||
};
|
||||
interface IProps {
|
||||
message: string;
|
||||
}
|
||||
|
||||
render() {
|
||||
@replaceableComponent("views.context_menus.GenericTextContextMenu")
|
||||
export default class GenericTextContextMenu extends React.Component<IProps> {
|
||||
public render(): JSX.Element {
|
||||
return <div>{ this.props.message }</div>;
|
||||
}
|
||||
}
|
|
@ -14,53 +14,59 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import * as sdk from '../../../index';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { User } from "matrix-js-sdk/src/models/user";
|
||||
import Spinner from "../elements/Spinner";
|
||||
|
||||
interface IProps {
|
||||
// js-sdk User object. Not required because it might not exist.
|
||||
user?: User;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
message: string;
|
||||
waiting: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.context_menus.StatusMessageContextMenu")
|
||||
export default class StatusMessageContextMenu extends React.Component {
|
||||
static propTypes = {
|
||||
// js-sdk User object. Not required because it might not exist.
|
||||
user: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
export default class StatusMessageContextMenu extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
message: this.comittedStatusMessage,
|
||||
waiting: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
const { user } = this.props;
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
user.on("User._unstable_statusMessage", this._onStatusMessageCommitted);
|
||||
user.on("User._unstable_statusMessage", this.onStatusMessageCommitted);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
const { user } = this.props;
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
user.removeListener(
|
||||
"User._unstable_statusMessage",
|
||||
this._onStatusMessageCommitted,
|
||||
this.onStatusMessageCommitted,
|
||||
);
|
||||
}
|
||||
|
||||
get comittedStatusMessage() {
|
||||
return this.props.user ? this.props.user._unstable_statusMessage : "";
|
||||
get comittedStatusMessage(): string {
|
||||
return this.props.user ? this.props.user.unstable_statusMessage : "";
|
||||
}
|
||||
|
||||
_onStatusMessageCommitted = () => {
|
||||
private onStatusMessageCommitted = (): void => {
|
||||
// The `User` object has observed a status message change.
|
||||
this.setState({
|
||||
message: this.comittedStatusMessage,
|
||||
|
@ -68,14 +74,14 @@ export default class StatusMessageContextMenu extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
_onClearClick = (e) => {
|
||||
private onClearClick = (): void=> {
|
||||
MatrixClientPeg.get()._unstable_setStatusMessage("");
|
||||
this.setState({
|
||||
waiting: true,
|
||||
});
|
||||
};
|
||||
|
||||
_onSubmit = (e) => {
|
||||
private onSubmit = (e: ButtonEvent): void => {
|
||||
e.preventDefault();
|
||||
MatrixClientPeg.get()._unstable_setStatusMessage(this.state.message);
|
||||
this.setState({
|
||||
|
@ -83,27 +89,25 @@ export default class StatusMessageContextMenu extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
_onStatusChange = (e) => {
|
||||
private onStatusChange = (e: ChangeEvent): void => {
|
||||
// The input field's value was changed.
|
||||
this.setState({
|
||||
message: e.target.value,
|
||||
message: (e.target as HTMLInputElement).value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const Spinner = sdk.getComponent('views.elements.Spinner');
|
||||
|
||||
public render(): JSX.Element {
|
||||
let actionButton;
|
||||
if (this.comittedStatusMessage) {
|
||||
if (this.state.message === this.comittedStatusMessage) {
|
||||
actionButton = <AccessibleButton className="mx_StatusMessageContextMenu_clear"
|
||||
onClick={this._onClearClick}
|
||||
onClick={this.onClearClick}
|
||||
>
|
||||
<span>{ _t("Clear status") }</span>
|
||||
</AccessibleButton>;
|
||||
} else {
|
||||
actionButton = <AccessibleButton className="mx_StatusMessageContextMenu_submit"
|
||||
onClick={this._onSubmit}
|
||||
onClick={this.onSubmit}
|
||||
>
|
||||
<span>{ _t("Update status") }</span>
|
||||
</AccessibleButton>;
|
||||
|
@ -112,7 +116,7 @@ export default class StatusMessageContextMenu extends React.Component {
|
|||
actionButton = <AccessibleButton
|
||||
className="mx_StatusMessageContextMenu_submit"
|
||||
disabled={!this.state.message}
|
||||
onClick={this._onSubmit}
|
||||
onClick={this.onSubmit}
|
||||
>
|
||||
<span>{ _t("Set status") }</span>
|
||||
</AccessibleButton>;
|
||||
|
@ -120,13 +124,13 @@ export default class StatusMessageContextMenu extends React.Component {
|
|||
|
||||
let spinner = null;
|
||||
if (this.state.waiting) {
|
||||
spinner = <Spinner w="24" h="24" />;
|
||||
spinner = <Spinner w={24} h={24} />;
|
||||
}
|
||||
|
||||
const form = <form
|
||||
className="mx_StatusMessageContextMenu_form"
|
||||
autoComplete="off"
|
||||
onSubmit={this._onSubmit}
|
||||
onSubmit={this.onSubmit}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
|
@ -134,9 +138,9 @@ export default class StatusMessageContextMenu extends React.Component {
|
|||
key="message"
|
||||
placeholder={_t("Set a new status...")}
|
||||
autoFocus={true}
|
||||
maxLength="60"
|
||||
maxLength={60}
|
||||
value={this.state.message}
|
||||
onChange={this._onStatusChange}
|
||||
onChange={this.onStatusChange}
|
||||
/>
|
||||
<div className="mx_StatusMessageContextMenu_actionContainer">
|
||||
{ actionButton }
|
|
@ -429,7 +429,7 @@ const UserOptionsSection: React.FC<{
|
|||
if (!isMe) {
|
||||
directMessageButton = (
|
||||
<AccessibleButton onClick={() => { openDMForUser(cli, member.userId); }} className="mx_UserInfo_field">
|
||||
{ _t('Direct message') }
|
||||
{ _t("Message") }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,8 @@ import { AutocompleteAction, getKeyBindingsManager, MessageComposerAction } from
|
|||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
// matches emoticons which follow the start of a line or whitespace
|
||||
const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$');
|
||||
const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s|:^$');
|
||||
export const REGEX_EMOTICON = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')$');
|
||||
|
||||
const IS_MAC = navigator.platform.indexOf("Mac") !== -1;
|
||||
|
||||
|
@ -161,7 +162,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
}
|
||||
}
|
||||
|
||||
private replaceEmoticon = (caretPosition: DocumentPosition): number => {
|
||||
public replaceEmoticon(caretPosition: DocumentPosition, regex: RegExp): number {
|
||||
const { model } = this.props;
|
||||
const range = model.startRange(caretPosition);
|
||||
// expand range max 8 characters backwards from caretPosition,
|
||||
|
@ -170,9 +171,9 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
range.expandBackwardsWhile((index, offset) => {
|
||||
const part = model.parts[index];
|
||||
n -= 1;
|
||||
return n >= 0 && (part.type === Type.Plain || part.type === Type.PillCandidate);
|
||||
return n >= 0 && [Type.Plain, Type.PillCandidate, Type.Newline].includes(part.type);
|
||||
});
|
||||
const emoticonMatch = REGEX_EMOTICON_WHITESPACE.exec(range.text);
|
||||
const emoticonMatch = regex.exec(range.text);
|
||||
if (emoticonMatch) {
|
||||
const query = emoticonMatch[1].replace("-", "");
|
||||
// try both exact match and lower-case, this means that xd won't match xD but :P will match :p
|
||||
|
@ -180,18 +181,23 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
|
||||
if (data) {
|
||||
const { partCreator } = model;
|
||||
const hasPrecedingSpace = emoticonMatch[0][0] === " ";
|
||||
const moveStart = emoticonMatch[0][0] === " " ? 1 : 0;
|
||||
const moveEnd = emoticonMatch[0].length - emoticonMatch.length - moveStart;
|
||||
|
||||
// we need the range to only comprise of the emoticon
|
||||
// because we'll replace the whole range with an emoji,
|
||||
// so move the start forward to the start of the emoticon.
|
||||
// Take + 1 because index is reported without the possible preceding space.
|
||||
range.moveStart(emoticonMatch.index + (hasPrecedingSpace ? 1 : 0));
|
||||
range.moveStartForwards(emoticonMatch.index + moveStart);
|
||||
// and move end backwards so that we don't replace the trailing space/newline
|
||||
range.moveEndBackwards(moveEnd);
|
||||
|
||||
// this returns the amount of added/removed characters during the replace
|
||||
// so the caret position can be adjusted.
|
||||
return range.replace([partCreator.plain(data.unicode + " ")]);
|
||||
return range.replace([partCreator.plain(data.unicode)]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private updateEditorState = (selection: Caret, inputType?: string, diff?: IDiff): void => {
|
||||
renderModel(this.editorRef.current, this.props.model);
|
||||
|
@ -607,8 +613,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
};
|
||||
|
||||
private configureEmoticonAutoReplace = (): void => {
|
||||
const shouldReplace = SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji');
|
||||
this.props.model.setTransformCallback(shouldReplace ? this.replaceEmoticon : null);
|
||||
this.props.model.setTransformCallback(this.transform);
|
||||
};
|
||||
|
||||
private configureShouldShowPillAvatar = (): void => {
|
||||
|
@ -621,6 +626,11 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
this.setState({ surroundWith });
|
||||
};
|
||||
|
||||
private transform = (documentPosition: DocumentPosition): void => {
|
||||
const shouldReplace = SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji');
|
||||
if (shouldReplace) this.replaceEmoticon(documentPosition, REGEX_EMOTICON_WHITESPACE);
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("selectionchange", this.onSelectionChange);
|
||||
this.editorRef.current.removeEventListener("input", this.onInput, true);
|
||||
|
|
|
@ -1192,14 +1192,19 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
default: {
|
||||
const thread = ReplyThread.makeThread(
|
||||
this.props.mxEvent,
|
||||
this.props.onHeightChanged,
|
||||
this.props.permalinkCreator,
|
||||
this.replyThread,
|
||||
this.props.layout,
|
||||
this.props.alwaysShowTimestamps || this.state.hover,
|
||||
);
|
||||
let thread;
|
||||
// When the "showHiddenEventsInTimeline" lab is enabled,
|
||||
// avoid showing replies for hidden events (events without tiles)
|
||||
if (haveTileForEvent(this.props.mxEvent)) {
|
||||
thread = ReplyThread.makeThread(
|
||||
this.props.mxEvent,
|
||||
this.props.onHeightChanged,
|
||||
this.props.permalinkCreator,
|
||||
this.replyThread,
|
||||
this.props.layout,
|
||||
this.props.alwaysShowTimestamps || this.state.hover,
|
||||
);
|
||||
}
|
||||
|
||||
const isOwnEvent = this.props.mxEvent?.sender?.userId === MatrixClientPeg.get().getUserId();
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ let instanceCount = 0;
|
|||
const NARROW_MODE_BREAKPOINT = 500;
|
||||
|
||||
interface IComposerAvatarProps {
|
||||
me: object;
|
||||
me: RoomMember;
|
||||
}
|
||||
|
||||
function ComposerAvatar(props: IComposerAvatarProps) {
|
||||
|
|
|
@ -547,7 +547,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
const unfilteredHistorical = unfilteredLists[DefaultTagID.Archived] || [];
|
||||
const unfilteredFavourite = unfilteredLists[DefaultTagID.Favourite] || [];
|
||||
// show a prompt to join/create rooms if the user is in 0 rooms and no historical
|
||||
if (unfilteredRooms.length < 1 && unfilteredHistorical < 1 && unfilteredFavourite < 1) {
|
||||
if (unfilteredRooms.length < 1 && unfilteredHistorical.length < 1 && unfilteredFavourite.length < 1) {
|
||||
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
||||
<div>{ _t("Use the + to make a new room or explore existing ones below") }</div>
|
||||
<AccessibleButton
|
||||
|
|
|
@ -31,8 +31,8 @@ import {
|
|||
textSerialize,
|
||||
unescapeMessage,
|
||||
} from '../../../editor/serialize';
|
||||
import BasicMessageComposer, { REGEX_EMOTICON } from "./BasicMessageComposer";
|
||||
import { CommandPartCreator, Part, PartCreator, SerializedPart, Type } from '../../../editor/parts';
|
||||
import BasicMessageComposer from "./BasicMessageComposer";
|
||||
import ReplyThread from "../elements/ReplyThread";
|
||||
import { findEditableEvent } from '../../../utils/EventUtils';
|
||||
import SendHistoryManager from "../../../SendHistoryManager";
|
||||
|
@ -347,15 +347,24 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
}
|
||||
|
||||
public async sendMessage(): Promise<void> {
|
||||
if (this.model.isEmpty) {
|
||||
const model = this.model;
|
||||
|
||||
if (model.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace emoticon at the end of the message
|
||||
if (SettingsStore.getValue('MessageComposerInput.autoReplaceEmoji')) {
|
||||
const caret = this.editorRef.current?.getCaret();
|
||||
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
|
||||
this.editorRef.current?.replaceEmoticon(position, REGEX_EMOTICON);
|
||||
}
|
||||
|
||||
const replyToEvent = this.props.replyToEvent;
|
||||
let shouldSend = true;
|
||||
let content;
|
||||
|
||||
if (!containsEmote(this.model) && this.isSlashCommand()) {
|
||||
if (!containsEmote(model) && this.isSlashCommand()) {
|
||||
const [cmd, args, commandText] = this.getSlashCommand();
|
||||
if (cmd) {
|
||||
if (cmd.category === CommandCategories.messages) {
|
||||
|
@ -400,7 +409,7 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
}
|
||||
}
|
||||
|
||||
if (isQuickReaction(this.model)) {
|
||||
if (isQuickReaction(model)) {
|
||||
shouldSend = false;
|
||||
this.sendQuickReaction();
|
||||
}
|
||||
|
@ -410,7 +419,7 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
const { roomId } = this.props.room;
|
||||
if (!content) {
|
||||
content = createMessageContent(
|
||||
this.model,
|
||||
model,
|
||||
replyToEvent,
|
||||
this.props.replyInThread,
|
||||
this.props.permalinkCreator,
|
||||
|
@ -446,9 +455,9 @@ export default class SendMessageComposer extends React.Component<IProps> {
|
|||
CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, false, !!replyToEvent, content);
|
||||
}
|
||||
|
||||
this.sendHistoryManager.save(this.model, replyToEvent);
|
||||
this.sendHistoryManager.save(model, replyToEvent);
|
||||
// clear composer
|
||||
this.model.reset([]);
|
||||
model.reset([]);
|
||||
this.editorRef.current?.clearUndoHistory();
|
||||
this.editorRef.current?.focus();
|
||||
this.clearStoredEditorState();
|
||||
|
|
|
@ -277,9 +277,13 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
if (this.state.screensharing) {
|
||||
isScreensharing = await this.props.call.setScreensharingEnabled(false);
|
||||
} else {
|
||||
const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
|
||||
const [source] = await finished;
|
||||
isScreensharing = await this.props.call.setScreensharingEnabled(true, source);
|
||||
if (window.electron?.getDesktopCapturerSources) {
|
||||
const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
|
||||
const [source] = await finished;
|
||||
isScreensharing = await this.props.call.setScreensharingEnabled(true, source);
|
||||
} else {
|
||||
isScreensharing = await this.props.call.setScreensharingEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue