Merge pull request #3893 from matrix-org/t3chguy/double_slash
Slash Command improvements around sending messages with leading slash
This commit is contained in:
commit
97edb824bc
7 changed files with 198 additions and 62 deletions
|
@ -24,6 +24,8 @@ import {
|
|||
containsEmote,
|
||||
stripEmoteCommand,
|
||||
unescapeMessage,
|
||||
startsWith,
|
||||
stripPrefix,
|
||||
} from '../../../editor/serialize';
|
||||
import {CommandPartCreator} from '../../../editor/parts';
|
||||
import BasicMessageComposer from "./BasicMessageComposer";
|
||||
|
@ -33,7 +35,7 @@ import ReplyThread from "../elements/ReplyThread";
|
|||
import {parseEvent} from '../../../editor/deserialize';
|
||||
import {findEditableEvent} from '../../../utils/EventUtils';
|
||||
import SendHistoryManager from "../../../SendHistoryManager";
|
||||
import {processCommandInput} from '../../../SlashCommands';
|
||||
import {getCommand} from '../../../SlashCommands';
|
||||
import * as sdk from '../../../index';
|
||||
import Modal from '../../../Modal';
|
||||
import {_t, _td} from '../../../languageHandler';
|
||||
|
@ -56,11 +58,15 @@ function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
|
|||
}
|
||||
}
|
||||
|
||||
function createMessageContent(model, permalinkCreator) {
|
||||
// exported for tests
|
||||
export function createMessageContent(model, permalinkCreator) {
|
||||
const isEmote = containsEmote(model);
|
||||
if (isEmote) {
|
||||
model = stripEmoteCommand(model);
|
||||
}
|
||||
if (startsWith(model, "//")) {
|
||||
model = stripPrefix(model, "/");
|
||||
}
|
||||
model = unescapeMessage(model);
|
||||
const repliedToEvent = RoomViewStore.getQuotingEvent();
|
||||
|
||||
|
@ -175,20 +181,21 @@ export default class SendMessageComposer extends React.Component {
|
|||
const parts = this.model.parts;
|
||||
const firstPart = parts[0];
|
||||
if (firstPart) {
|
||||
if (firstPart.type === "command") {
|
||||
if (firstPart.type === "command" && firstPart.text.startsWith("/") && !firstPart.text.startsWith("//")) {
|
||||
return true;
|
||||
}
|
||||
// be extra resilient when somehow the AutocompleteWrapperModel or
|
||||
// CommandPartCreator fails to insert a command part, so we don't send
|
||||
// a command as a message
|
||||
if (firstPart.text.startsWith("/") && (firstPart.type === "plain" || firstPart.type === "pill-candidate")) {
|
||||
if (firstPart.text.startsWith("/") && !firstPart.text.startsWith("//")
|
||||
&& (firstPart.type === "plain" || firstPart.type === "pill-candidate")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async _runSlashCommand() {
|
||||
_getSlashCommand() {
|
||||
const commandText = this.model.parts.reduce((text, part) => {
|
||||
// use mxid to textify user pills in a command
|
||||
if (part.type === "user-pill") {
|
||||
|
@ -196,50 +203,86 @@ export default class SendMessageComposer extends React.Component {
|
|||
}
|
||||
return text + part.text;
|
||||
}, "");
|
||||
const cmd = processCommandInput(this.props.room.roomId, commandText);
|
||||
return [getCommand(this.props.room.roomId, commandText), commandText];
|
||||
}
|
||||
|
||||
if (cmd) {
|
||||
let error = cmd.error;
|
||||
if (cmd.promise) {
|
||||
try {
|
||||
await cmd.promise;
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
async _runSlashCommand(fn) {
|
||||
const cmd = fn();
|
||||
let error = cmd.error;
|
||||
if (cmd.promise) {
|
||||
try {
|
||||
await cmd.promise;
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
if (error) {
|
||||
console.error("Command failure: %s", error);
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
// assume the error is a server error when the command is async
|
||||
const isServerError = !!cmd.promise;
|
||||
const title = isServerError ? _td("Server error") : _td("Command error");
|
||||
}
|
||||
if (error) {
|
||||
console.error("Command failure: %s", error);
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
// assume the error is a server error when the command is async
|
||||
const isServerError = !!cmd.promise;
|
||||
const title = isServerError ? _td("Server error") : _td("Command error");
|
||||
|
||||
let errText;
|
||||
if (typeof error === 'string') {
|
||||
errText = error;
|
||||
} else if (error.message) {
|
||||
errText = error.message;
|
||||
} else {
|
||||
errText = _t("Server unavailable, overloaded, or something else went wrong.");
|
||||
}
|
||||
|
||||
Modal.createTrackedDialog(title, '', ErrorDialog, {
|
||||
title: _t(title),
|
||||
description: errText,
|
||||
});
|
||||
let errText;
|
||||
if (typeof error === 'string') {
|
||||
errText = error;
|
||||
} else if (error.message) {
|
||||
errText = error.message;
|
||||
} else {
|
||||
console.log("Command success.");
|
||||
errText = _t("Server unavailable, overloaded, or something else went wrong.");
|
||||
}
|
||||
|
||||
Modal.createTrackedDialog(title, '', ErrorDialog, {
|
||||
title: _t(title),
|
||||
description: errText,
|
||||
});
|
||||
} else {
|
||||
console.log("Command success.");
|
||||
}
|
||||
}
|
||||
|
||||
_sendMessage() {
|
||||
async _sendMessage() {
|
||||
if (this.model.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
let shouldSend = true;
|
||||
|
||||
if (!containsEmote(this.model) && this._isSlashCommand()) {
|
||||
this._runSlashCommand();
|
||||
} else {
|
||||
const [cmd, commandText] = this._getSlashCommand();
|
||||
if (cmd) {
|
||||
shouldSend = false;
|
||||
this._runSlashCommand(cmd);
|
||||
} else {
|
||||
// ask the user if their unknown command should be sent as a message
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const {finished} = Modal.createTrackedDialog("Unknown command", "", QuestionDialog, {
|
||||
title: _t("Unknown Command"),
|
||||
description: <div>
|
||||
<p>
|
||||
{ _t("Unrecognised command: %(commandText)s", {commandText}) }
|
||||
</p>
|
||||
<p>
|
||||
{ _t("You can use <code>/help</code> to list available commands. " +
|
||||
"Did you mean to send this as a message?", {}, {
|
||||
code: t => <code>{ t }</code>,
|
||||
}) }
|
||||
</p>
|
||||
<p>
|
||||
{ _t("Hint: Begin your message with <code>//</code> to start it with a slash.", {}, {
|
||||
code: t => <code>{ t }</code>,
|
||||
}) }
|
||||
</p>
|
||||
</div>,
|
||||
button: _t('Send as message'),
|
||||
});
|
||||
const [sendAnyway] = await finished;
|
||||
// if !sendAnyway bail to let the user edit the composer and try again
|
||||
if (!sendAnyway) return;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSend) {
|
||||
const isReply = !!RoomViewStore.getQuotingEvent();
|
||||
const {roomId} = this.props.room;
|
||||
const content = createMessageContent(this.model, this.props.permalinkCreator);
|
||||
|
@ -253,6 +296,7 @@ export default class SendMessageComposer extends React.Component {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.sendHistoryManager.save(this.model);
|
||||
// clear composer
|
||||
this.model.reset([]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue