From 7ce9a391195f45895b5313b9b8d390437764c99c Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sun, 25 Mar 2018 12:24:38 -0700 Subject: [PATCH] add emoji autosuggest --- routes/_actions/emoji.js | 18 +++++ .../compose/ComposeAutosuggest.html | 45 +++++++++--- .../compose/ComposeAutosuggestionList.html | 68 ++++++++++++------- routes/_components/compose/ComposeInput.html | 21 ++++-- .../_components/compose/ComposeToolbar.html | 2 - routes/_store/observers/instanceObservers.js | 2 + tests/roles.js | 2 +- tests/spec/018-compose-autosuggest.js | 46 +++++++++++++ tests/utils.js | 4 ++ 9 files changed, 163 insertions(+), 45 deletions(-) create mode 100644 tests/spec/018-compose-autosuggest.js diff --git a/routes/_actions/emoji.js b/routes/_actions/emoji.js index 2ced1a98..7b78b26d 100644 --- a/routes/_actions/emoji.js +++ b/routes/_actions/emoji.js @@ -25,3 +25,21 @@ export function insertEmoji (realm, emoji) { let newText = `${pre}:${emoji.shortcode}: ${post}` store.setComposeData(realm, {text: newText}) } + +export function insertEmojiAtPosition (realm, emoji, startIndex, endIndex) { + let oldText = store.getComposeData(realm, 'text') + let pre = oldText ? substring(oldText, 0, startIndex) : '' + let post = oldText ? substring(oldText, endIndex) : '' + let newText = `${pre}:${emoji.shortcode}: ${post}` + store.setComposeData(realm, {text: newText}) +} + +export async function clickSelectedAutosuggestionEmoji (realm) { + let selectionStart = store.get('composeSelectionStart') + let searchText = store.get('composeAutosuggestionSearchText') + let selection = store.get('composeAutosuggestionSelected') || 0 + let emoji = store.get('composeAutosuggestionSearchResults')[selection] + let startIndex = selectionStart - searchText.length + let endIndex = selectionStart + await insertEmojiAtPosition(realm, emoji, startIndex, endIndex) +} diff --git a/routes/_components/compose/ComposeAutosuggest.html b/routes/_components/compose/ComposeAutosuggest.html index 88833e60..7d209b32 100644 --- a/routes/_components/compose/ComposeAutosuggest.html +++ b/routes/_components/compose/ComposeAutosuggest.html @@ -2,7 +2,8 @@ aria-hidden="true" > @@ -32,6 +33,7 @@ import { store } from '../../_store/store' import { database } from '../../_database/database' import { insertUsername } from '../../_actions/compose' + import { insertEmojiAtPosition } from '../../_actions/emoji' import { scheduleIdleTask } from '../../_utils/scheduleIdleTask' import { once } from '../../_utils/once' import ComposeAutosuggestionList from './ComposeAutosuggestionList.html' @@ -39,7 +41,8 @@ const SEARCH_RESULTS_LIMIT = 4 const DATABASE_SEARCH_RESULTS_LIMIT = 30 const MIN_PREFIX_LENGTH = 1 - const SEARCH_REGEX = new RegExp(`(?:\\s|^)(@\\S{${MIN_PREFIX_LENGTH},})$`) + const ACCOUNT_SEARCH_REGEX = new RegExp(`(?:\\s|^)(@\\S{${MIN_PREFIX_LENGTH},})$`) + const EMOJI_SEARCH_REGEX = new RegExp(`(?:\\s|^)(:[^:]{${MIN_PREFIX_LENGTH},})$`) export default { oncreate() { @@ -63,7 +66,7 @@ } else { Promise.race([ new Promise(resolve => setTimeout(resolve, 200)), - new Promise(resolve => this.once('userSelected', resolve)) + new Promise(resolve => this.once('autosuggestItemSelected', resolve)) ]).then(updateFocusedState) } }) @@ -71,11 +74,15 @@ if (!searchText) { return } - let results = await this.search(searchText) + let type = searchText.startsWith('@') ? 'account' : 'emoji' + let results = (type === 'account') + ? await this.searchAccounts(searchText) + : await this.searchEmoji(searchText) this.store.set({ composeAutosuggestionSelected: 0, composeAutosuggestionSearchText: searchText, - composeAutosuggestionSearchResults: results + composeAutosuggestionSearchResults: results, + composeAutosuggestionType: type, }) }) this.observe('shown', shown => { @@ -84,26 +91,41 @@ }, methods: { once: once, - onUserSelected(account) { - this.fire('userSelected') + onClick(item) { + this.fire('autosuggestItemSelected') let realm = this.get('realm') let selectionStart = this.store.get('composeSelectionStart') let searchText = this.store.get('composeAutosuggestionSearchText') let startIndex = selectionStart - searchText.length let endIndex = selectionStart - /* no await */ insertUsername(realm, account.acct, startIndex, endIndex) + if (item.acct) { + /* no await */ insertUsername(realm, item.acct, startIndex, endIndex) + } else { + /* no await */ insertEmojiAtPosition(realm, item, startIndex, endIndex) + } + }, - async search(searchText) { + async searchAccounts(searchText) { + searchText = searchText.substring(1) let currentInstance = this.store.get('currentInstance') let results = await database.searchAccountsByUsername( - currentInstance, searchText.substring(1), DATABASE_SEARCH_RESULTS_LIMIT) + currentInstance, searchText, DATABASE_SEARCH_RESULTS_LIMIT) return results.slice(0, SEARCH_RESULTS_LIMIT) + }, + searchEmoji(searchText) { + searchText = searchText.toLowerCase().substring(1) + let customEmoji = this.store.get('currentCustomEmoji') + let results = customEmoji.filter(emoji => emoji.shortcode.toLowerCase().startsWith(searchText)) + .sort((a, b) => a.shortcode.toLowerCase() < b.shortcode.toLowerCase() ? -1 : 1) + .slice(0, SEARCH_RESULTS_LIMIT) + return results } }, computed: { composeSelectionStart: ($composeSelectionStart) => $composeSelectionStart, composeFocused: ($composeFocused) => $composeFocused, searchResults: ($composeAutosuggestionSearchResults) => $composeAutosuggestionSearchResults || [], + type: ($composeAutosuggestionType) => $composeAutosuggestionType || 'account', selected: ($composeAutosuggestionSelected) => $composeAutosuggestionSelected || 0, searchText: (text, composeSelectionStartDeferred) => { let selectionStart = composeSelectionStartDeferred || 0 @@ -111,7 +133,8 @@ return } - let match = text.substring(0, selectionStart).match(SEARCH_REGEX) + let textUpToCursor = text.substring(0, selectionStart) + let match = textUpToCursor.match(ACCOUNT_SEARCH_REGEX) || textUpToCursor.match(EMOJI_SEARCH_REGEX) return match && match[1] }, shown: (composeFocusedDeferred, searchText, searchResults) => { diff --git a/routes/_components/compose/ComposeAutosuggestionList.html b/routes/_components/compose/ComposeAutosuggestionList.html index 68978d8b..6ee96d19 100644 --- a/routes/_components/compose/ComposeAutosuggestionList.html +++ b/routes/_components/compose/ComposeAutosuggestionList.html @@ -1,61 +1,76 @@ -