Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Weblate 2018-07-17 16:15:11 +00:00
commit 4be222c6aa
4 changed files with 58 additions and 24 deletions

View file

@ -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

View file

@ -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';

View file

@ -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>

View file

@ -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,
})); }));
} }