Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
4be222c6aa
4 changed files with 58 additions and 24 deletions
|
@ -111,7 +111,7 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
// relies on the length of the entity === length of the text in the decoration.
|
// relies on the length of the entity === length of the text in the decoration.
|
||||||
completion: user.rawDisplayName.replace(' (IRC)', ''),
|
completion: user.rawDisplayName.replace(' (IRC)', ''),
|
||||||
completionId: user.userId,
|
completionId: user.userId,
|
||||||
suffix: (selection.beginning && selection.start === 0) ? ': ' : ' ',
|
suffix: (selection.beginning && range.start === 0) ? ': ' : ' ',
|
||||||
href: makeUserPermalink(user.userId),
|
href: makeUserPermalink(user.userId),
|
||||||
component: (
|
component: (
|
||||||
<PillCompletion
|
<PillCompletion
|
||||||
|
|
|
@ -334,7 +334,9 @@ export default class MessageComposer extends React.Component {
|
||||||
if (this.state.showFormatting && this.state.inputState.isRichTextEnabled) {
|
if (this.state.showFormatting && this.state.inputState.isRichTextEnabled) {
|
||||||
const {marks, blockType} = this.state.inputState;
|
const {marks, blockType} = this.state.inputState;
|
||||||
const formatButtons = formatButtonList.map((name) => {
|
const formatButtons = formatButtonList.map((name) => {
|
||||||
const active = marks.some(mark => mark.type === name) || blockType === name;
|
// special-case to match the md serializer and the special-case in MessageComposerInput.js
|
||||||
|
const markName = name === 'inline-code' ? 'code' : name;
|
||||||
|
const active = marks.some(mark => mark.type === markName) || blockType === name;
|
||||||
const suffix = active ? '-on' : '';
|
const suffix = active ? '-on' : '';
|
||||||
const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
|
const onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
|
||||||
const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
|
const className = 'mx_MessageComposer_format_button mx_filterFlipColor';
|
||||||
|
|
|
@ -382,7 +382,8 @@ export default class MessageComposerInput extends React.Component {
|
||||||
const quote = Block.create('block-quote');
|
const quote = Block.create('block-quote');
|
||||||
if (this.state.isRichTextEnabled) {
|
if (this.state.isRichTextEnabled) {
|
||||||
let change = editorState.change();
|
let change = editorState.change();
|
||||||
if (editorState.anchorText.text === '' && editorState.anchorBlock.nodes.size === 1) {
|
const anchorText = editorState.anchorText;
|
||||||
|
if ((!anchorText || anchorText.text === '') && editorState.anchorBlock.nodes.size === 1) {
|
||||||
// replace the current block rather than split the block
|
// replace the current block rather than split the block
|
||||||
change = change.replaceNodeByKey(editorState.anchorBlock.key, quote);
|
change = change.replaceNodeByKey(editorState.anchorBlock.key, quote);
|
||||||
}
|
}
|
||||||
|
@ -969,19 +970,21 @@ export default class MessageComposerInput extends React.Component {
|
||||||
onPaste = (event: Event, change: Change, editor: Editor): Change => {
|
onPaste = (event: Event, change: Change, editor: Editor): Change => {
|
||||||
const transfer = getEventTransfer(event);
|
const transfer = getEventTransfer(event);
|
||||||
|
|
||||||
if (transfer.type === "files") {
|
switch (transfer.type) {
|
||||||
return this.props.onFilesPasted(transfer.files);
|
case 'files':
|
||||||
}
|
return this.props.onFilesPasted(transfer.files);
|
||||||
else if (transfer.type === "html") {
|
case 'html': {
|
||||||
// FIXME: https://github.com/ianstormtaylor/slate/issues/1497 means
|
// FIXME: https://github.com/ianstormtaylor/slate/issues/1497 means
|
||||||
// that we will silently discard nested blocks (e.g. nested lists) :(
|
// that we will silently discard nested blocks (e.g. nested lists) :(
|
||||||
const fragment = this.html.deserialize(transfer.html);
|
const fragment = this.html.deserialize(transfer.html);
|
||||||
if (this.state.isRichTextEnabled) {
|
if (this.state.isRichTextEnabled) {
|
||||||
return change.insertFragment(fragment.document);
|
return change.insertFragment(fragment.document);
|
||||||
}
|
} else {
|
||||||
else {
|
return change.insertText(this.md.serialize(fragment));
|
||||||
return change.insertText(this.md.serialize(fragment));
|
}
|
||||||
}
|
}
|
||||||
|
case 'text':
|
||||||
|
return change.insertText(transfer.text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -990,15 +993,30 @@ export default class MessageComposerInput extends React.Component {
|
||||||
return change.insertText('\n');
|
return change.insertText('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.editorState.blocks.some(
|
const editorState = this.state.editorState;
|
||||||
block => ['code', 'block-quote', 'list-item'].includes(block.type)
|
|
||||||
)) {
|
const lastBlock = editorState.blocks.last();
|
||||||
// allow the user to terminate blocks by hitting return rather than sending a msg
|
if (['code', 'block-quote', 'list-item'].includes(lastBlock.type)) {
|
||||||
|
const text = lastBlock.text;
|
||||||
|
if (text === '') {
|
||||||
|
// allow the user to cancel empty block by hitting return, useful in conjunction with below `inBlock`
|
||||||
|
return change
|
||||||
|
.setBlocks(DEFAULT_NODE)
|
||||||
|
.unwrapBlock('bulleted-list')
|
||||||
|
.unwrapBlock('numbered-list');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO strip trailing lines from blockquotes/list entries
|
||||||
|
// the below code seemingly works but doesn't account for edge cases like return with caret not at end
|
||||||
|
/* const trailingNewlines = text.match(/\n*$/);
|
||||||
|
if (trailingNewlines && trailingNewlines[0]) {
|
||||||
|
remove trailing newlines at the end of this block before making a new one
|
||||||
|
return change.deleteBackward(trailingNewlines[0].length);
|
||||||
|
}*/
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const editorState = this.state.editorState;
|
|
||||||
|
|
||||||
let contentText;
|
let contentText;
|
||||||
let contentHTML;
|
let contentHTML;
|
||||||
|
|
||||||
|
@ -1515,6 +1533,14 @@ export default class MessageComposerInput extends React.Component {
|
||||||
mx_MessageComposer_input_error: this.state.someCompletions === false,
|
mx_MessageComposer_input_error: this.state.someCompletions === false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isEmpty = this.state.editorState.document.isEmpty;
|
||||||
|
|
||||||
|
let {placeholder} = this.props;
|
||||||
|
// XXX: workaround for placeholder being shown when there is a formatting block e.g blockquote but no text
|
||||||
|
if (isEmpty && this.state.editorState.startBlock.type !== DEFAULT_NODE) {
|
||||||
|
placeholder = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MessageComposer_input_wrapper" onClick={this.focusComposer}>
|
<div className="mx_MessageComposer_input_wrapper" onClick={this.focusComposer}>
|
||||||
<div className="mx_MessageComposer_autocomplete_wrapper">
|
<div className="mx_MessageComposer_autocomplete_wrapper">
|
||||||
|
@ -1536,7 +1562,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
<Editor ref="editor"
|
<Editor ref="editor"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
className="mx_MessageComposer_editor"
|
className="mx_MessageComposer_editor"
|
||||||
placeholder={this.props.placeholder}
|
placeholder={placeholder}
|
||||||
value={this.state.editorState}
|
value={this.state.editorState}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
|
@ -1546,7 +1572,7 @@ export default class MessageComposerInput extends React.Component {
|
||||||
renderNode={this.renderNode}
|
renderNode={this.renderNode}
|
||||||
renderMark={this.renderMark}
|
renderMark={this.renderMark}
|
||||||
// disable spell check for the placeholder because browsers don't like "unencrypted"
|
// disable spell check for the placeholder because browsers don't like "unencrypted"
|
||||||
spellCheck={!this.state.editorState.document.isEmpty}
|
spellCheck={!isEmpty}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,7 +32,13 @@ class MessageComposerStore {
|
||||||
|
|
||||||
setEditorState(roomId: string, editorState: Value, richText: boolean) {
|
setEditorState(roomId: string, editorState: Value, richText: boolean) {
|
||||||
localStorage.setItem(this._getKey(roomId), JSON.stringify({
|
localStorage.setItem(this._getKey(roomId), JSON.stringify({
|
||||||
editor_state: editorState,
|
editor_state: editorState.toJSON({
|
||||||
|
preserveSelection: true,
|
||||||
|
// XXX: re-hydrating history is not currently supported by fromJSON
|
||||||
|
// preserveHistory: true,
|
||||||
|
// XXX: this seems like a workaround for selection.isSet being based on anchorKey instead of anchorPath
|
||||||
|
preserveKeys: true,
|
||||||
|
}),
|
||||||
rich_text: richText,
|
rich_text: richText,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue