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

Moar Slate Fixes #2082

Merged
merged 6 commits into from
Jul 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"react-dom": "^15.6.0",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"resize-observer-polyfill": "^1.5.0",
"slate": "0.33.4",
"slate": "0.34.7",
"slate-react": "^0.12.4",
"slate-html-serializer": "^0.6.1",
"slate-md-serializer": "matrix-org/slate-md-serializer#f7c4ad3",
Expand Down
3 changes: 2 additions & 1 deletion res/css/views/rooms/_Autocomplete.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@
flex-flow: wrap;
}

.mx_Autocomplete_Completion.selected {
.mx_Autocomplete_Completion.selected,
.mx_Autocomplete_Completion:hover {
background: $menu-bg-color;
outline: none;
}
Expand Down
34 changes: 11 additions & 23 deletions src/RichText.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,25 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';

import * as sdk from './index';
import * as emojione from 'emojione';

import { SelectionRange } from "./autocomplete/Autocompleter";


export function unicodeToEmojiUri(str) {
let replaceWith, unicode, alt;
if ((!emojione.unicodeAlt) || (emojione.sprites)) {
// if we are using the shortname as the alt tag then we need a reversed array to map unicode code point to shortnames
const mappedUnicode = emojione.mapUnicodeToShort();
}

str = str.replace(emojione.regUnicode, function(unicodeChar) {
if ( (typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap)) ) {
// if the unicodeChar doesnt exist just return the entire match
const mappedUnicode = emojione.mapUnicodeToShort();

// remove any zero width joiners/spaces used in conjugate emojis as the emojione URIs don't contain them
return str.replace(emojione.regUnicode, function(unicodeChar) {
if ((typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap))) {
// if the unicodeChar doesn't exist just return the entire match
return unicodeChar;
} else {
// Remove variant selector VS16 (explicitly emoji) as it is unnecessary and leads to an incorrect URL below
if (unicodeChar.length == 2 && unicodeChar[1] == '\ufe0f') {
unicodeChar = unicodeChar[0];
}

// get the unicode codepoint from the actual char
unicode = emojione.jsEscapeMap[unicodeChar];
const unicode = emojione.jsEscapeMap[unicodeChar];

const short = mappedUnicode[unicode];
const fname = emojione.emojioneList[short].fname;

return emojione.imagePathSVG+unicode+'.svg'+emojione.cacheBustParam;
return emojione.imagePathSVG+fname+'.svg'+emojione.cacheBustParam;
}
});

return str;
}
2 changes: 1 addition & 1 deletion src/autocomplete/CommandProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class CommandProvider extends AutocompleteProvider {
const name = command[1].substr(1); // strip leading `/`
if (CommandMap[name]) {
// some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments
if (!CommandMap[name].hideCompletionAfterSpace) return [];
if (CommandMap[name].hideCompletionAfterSpace) return [];
matches = [CommandMap[name]];
}
} else {
Expand Down
10 changes: 7 additions & 3 deletions src/autocomplete/PlainWithPillsSerializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,17 @@ class PlainWithPillsSerializer {
} else if (node.type == 'emoji') {
return node.data.get('emojiUnicode');
} else if (node.type == 'pill') {
const completion = node.data.get('completion');
// over the wire the @room pill is just plaintext
if (completion === '@room') return completion;

switch (this.pillFormat) {
case 'plain':
return node.data.get('completion');
return completion;
case 'md':
return `[${ node.data.get('completion') }](${ node.data.get('href') })`;
return `[${ completion }](${ node.data.get('href') })`;
case 'id':
return node.data.get('completionId') || node.data.get('completion');
return node.data.get('completionId') || completion;
}
} else if (node.nodes) {
return node.nodes.map(this._serializeNode).join('');
Expand Down
14 changes: 7 additions & 7 deletions src/components/views/messages/TextualBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ module.exports = React.createClass({
// update the current node with one that's now taken its place
node = pillContainer;
}
} else if (node.nodeType == Node.TEXT_NODE) {
} else if (node.nodeType === Node.TEXT_NODE) {
const Pill = sdk.getComponent('elements.Pill');

let currentTextNode = node;
Expand Down Expand Up @@ -232,6 +232,12 @@ module.exports = React.createClass({
if (atRoomRule && pushProcessor.ruleMatchesEvent(atRoomRule, this.props.mxEvent)) {
// Now replace all those nodes with Pills
for (const roomNotifTextNode of roomNotifTextNodes) {
// Set the next node to be processed to the one after the node
// we're adding now, since we've just inserted nodes into the structure
// we're iterating over.
// Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once
node = roomNotifTextNode.nextSibling;

const pillContainer = document.createElement('span');
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
const pill = <Pill
Expand All @@ -243,12 +249,6 @@ module.exports = React.createClass({

ReactDOM.render(pill, pillContainer);
roomNotifTextNode.parentNode.replaceChild(pillContainer, roomNotifTextNode);

// Set the next node to be processed to the one after the node
// we're adding now, since we've just inserted nodes into the structure
// we're iterating over.
// Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once
node = roomNotifTextNode.nextSibling;
}
// Nothing else to do for a text node (and we don't need to advance
// the loop pointer because we did it above)
Expand Down
9 changes: 4 additions & 5 deletions src/components/views/rooms/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,12 @@ export default class Autocomplete extends React.Component {
return done.promise;
}

onCompletionClicked(): boolean {
if (this.countCompletions() === 0 || this.state.selectionOffset === COMPOSER_SELECTED) {
onCompletionClicked(selectionOffset: number): boolean {
if (this.countCompletions() === 0 || selectionOffset === COMPOSER_SELECTED) {
return false;
}

this.props.onConfirm(this.state.completionList[this.state.selectionOffset - 1]);
this.props.onConfirm(this.state.completionList[selectionOffset - 1]);
this.hide();

return true;
Expand Down Expand Up @@ -264,8 +264,7 @@ export default class Autocomplete extends React.Component {
position++;

const onClick = () => {
this.setSelection(componentPosition);
this.onCompletionClicked();
this.onCompletionClicked(componentPosition);
};

return React.cloneElement(completion.component, {
Expand Down
33 changes: 26 additions & 7 deletions src/components/views/rooms/MessageComposerInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,17 +387,28 @@ export default class MessageComposerInput extends React.Component {
const anchorText = editorState.anchorText;
if ((!anchorText || anchorText.text === '') && editorState.anchorBlock.nodes.size === 1) {
// replace the current block rather than split the block
// XXX: this destroys our focus by deleting the thing we are anchored/focused on
change = change.replaceNodeByKey(editorState.anchorBlock.key, quote);
}
else {
} else {
// insert it into the middle of the block (splitting it)
change = change.insertBlock(quote);
}
change = change.insertFragmentByKey(quote.key, 0, fragment.document)
.focus();

// XXX: heuristic to strip out wrapping <p> which breaks quoting in RT mode
if (fragment.document.nodes.size && fragment.document.nodes.get(0).type === DEFAULT_NODE) {
change = change.insertFragmentByKey(quote.key, 0, fragment.document.nodes.get(0));
} else {
change = change.insertFragmentByKey(quote.key, 0, fragment.document);
}

// XXX: this is to bring back the focus in a sane place and add a paragraph after it
change = change.select({
anchorKey: quote.key,
focusKey: quote.key,
}).collapseToEndOfBlock().insertBlock(Block.create(DEFAULT_NODE)).focus();

this.onChange(change);
}
else {
} else {
let fragmentChange = fragment.change();
fragmentChange.moveToRangeOf(fragment.document)
.wrapBlock(quote);
Expand Down Expand Up @@ -1301,6 +1312,14 @@ export default class MessageComposerInput extends React.Component {
await this.setDisplayedCompletion(null); // restore originalEditorState
};

onAutocompleteConfirm = (displayedCompletion: ?Completion) => {
this.focusComposer();
// XXX: this fails if the composer isn't focused so focus it and delay the completion until next tick
setImmediate(() => {
this.setDisplayedCompletion(displayedCompletion);
});
};

/* If passed null, restores the original editor content from state.originalEditorState.
* If passed a non-null displayedCompletion, modifies state.originalEditorState to compute new state.editorState.
*/
Expand Down Expand Up @@ -1563,7 +1582,7 @@ export default class MessageComposerInput extends React.Component {
<Autocomplete
ref={(e) => this.autocomplete = e}
room={this.props.room}
onConfirm={this.setDisplayedCompletion}
onConfirm={this.onAutocompleteConfirm}
onSelectionChange={this.setDisplayedCompletion}
query={ this.suppressAutoComplete ? '' : this.getAutocompleteQuery(activeEditorState) }
selection={this.getSelectionRange(activeEditorState)}
Expand Down