Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit cf6ce0c

Browse files
authored
Merge pull request #2082 from matrix-org/t3chguy/slate_cont2
Moar Slate Fixes
2 parents c19a5bd + 020e714 commit cf6ce0c

File tree

8 files changed

+59
-48
lines changed

8 files changed

+59
-48
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
"react-dom": "^15.6.0",
8686
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
8787
"resize-observer-polyfill": "^1.5.0",
88-
"slate": "0.33.4",
88+
"slate": "0.34.7",
8989
"slate-react": "^0.12.4",
9090
"slate-html-serializer": "^0.6.1",
9191
"slate-md-serializer": "matrix-org/slate-md-serializer#f7c4ad3",

res/css/views/rooms/_Autocomplete.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@
6969
flex-flow: wrap;
7070
}
7171

72-
.mx_Autocomplete_Completion.selected {
72+
.mx_Autocomplete_Completion.selected,
73+
.mx_Autocomplete_Completion:hover {
7374
background: $menu-bg-color;
7475
outline: none;
7576
}

src/RichText.js

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,25 @@ See the License for the specific language governing permissions and
1616
limitations under the License.
1717
*/
1818

19-
import React from 'react';
20-
21-
import * as sdk from './index';
2219
import * as emojione from 'emojione';
2320

24-
import { SelectionRange } from "./autocomplete/Autocompleter";
25-
2621

2722
export function unicodeToEmojiUri(str) {
28-
let replaceWith, unicode, alt;
29-
if ((!emojione.unicodeAlt) || (emojione.sprites)) {
30-
// if we are using the shortname as the alt tag then we need a reversed array to map unicode code point to shortnames
31-
const mappedUnicode = emojione.mapUnicodeToShort();
32-
}
33-
34-
str = str.replace(emojione.regUnicode, function(unicodeChar) {
35-
if ( (typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap)) ) {
36-
// if the unicodeChar doesnt exist just return the entire match
23+
const mappedUnicode = emojione.mapUnicodeToShort();
24+
25+
// remove any zero width joiners/spaces used in conjugate emojis as the emojione URIs don't contain them
26+
return str.replace(emojione.regUnicode, function(unicodeChar) {
27+
if ((typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap))) {
28+
// if the unicodeChar doesn't exist just return the entire match
3729
return unicodeChar;
3830
} else {
39-
// Remove variant selector VS16 (explicitly emoji) as it is unnecessary and leads to an incorrect URL below
40-
if (unicodeChar.length == 2 && unicodeChar[1] == '\ufe0f') {
41-
unicodeChar = unicodeChar[0];
42-
}
43-
4431
// get the unicode codepoint from the actual char
45-
unicode = emojione.jsEscapeMap[unicodeChar];
32+
const unicode = emojione.jsEscapeMap[unicodeChar];
33+
34+
const short = mappedUnicode[unicode];
35+
const fname = emojione.emojioneList[short].fname;
4636

47-
return emojione.imagePathSVG+unicode+'.svg'+emojione.cacheBustParam;
37+
return emojione.imagePathSVG+fname+'.svg'+emojione.cacheBustParam;
4838
}
4939
});
50-
51-
return str;
5240
}

src/autocomplete/CommandProvider.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default class CommandProvider extends AutocompleteProvider {
4848
const name = command[1].substr(1); // strip leading `/`
4949
if (CommandMap[name]) {
5050
// some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments
51-
if (!CommandMap[name].hideCompletionAfterSpace) return [];
51+
if (CommandMap[name].hideCompletionAfterSpace) return [];
5252
matches = [CommandMap[name]];
5353
}
5454
} else {

src/autocomplete/PlainWithPillsSerializer.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,17 @@ class PlainWithPillsSerializer {
6464
} else if (node.type == 'emoji') {
6565
return node.data.get('emojiUnicode');
6666
} else if (node.type == 'pill') {
67+
const completion = node.data.get('completion');
68+
// over the wire the @room pill is just plaintext
69+
if (completion === '@room') return completion;
70+
6771
switch (this.pillFormat) {
6872
case 'plain':
69-
return node.data.get('completion');
73+
return completion;
7074
case 'md':
71-
return `[${ node.data.get('completion') }](${ node.data.get('href') })`;
75+
return `[${ completion }](${ node.data.get('href') })`;
7276
case 'id':
73-
return node.data.get('completionId') || node.data.get('completion');
77+
return node.data.get('completionId') || completion;
7478
}
7579
} else if (node.nodes) {
7680
return node.nodes.map(this._serializeNode).join('');

src/components/views/messages/TextualBody.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ module.exports = React.createClass({
203203
// update the current node with one that's now taken its place
204204
node = pillContainer;
205205
}
206-
} else if (node.nodeType == Node.TEXT_NODE) {
206+
} else if (node.nodeType === Node.TEXT_NODE) {
207207
const Pill = sdk.getComponent('elements.Pill');
208208

209209
let currentTextNode = node;
@@ -232,6 +232,12 @@ module.exports = React.createClass({
232232
if (atRoomRule && pushProcessor.ruleMatchesEvent(atRoomRule, this.props.mxEvent)) {
233233
// Now replace all those nodes with Pills
234234
for (const roomNotifTextNode of roomNotifTextNodes) {
235+
// Set the next node to be processed to the one after the node
236+
// we're adding now, since we've just inserted nodes into the structure
237+
// we're iterating over.
238+
// Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once
239+
node = roomNotifTextNode.nextSibling;
240+
235241
const pillContainer = document.createElement('span');
236242
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
237243
const pill = <Pill
@@ -243,12 +249,6 @@ module.exports = React.createClass({
243249

244250
ReactDOM.render(pill, pillContainer);
245251
roomNotifTextNode.parentNode.replaceChild(pillContainer, roomNotifTextNode);
246-
247-
// Set the next node to be processed to the one after the node
248-
// we're adding now, since we've just inserted nodes into the structure
249-
// we're iterating over.
250-
// Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once
251-
node = roomNotifTextNode.nextSibling;
252252
}
253253
// Nothing else to do for a text node (and we don't need to advance
254254
// the loop pointer because we did it above)

src/components/views/rooms/Autocomplete.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,12 @@ export default class Autocomplete extends React.Component {
216216
return done.promise;
217217
}
218218

219-
onCompletionClicked(): boolean {
220-
if (this.countCompletions() === 0 || this.state.selectionOffset === COMPOSER_SELECTED) {
219+
onCompletionClicked(selectionOffset: number): boolean {
220+
if (this.countCompletions() === 0 || selectionOffset === COMPOSER_SELECTED) {
221221
return false;
222222
}
223223

224-
this.props.onConfirm(this.state.completionList[this.state.selectionOffset - 1]);
224+
this.props.onConfirm(this.state.completionList[selectionOffset - 1]);
225225
this.hide();
226226

227227
return true;
@@ -264,8 +264,7 @@ export default class Autocomplete extends React.Component {
264264
position++;
265265

266266
const onClick = () => {
267-
this.setSelection(componentPosition);
268-
this.onCompletionClicked();
267+
this.onCompletionClicked(componentPosition);
269268
};
270269

271270
return React.cloneElement(completion.component, {

src/components/views/rooms/MessageComposerInput.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -387,17 +387,28 @@ export default class MessageComposerInput extends React.Component {
387387
const anchorText = editorState.anchorText;
388388
if ((!anchorText || anchorText.text === '') && editorState.anchorBlock.nodes.size === 1) {
389389
// replace the current block rather than split the block
390+
// XXX: this destroys our focus by deleting the thing we are anchored/focused on
390391
change = change.replaceNodeByKey(editorState.anchorBlock.key, quote);
391-
}
392-
else {
392+
} else {
393393
// insert it into the middle of the block (splitting it)
394394
change = change.insertBlock(quote);
395395
}
396-
change = change.insertFragmentByKey(quote.key, 0, fragment.document)
397-
.focus();
396+
397+
// XXX: heuristic to strip out wrapping <p> which breaks quoting in RT mode
398+
if (fragment.document.nodes.size && fragment.document.nodes.get(0).type === DEFAULT_NODE) {
399+
change = change.insertFragmentByKey(quote.key, 0, fragment.document.nodes.get(0));
400+
} else {
401+
change = change.insertFragmentByKey(quote.key, 0, fragment.document);
402+
}
403+
404+
// XXX: this is to bring back the focus in a sane place and add a paragraph after it
405+
change = change.select({
406+
anchorKey: quote.key,
407+
focusKey: quote.key,
408+
}).collapseToEndOfBlock().insertBlock(Block.create(DEFAULT_NODE)).focus();
409+
398410
this.onChange(change);
399-
}
400-
else {
411+
} else {
401412
let fragmentChange = fragment.change();
402413
fragmentChange.moveToRangeOf(fragment.document)
403414
.wrapBlock(quote);
@@ -1301,6 +1312,14 @@ export default class MessageComposerInput extends React.Component {
13011312
await this.setDisplayedCompletion(null); // restore originalEditorState
13021313
};
13031314

1315+
onAutocompleteConfirm = (displayedCompletion: ?Completion) => {
1316+
this.focusComposer();
1317+
// XXX: this fails if the composer isn't focused so focus it and delay the completion until next tick
1318+
setImmediate(() => {
1319+
this.setDisplayedCompletion(displayedCompletion);
1320+
});
1321+
};
1322+
13041323
/* If passed null, restores the original editor content from state.originalEditorState.
13051324
* If passed a non-null displayedCompletion, modifies state.originalEditorState to compute new state.editorState.
13061325
*/
@@ -1563,7 +1582,7 @@ export default class MessageComposerInput extends React.Component {
15631582
<Autocomplete
15641583
ref={(e) => this.autocomplete = e}
15651584
room={this.props.room}
1566-
onConfirm={this.setDisplayedCompletion}
1585+
onConfirm={this.onAutocompleteConfirm}
15671586
onSelectionChange={this.setDisplayedCompletion}
15681587
query={ this.suppressAutoComplete ? '' : this.getAutocompleteQuery(activeEditorState) }
15691588
selection={this.getSelectionRange(activeEditorState)}

0 commit comments

Comments
 (0)