commit
						cf6ce0c1e2
					
				|  | @ -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", | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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(); | ||||
|     } | ||||
|     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
 | ||||
|     // 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]; | ||||
| 
 | ||||
|             return emojione.imagePathSVG+unicode+'.svg'+emojione.cacheBustParam; | ||||
|             const short = mappedUnicode[unicode]; | ||||
|             const fname = emojione.emojioneList[short].fname; | ||||
| 
 | ||||
|             return emojione.imagePathSVG+fname+'.svg'+emojione.cacheBustParam; | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     return str; | ||||
| } | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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(''); | ||||
|  |  | |||
|  | @ -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; | ||||
|  | @ -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 | ||||
|  | @ -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)
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  | @ -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, { | ||||
|  |  | |||
|  | @ -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); | ||||
|  | @ -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. | ||||
|      */ | ||||
|  | @ -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)} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker