mirror of https://github.com/vector-im/riot-web
Merge remote-tracking branch 'origin/develop' into develop
commit
4be222c6aa
|
@ -111,7 +111,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||
// relies on the length of the entity === length of the text in the decoration.
|
||||
completion: user.rawDisplayName.replace(' (IRC)', ''),
|
||||
completionId: user.userId,
|
||||
suffix: (selection.beginning && selection.start === 0) ? ': ' : ' ',
|
||||
suffix: (selection.beginning && range.start === 0) ? ': ' : ' ',
|
||||
href: makeUserPermalink(user.userId),
|
||||
component: (
|
||||
<PillCompletion
|
||||
|
|
|
@ -334,7 +334,9 @@ export default class MessageComposer extends React.Component {
|
|||
if (this.state.showFormatting && this.state.inputState.isRichTextEnabled) {
|
||||
const {marks, blockType} = this.state.inputState;
|
||||
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 onFormatButtonClicked = this.onFormatButtonClicked.bind(this, name);
|
||||
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');
|
||||
if (this.state.isRichTextEnabled) {
|
||||
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
|
||||
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 => {
|
||||
const transfer = getEventTransfer(event);
|
||||
|
||||
if (transfer.type === "files") {
|
||||
return this.props.onFilesPasted(transfer.files);
|
||||
}
|
||||
else if (transfer.type === "html") {
|
||||
// FIXME: https://github.com/ianstormtaylor/slate/issues/1497 means
|
||||
// that we will silently discard nested blocks (e.g. nested lists) :(
|
||||
const fragment = this.html.deserialize(transfer.html);
|
||||
if (this.state.isRichTextEnabled) {
|
||||
return change.insertFragment(fragment.document);
|
||||
}
|
||||
else {
|
||||
return change.insertText(this.md.serialize(fragment));
|
||||
switch (transfer.type) {
|
||||
case 'files':
|
||||
return this.props.onFilesPasted(transfer.files);
|
||||
case 'html': {
|
||||
// FIXME: https://github.com/ianstormtaylor/slate/issues/1497 means
|
||||
// that we will silently discard nested blocks (e.g. nested lists) :(
|
||||
const fragment = this.html.deserialize(transfer.html);
|
||||
if (this.state.isRichTextEnabled) {
|
||||
return change.insertFragment(fragment.document);
|
||||
} else {
|
||||
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');
|
||||
}
|
||||
|
||||
if (this.state.editorState.blocks.some(
|
||||
block => ['code', 'block-quote', 'list-item'].includes(block.type)
|
||||
)) {
|
||||
// allow the user to terminate blocks by hitting return rather than sending a msg
|
||||
const editorState = this.state.editorState;
|
||||
|
||||
const lastBlock = editorState.blocks.last();
|
||||
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;
|
||||
}
|
||||
|
||||
const editorState = this.state.editorState;
|
||||
|
||||
let contentText;
|
||||
let contentHTML;
|
||||
|
||||
|
@ -1515,6 +1533,14 @@ export default class MessageComposerInput extends React.Component {
|
|||
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 (
|
||||
<div className="mx_MessageComposer_input_wrapper" onClick={this.focusComposer}>
|
||||
<div className="mx_MessageComposer_autocomplete_wrapper">
|
||||
|
@ -1536,7 +1562,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
<Editor ref="editor"
|
||||
dir="auto"
|
||||
className="mx_MessageComposer_editor"
|
||||
placeholder={this.props.placeholder}
|
||||
placeholder={placeholder}
|
||||
value={this.state.editorState}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
|
@ -1546,7 +1572,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
renderNode={this.renderNode}
|
||||
renderMark={this.renderMark}
|
||||
// disable spell check for the placeholder because browsers don't like "unencrypted"
|
||||
spellCheck={!this.state.editorState.document.isEmpty}
|
||||
spellCheck={!isEmpty}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,13 @@ class MessageComposerStore {
|
|||
|
||||
setEditorState(roomId: string, editorState: Value, richText: boolean) {
|
||||
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,
|
||||
}));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue