diff --git a/.eslintrc b/.eslintrc index 761fd2af2b..3f6c8e6953 100644 --- a/.eslintrc +++ b/.eslintrc @@ -36,7 +36,12 @@ "no-new-wrappers": ["error"], "no-invalid-regexp": ["error"], "no-extra-bind": ["error"], - "no-magic-numbers": ["error"], + "no-magic-numbers": ["error", { + "ignore": [-1, 0, 1], // usually used in array/string indexing + "ignoreArrayIndexes": true, + "enforceConst": true, + "detectObjects": true + }], "consistent-return": ["error"], "valid-jsdoc": ["error"], "no-use-before-define": ["error"], diff --git a/src/RichText.js b/src/RichText.js index 678a7de190..f4fa4883cb 100644 --- a/src/RichText.js +++ b/src/RichText.js @@ -9,6 +9,7 @@ import { SelectionState } from 'draft-js'; import * as sdk from './index'; +import * as emojione from 'emojione'; const BLOCK_RENDER_MAP = DefaultDraftBlockRenderMap.set('unstyled', { element: 'span' @@ -35,6 +36,8 @@ const MARKDOWN_REGEX = { const USERNAME_REGEX = /@\S+:\S+/g; const ROOM_REGEX = /#\S+:\S+/g; +let EMOJI_REGEX = null; +window.EMOJI_REGEX = EMOJI_REGEX = new RegExp(emojione.unicodeRegexp, 'g'); export function contentStateToHTML(contentState: ContentState): string { return contentState.getBlockMap().map((block) => { @@ -89,6 +92,7 @@ export function getScopedRTDecorators(scope: any): CompositeDecorator { return {avatar} {props.children}; } }; + let roomDecorator = { strategy: (contentBlock, callback) => { findWithRegex(ROOM_REGEX, contentBlock, callback); @@ -98,6 +102,16 @@ export function getScopedRTDecorators(scope: any): CompositeDecorator { } }; + // Unused for now, due to https://github.com/facebook/draft-js/issues/414 + let emojiDecorator = { + strategy: (contentBlock, callback) => { + findWithRegex(EMOJI_REGEX, contentBlock, callback); + }, + component: (props) => { + return + } + }; + return [usernameDecorator, roomDecorator]; } diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js index ca0c5df701..414b0f1ebb 100644 --- a/src/components/views/rooms/Autocomplete.js +++ b/src/components/views/rooms/Autocomplete.js @@ -11,26 +11,28 @@ export default class Autocomplete extends React.Component { completions: [], // how far down the completion list we are - selectionOffset: 0 + selectionOffset: 0, }; } componentWillReceiveProps(props, state) { - if(props.query == this.props.query) return; + if (props.query === this.props.query) { + return; + } - getCompletions(props.query, props.selection).map(completionResult => { + getCompletions(props.query, props.selection).forEach(completionResult => { try { completionResult.completions.then(completions => { let i = this.state.completions.findIndex( completion => completion.provider === completionResult.provider ); - i = i == -1 ? this.state.completions.length : i; + i = i === -1 ? this.state.completions.length : i; let newCompletions = Object.assign([], this.state.completions); completionResult.completions = completions; newCompletions[i] = completionResult; this.setState({ - completions: newCompletions + completions: newCompletions, }); }, err => { console.error(err); @@ -42,13 +44,25 @@ export default class Autocomplete extends React.Component { }); } - onUpArrow() { - this.setState({selectionOffset: this.state.selectionOffset - 1}); + countCompletions(): number { + return this.state.completions.map(completionResult => { + return completionResult.completions.length; + }).reduce((l, r) => l + r); + } + + // called from MessageComposerInput + onUpArrow(): boolean { + let completionCount = this.countCompletions(), + selectionOffset = (completionCount + this.state.selectionOffset - 1) % completionCount; + this.setState({selectionOffset}); return true; } - onDownArrow() { - this.setState({selectionOffset: this.state.selectionOffset + 1}); + // called from MessageComposerInput + onDownArrow(): boolean { + let completionCount = this.countCompletions(), + selectionOffset = (this.state.selectionOffset + 1) % completionCount; + this.setState({selectionOffset}); return true; } @@ -58,18 +72,20 @@ export default class Autocomplete extends React.Component { let completions = completionResult.completions.map((completion, i) => { let Component = completion.component; let className = classNames('mx_Autocomplete_Completion', { - 'selected': position == this.state.selectionOffset + 'selected': position === this.state.selectionOffset, }); let componentPosition = position; position++; - if(Component) { + if (Component) { return Component; } + + let onMouseOver = () => this.setState({selectionOffset: componentPosition}); return (
this.setState({selectionOffset: componentPosition})}> + onMouseOver={onMouseOver}> {completion.title} {completion.subtitle} @@ -82,7 +98,11 @@ export default class Autocomplete extends React.Component { return completions.length > 0 ? (
{completionResult.provider.getName()} - + {completions}
@@ -91,7 +111,11 @@ export default class Autocomplete extends React.Component { return (
- + {renderedCompletions}
@@ -101,5 +125,5 @@ export default class Autocomplete extends React.Component { Autocomplete.propTypes = { // the query string for which to show autocomplete suggestions - query: React.PropTypes.string.isRequired + query: React.PropTypes.string.isRequired, };