diff options
| author | Henry Jameson <me@hjkos.com> | 2019-08-31 22:38:02 +0300 |
|---|---|---|
| committer | Henry Jameson <me@hjkos.com> | 2019-08-31 22:38:02 +0300 |
| commit | 18ec13d796c0928d09fa93de4117822d2e35502c (patch) | |
| tree | 1cfb4d68a246c604396bb64bbba3e69bdf4fe511 /src/components/emoji-input/suggestor.js | |
| parent | b3e9a5a71819c7d3a4b35c5b6ad551785a7ba44f (diff) | |
| parent | 018a650166a5dce0878b696359a999ab67634cfc (diff) | |
Merge remote-tracking branch 'upstream/develop' into docs
* upstream/develop: (193 commits)
fix user avatar fallback logic
remove dead code
make bio textarea resizable vertically only
remove dead code
remove dead code
fix crazy watch logic in conversation
show three dot button only if needed
hide mute conversation button to guests
update keyBy
generate idObj at timeline level
fix pin showing logic in conversation
Show a message when JS is disabled
Initialize chat only if user is logged in and it wasn't initialized before
i18n/Update Japanese
i18n/Update pedantic Japanese
sync profile tab state with location query
refactor TabSwitcher
use better name of controlled prop
fix potential bug to render active tab in controlled way
remove unused param
...
Diffstat (limited to 'src/components/emoji-input/suggestor.js')
| -rw-r--r-- | src/components/emoji-input/suggestor.js | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/components/emoji-input/suggestor.js b/src/components/emoji-input/suggestor.js new file mode 100644 index 00000000..aec5c39d --- /dev/null +++ b/src/components/emoji-input/suggestor.js @@ -0,0 +1,94 @@ +import { debounce } from 'lodash' +/** + * suggest - generates a suggestor function to be used by emoji-input + * data: object providing source information for specific types of suggestions: + * data.emoji - optional, an array of all emoji available i.e. + * (state.instance.emoji + state.instance.customEmoji) + * data.users - optional, an array of all known users + * updateUsersList - optional, a function to search and append to users + * + * Depending on data present one or both (or none) can be present, so if field + * doesn't support user linking you can just provide only emoji. + */ + +const debounceUserSearch = debounce((data, input) => { + data.updateUsersList(input) +}, 500, { leading: true, trailing: false }) + +export default data => input => { + const firstChar = input[0] + if (firstChar === ':' && data.emoji) { + return suggestEmoji(data.emoji)(input) + } + if (firstChar === '@' && data.users) { + return suggestUsers(data)(input) + } + return [] +} + +export const suggestEmoji = emojis => input => { + const noPrefix = input.toLowerCase().substr(1) + return emojis + .filter(({ displayText }) => displayText.toLowerCase().startsWith(noPrefix)) + .sort((a, b) => { + let aScore = 0 + let bScore = 0 + + // Make custom emojis a priority + aScore += a.imageUrl ? 10 : 0 + bScore += b.imageUrl ? 10 : 0 + + // Sort alphabetically + const alphabetically = a.displayText > b.displayText ? 1 : -1 + + return bScore - aScore + alphabetically + }) +} + +export const suggestUsers = data => input => { + const noPrefix = input.toLowerCase().substr(1) + const users = data.users + + const newUsers = users.filter( + user => + user.screen_name.toLowerCase().startsWith(noPrefix) || + user.name.toLowerCase().startsWith(noPrefix) + + /* taking only 20 results so that sorting is a bit cheaper, we display + * only 5 anyway. could be inaccurate, but we ideally we should query + * backend anyway + */ + ).slice(0, 20).sort((a, b) => { + let aScore = 0 + let bScore = 0 + + // Matches on screen name (i.e. user@instance) makes a priority + aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 + bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 + + // Matches on name takes second priority + aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 + bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 + + const diff = (bScore - aScore) * 10 + + // Then sort alphabetically + const nameAlphabetically = a.name > b.name ? 1 : -1 + const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1 + + return diff + nameAlphabetically + screenNameAlphabetically + /* eslint-disable camelcase */ + }).map(({ screen_name, name, profile_image_url_original }) => ({ + displayText: screen_name, + detailText: name, + imageUrl: profile_image_url_original, + replacement: '@' + screen_name + ' ' + })) + + // BE search users if there are no matches + if (newUsers.length === 0 && data.updateUsersList) { + debounceUserSearch(data, noPrefix) + } + return newUsers + /* eslint-enable camelcase */ +} |
