From cbe5944ff0c2339253c175121ba8fcc80313e257 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 16 Jul 2018 22:04:42 +0100 Subject: [PATCH 1/9] preserve selection and history between room changes and refreshes Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/MessageComposerStore.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/stores/MessageComposerStore.js b/src/stores/MessageComposerStore.js index 0b22057f3c..f180d931bf 100644 --- a/src/stores/MessageComposerStore.js +++ b/src/stores/MessageComposerStore.js @@ -32,7 +32,12 @@ 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, + preserveHistory: true, + // XXX: this seems like a workaround for selection.isSet being based on anchorKey instead of anchorPath + preserveKeys: true, + }), rich_text: richText, })); } From 23b6abf8b727e6c478f163087b3340aaf5e33314 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 16 Jul 2018 22:25:41 +0100 Subject: [PATCH 2/9] no point saving history at this point, maybe future supported Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/MessageComposerStore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stores/MessageComposerStore.js b/src/stores/MessageComposerStore.js index f180d931bf..ab2dbfedec 100644 --- a/src/stores/MessageComposerStore.js +++ b/src/stores/MessageComposerStore.js @@ -34,7 +34,8 @@ class MessageComposerStore { localStorage.setItem(this._getKey(roomId), JSON.stringify({ editor_state: editorState.toJSON({ preserveSelection: true, - preserveHistory: 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, }), From 5b79e3bcd52db91ac924f317850412140eee019f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 17 Jul 2018 11:56:24 +0100 Subject: [PATCH 3/9] fix user autocomplete not adding `: ` if at the start Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/autocomplete/UserProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocomplete/UserProvider.js b/src/autocomplete/UserProvider.js index 7998337e2e..156aac2eb8 100644 --- a/src/autocomplete/UserProvider.js +++ b/src/autocomplete/UserProvider.js @@ -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: ( Date: Tue, 17 Jul 2018 12:30:22 +0100 Subject: [PATCH 4/9] allow user to break out of blockquote/etc by hitting enter twice Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/rooms/MessageComposerInput.js | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 5d6b12f339..2fae59a1fe 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -990,15 +990,29 @@ 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) - )) { + const editorState = this.state.editorState; + + let inBlock = false; + let inEmptyBlock = false; + + for (const block of editorState.blocks) { + if (['code', 'block-quote', 'list-item'].includes(block.type)) { + inBlock = true; + if (block.text === '') { + inEmptyBlock = true; + } + break; + } + } + + if (inEmptyBlock) { + // allow the user to cancel empty block by hitting return, useful in conjunction with below `inBlock` + return change.setBlocks(DEFAULT_NODE); + } else if (inBlock) { // allow the user to terminate blocks by hitting return rather than sending a msg return; } - const editorState = this.state.editorState; - let contentText; let contentHTML; From 8d4cead105a35a4ca23c3c5f281df6652f3ff551 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 17 Jul 2018 13:24:28 +0100 Subject: [PATCH 5/9] special-case 'inline-code' button because mark is called 'code' Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/MessageComposer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 657fd463fd..a7e02d16ae 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -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'; From 688776bc10d43a1676921217126daa0e988c55c4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 17 Jul 2018 13:29:40 +0100 Subject: [PATCH 6/9] allow enter to remove current block if its empty useful for lists Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/rooms/MessageComposerInput.js | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 2fae59a1fe..841fcccf06 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -992,24 +992,25 @@ export default class MessageComposerInput extends React.Component { const editorState = this.state.editorState; - let inBlock = false; - let inEmptyBlock = false; - - for (const block of editorState.blocks) { - if (['code', 'block-quote', 'list-item'].includes(block.type)) { - inBlock = true; - if (block.text === '') { - inEmptyBlock = true; - } - break; + 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'); } - } - if (inEmptyBlock) { - // allow the user to cancel empty block by hitting return, useful in conjunction with below `inBlock` - return change.setBlocks(DEFAULT_NODE); - } else if (inBlock) { - // allow the user to terminate blocks by hitting return rather than sending a msg + // 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; } From 414908611d5d688488e2c9c8d9e23e483a217725 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 17 Jul 2018 13:48:15 +0100 Subject: [PATCH 7/9] prevent exception if there is no text node at anchor Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/MessageComposerInput.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 841fcccf06..e4bd21cdf0 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -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); } From 1b467f6ca85185fc1d51bb029f80a0f6f6766d21 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 17 Jul 2018 16:02:37 +0100 Subject: [PATCH 8/9] fix pasting of plaintext content (prevent loads of

s) Fixes vector-im/riot-web#7043 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/rooms/MessageComposerInput.js | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index e4bd21cdf0..5885ae4e6a 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -970,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); } }; From 720a728278a3eeac27f8732ad3a510dcfd832e9b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 17 Jul 2018 16:13:33 +0100 Subject: [PATCH 9/9] workaround for placeholder being shown when empty text but block. Fixes vector-im/riot-web#7042 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/MessageComposerInput.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index 5885ae4e6a..1850601d8a 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -1533,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 (

@@ -1554,7 +1562,7 @@ export default class MessageComposerInput extends React.Component {