Fix /join #alias command in markdown mode
Tab-completing a room alias inserts a markdown link, which is now stripped before a command is parsed. We also strip the MD links when doing the autocompletion itself (getAutocompleteQuery). There is still an issue where `/invite` will not work at all with tab-completion - the text of a user Pill is not the userID but rather the display name, which cannot be used as an argument to the command.
This commit is contained in:
parent
91e74b3333
commit
e71df44fc9
1 changed files with 54 additions and 33 deletions
|
@ -644,7 +644,17 @@ export default class MessageComposerInput extends React.Component {
|
||||||
|
|
||||||
let contentText = contentState.getPlainText(), contentHTML;
|
let contentText = contentState.getPlainText(), contentHTML;
|
||||||
|
|
||||||
const cmd = SlashCommands.processInput(this.props.room.roomId, contentText);
|
// Strip MD user (tab-completed) mentions to preserve plaintext mention behaviour.
|
||||||
|
// We have to do this now as opposed to after calculating the contentText for MD
|
||||||
|
// mode because entity positions may not be maintained when using
|
||||||
|
// md.toPlaintext().
|
||||||
|
// Unfortunately this means we lose mentions in history when in MD mode. This
|
||||||
|
// would be fixed if history was stored as contentState.
|
||||||
|
contentText = this.removeMDLinks(contentState, ['@']);
|
||||||
|
|
||||||
|
// Some commands (/join) require pills to be replaced with their text content
|
||||||
|
const commandText = this.removeMDLinks(contentState, ['#']);
|
||||||
|
const cmd = SlashCommands.processInput(this.props.room.roomId, commandText);
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
if (!cmd.error) {
|
if (!cmd.error) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -708,7 +718,9 @@ export default class MessageComposerInput extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const md = new Markdown(contentText);
|
// Use the original plaintext because `contextText` has had mentions stripped
|
||||||
|
// and these need to end up in contentHTML
|
||||||
|
const md = new Markdown(contentState.getPlainText());
|
||||||
if (md.isPlainText()) {
|
if (md.isPlainText()) {
|
||||||
contentText = md.toPlaintext();
|
contentText = md.toPlaintext();
|
||||||
} else {
|
} else {
|
||||||
|
@ -737,35 +749,6 @@ export default class MessageComposerInput extends React.Component {
|
||||||
sendTextFn = this.client.sendEmoteMessage;
|
sendTextFn = this.client.sendEmoteMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip MD user (tab-completed) mentions to preserve plaintext mention behaviour
|
|
||||||
contentText = contentText.replace(REGEX_MATRIXTO_MARKDOWN_GLOBAL,
|
|
||||||
(markdownLink, text, resource, prefix, offset) => {
|
|
||||||
// Calculate the offset relative to the current block that the offset is in
|
|
||||||
let sum = 0;
|
|
||||||
const blocks = contentState.getBlocksAsArray();
|
|
||||||
let block;
|
|
||||||
for (let i = 0; i < blocks.length; i++) {
|
|
||||||
block = blocks[i];
|
|
||||||
sum += block.getLength();
|
|
||||||
if (sum > offset) {
|
|
||||||
sum -= block.getLength();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offset -= sum;
|
|
||||||
|
|
||||||
const entityKey = block.getEntityAt(offset);
|
|
||||||
const entity = entityKey ? Entity.get(entityKey) : null;
|
|
||||||
if (entity && entity.getData().isCompletion && prefix === '@') {
|
|
||||||
// This is a completed mention, so do not insert MD link, just text
|
|
||||||
return text;
|
|
||||||
} else {
|
|
||||||
// This is either a MD link that was typed into the composer or another
|
|
||||||
// type of pill (e.g. room pill)
|
|
||||||
return markdownLink;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let sendMessagePromise;
|
let sendMessagePromise;
|
||||||
if (contentHTML) {
|
if (contentHTML) {
|
||||||
sendMessagePromise = sendHtmlFn.call(
|
sendMessagePromise = sendHtmlFn.call(
|
||||||
|
@ -1013,6 +996,45 @@ export default class MessageComposerInput extends React.Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAutocompleteQuery(contentState: ContentState) {
|
||||||
|
// Don't send markdown links to the autocompleter
|
||||||
|
return this.removeMDLinks(contentState, ['@', '#']);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeMDLinks(contentState: ContentState, prefixes: string[]) {
|
||||||
|
const plaintext = contentState.getPlainText();
|
||||||
|
console.info('Removing MD', plaintext);
|
||||||
|
if (!plaintext) return '';
|
||||||
|
return plaintext.replace(REGEX_MATRIXTO_MARKDOWN_GLOBAL,
|
||||||
|
(markdownLink, text, resource, prefix, offset) => {
|
||||||
|
if (!prefixes.includes(prefix)) return markdownLink;
|
||||||
|
// Calculate the offset relative to the current block that the offset is in
|
||||||
|
let sum = 0;
|
||||||
|
const blocks = contentState.getBlocksAsArray();
|
||||||
|
let block;
|
||||||
|
for (let i = 0; i < blocks.length; i++) {
|
||||||
|
block = blocks[i];
|
||||||
|
sum += block.getLength();
|
||||||
|
if (sum > offset) {
|
||||||
|
sum -= block.getLength();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset -= sum;
|
||||||
|
|
||||||
|
const entityKey = block.getEntityAt(offset);
|
||||||
|
const entity = entityKey ? Entity.get(entityKey) : null;
|
||||||
|
if (entity && entity.getData().isCompletion) {
|
||||||
|
// This is a completed mention, so do not insert MD link, just text
|
||||||
|
return text;
|
||||||
|
} else {
|
||||||
|
// This is either a MD link that was typed into the composer or another
|
||||||
|
// type of pill (e.g. room pill)
|
||||||
|
return markdownLink;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onMarkdownToggleClicked = (e) => {
|
onMarkdownToggleClicked = (e) => {
|
||||||
e.preventDefault(); // don't steal focus from the editor!
|
e.preventDefault(); // don't steal focus from the editor!
|
||||||
this.handleKeyCommand('toggle-mode');
|
this.handleKeyCommand('toggle-mode');
|
||||||
|
@ -1038,7 +1060,6 @@ export default class MessageComposerInput extends React.Component {
|
||||||
});
|
});
|
||||||
|
|
||||||
const content = activeEditorState.getCurrentContent();
|
const content = activeEditorState.getCurrentContent();
|
||||||
const contentText = content.getPlainText();
|
|
||||||
const selection = RichText.selectionStateToTextOffsets(activeEditorState.getSelection(),
|
const selection = RichText.selectionStateToTextOffsets(activeEditorState.getSelection(),
|
||||||
activeEditorState.getCurrentContent().getBlocksAsArray());
|
activeEditorState.getCurrentContent().getBlocksAsArray());
|
||||||
|
|
||||||
|
@ -1048,7 +1069,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
ref={(e) => this.autocomplete = e}
|
ref={(e) => this.autocomplete = e}
|
||||||
onConfirm={this.setDisplayedCompletion}
|
onConfirm={this.setDisplayedCompletion}
|
||||||
query={contentText}
|
query={this.getAutocompleteQuery(content)}
|
||||||
selection={selection}/>
|
selection={selection}/>
|
||||||
</div>
|
</div>
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
|
|
Loading…
Reference in a new issue