forked from cybrespace/pinafore
parent
67e41e4fb0
commit
07fb5e867c
|
@ -0,0 +1,62 @@
|
|||
import { store } from '../_store/store'
|
||||
|
||||
export async function insertUsername (realm, username, startIndex, endIndex) {
|
||||
let { currentInstance } = store.get()
|
||||
let oldText = store.getComposeData(realm, 'text')
|
||||
let pre = oldText.substring(0, startIndex)
|
||||
let post = oldText.substring(endIndex)
|
||||
let newText = `${pre}@${username} ${post}`
|
||||
store.setComposeData(realm, {text: newText})
|
||||
store.setForAutosuggest(currentInstance, realm, {autosuggestSearchResults: []})
|
||||
}
|
||||
|
||||
export async function clickSelectedAutosuggestionUsername (realm) {
|
||||
let {
|
||||
composeSelectionStart,
|
||||
autosuggestSearchText,
|
||||
autosuggestSelected,
|
||||
autosuggestSearchResults
|
||||
} = store.get()
|
||||
let account = autosuggestSearchResults[autosuggestSelected]
|
||||
let startIndex = composeSelectionStart - autosuggestSearchText.length
|
||||
let endIndex = composeSelectionStart
|
||||
await insertUsername(realm, account.acct, startIndex, endIndex)
|
||||
}
|
||||
|
||||
export function insertEmojiAtPosition (realm, emoji, startIndex, endIndex) {
|
||||
let { currentInstance } = store.get()
|
||||
let oldText = store.getComposeData(realm, 'text') || ''
|
||||
let pre = oldText.substring(0, startIndex)
|
||||
let post = oldText.substring(endIndex)
|
||||
let newText = `${pre}:${emoji.shortcode}: ${post}`
|
||||
store.setComposeData(realm, {text: newText})
|
||||
store.setForAutosuggest(currentInstance, realm, {autosuggestSearchResults: []})
|
||||
}
|
||||
|
||||
export async function clickSelectedAutosuggestionEmoji (realm) {
|
||||
let {
|
||||
composeSelectionStart,
|
||||
autosuggestSearchText,
|
||||
autosuggestSelected,
|
||||
autosuggestSearchResults
|
||||
} = store.get()
|
||||
let emoji = autosuggestSearchResults[autosuggestSelected]
|
||||
let startIndex = composeSelectionStart - autosuggestSearchText.length
|
||||
let endIndex = composeSelectionStart
|
||||
await insertEmojiAtPosition(realm, emoji, startIndex, endIndex)
|
||||
}
|
||||
|
||||
export function selectAutosuggestItem (item) {
|
||||
let {
|
||||
currentComposeRealm,
|
||||
composeSelectionStart,
|
||||
autosuggestSearchText
|
||||
} = store.get()
|
||||
let startIndex = composeSelectionStart - autosuggestSearchText.length
|
||||
let endIndex = composeSelectionStart
|
||||
if (item.acct) {
|
||||
/* no await */ insertUsername(currentComposeRealm, item.acct, startIndex, endIndex)
|
||||
} else {
|
||||
/* no await */ insertEmojiAtPosition(currentComposeRealm, item, startIndex, endIndex)
|
||||
}
|
||||
}
|
|
@ -50,28 +50,6 @@ export async function postStatus (realm, text, inReplyToId, mediaIds,
|
|||
}
|
||||
}
|
||||
|
||||
export async function insertUsername (realm, username, startIndex, endIndex) {
|
||||
let oldText = store.getComposeData(realm, 'text')
|
||||
let pre = oldText.substring(0, startIndex)
|
||||
let post = oldText.substring(endIndex)
|
||||
let newText = `${pre}@${username} ${post}`
|
||||
store.setComposeData(realm, {text: newText})
|
||||
}
|
||||
|
||||
export async function clickSelectedAutosuggestionUsername (realm) {
|
||||
let {
|
||||
composeSelectionStart,
|
||||
composeAutosuggestionSearchText,
|
||||
composeAutosuggestionSelected,
|
||||
composeAutosuggestionSearchResults
|
||||
} = store.get()
|
||||
composeAutosuggestionSelected = composeAutosuggestionSelected || 0
|
||||
let account = composeAutosuggestionSearchResults[composeAutosuggestionSelected]
|
||||
let startIndex = composeSelectionStart - composeAutosuggestionSearchText.length
|
||||
let endIndex = composeSelectionStart
|
||||
await insertUsername(realm, account.acct, startIndex, endIndex)
|
||||
}
|
||||
|
||||
export function setReplySpoiler (realm, spoiler) {
|
||||
let contentWarning = store.getComposeData(realm, 'contentWarning')
|
||||
let contentWarningShown = store.getComposeData(realm, 'contentWarningShown')
|
||||
|
|
|
@ -28,25 +28,3 @@ 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(0, startIndex)
|
||||
let post = oldText.substring(endIndex)
|
||||
let newText = `${pre}:${emoji.shortcode}: ${post}`
|
||||
store.setComposeData(realm, {text: newText})
|
||||
}
|
||||
|
||||
export async function clickSelectedAutosuggestionEmoji (realm) {
|
||||
let {
|
||||
composeSelectionStart,
|
||||
composeAutosuggestionSearchText,
|
||||
composeAutosuggestionSelected,
|
||||
composeAutosuggestionSearchResults
|
||||
} = store.get()
|
||||
composeAutosuggestionSelected = composeAutosuggestionSelected || 0
|
||||
let emoji = composeAutosuggestionSearchResults[composeAutosuggestionSelected]
|
||||
let startIndex = composeSelectionStart - composeAutosuggestionSearchText.length
|
||||
let endIndex = composeSelectionStart
|
||||
await insertEmojiAtPosition(realm, emoji, startIndex, endIndex)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<div class="compose-autosuggest {shown ? 'shown' : ''} {realm === 'dialog' ? 'is-dialog' : ''}"
|
||||
aria-hidden="true" >
|
||||
<ComposeAutosuggestionList
|
||||
items={searchResults}
|
||||
items={autosuggestSearchResults}
|
||||
on:click="onClick(event)"
|
||||
{type}
|
||||
{selected}
|
||||
type={autosuggestType}
|
||||
selected={autosuggestSelected}
|
||||
/>
|
||||
</div>
|
||||
<style>
|
||||
|
@ -39,134 +39,65 @@
|
|||
</style>
|
||||
<script>
|
||||
import { store } from '../../_store/store'
|
||||
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'
|
||||
import {
|
||||
searchAccountsByUsername as searchAccountsByUsernameInDatabase
|
||||
} from '../../_database/accountsAndRelationships'
|
||||
import get from 'lodash-es/get'
|
||||
import { selectAutosuggestItem } from '../../_actions/autosuggest'
|
||||
import { observe } from 'svelte-extras'
|
||||
|
||||
const SEARCH_RESULTS_LIMIT = 4
|
||||
const DATABASE_SEARCH_RESULTS_LIMIT = 30
|
||||
const MIN_PREFIX_LENGTH = 1
|
||||
const ACCOUNT_SEARCH_REGEX = new RegExp(`(?:\\s|^)(@\\S{${MIN_PREFIX_LENGTH},})$`)
|
||||
const EMOJI_SEARCH_REGEX = new RegExp(`(?:\\s|^)(:[^:]{${MIN_PREFIX_LENGTH},})$`)
|
||||
import { once } from '../../_utils/once'
|
||||
|
||||
export default {
|
||||
oncreate () {
|
||||
// perf improves for input responsiveness
|
||||
this.observe('composeSelectionStart', () => {
|
||||
scheduleIdleTask(() => {
|
||||
let { composeSelectionStart } = this.get()
|
||||
this.set({composeSelectionStartDeferred: composeSelectionStart})
|
||||
})
|
||||
})
|
||||
this.observe('composeFocused', (composeFocused) => {
|
||||
let updateFocusedState = () => {
|
||||
scheduleIdleTask(() => {
|
||||
let { composeFocused } = this.get()
|
||||
this.set({composeFocusedDeferred: composeFocused})
|
||||
})
|
||||
}
|
||||
|
||||
this._promiseChain = Promise.resolve()
|
||||
this.observe('shouldBeShown', (shouldBeShown) => {
|
||||
// TODO: hack so that when the user clicks the button, and the textarea blurs,
|
||||
// we don't immediately hide the dropdown which would cause the click to get lost
|
||||
if (composeFocused) {
|
||||
updateFocusedState()
|
||||
} else {
|
||||
Promise.race([
|
||||
new Promise(resolve => setTimeout(resolve, 200)),
|
||||
new Promise(resolve => this.once('autosuggestItemSelected', resolve))
|
||||
]).then(updateFocusedState)
|
||||
}
|
||||
})
|
||||
this.observe('searchText', async searchText => {
|
||||
let { thisComposeFocused } = this.get()
|
||||
if (!thisComposeFocused || !searchText) {
|
||||
return
|
||||
}
|
||||
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,
|
||||
composeAutosuggestionType: type
|
||||
this._promiseChain = this._promiseChain.then(() => {
|
||||
if (!shouldBeShown) {
|
||||
return Promise.race([
|
||||
new Promise(resolve => setTimeout(resolve, 200)),
|
||||
new Promise(resolve => this.once('autosuggestItemSelected', resolve))
|
||||
])
|
||||
}
|
||||
}).then(() => {
|
||||
this.set({shown: shouldBeShown})
|
||||
})
|
||||
})
|
||||
this.observe('shown', shown => {
|
||||
let { thisComposeFocused } = this.get()
|
||||
if (!thisComposeFocused) {
|
||||
return
|
||||
}
|
||||
this.store.set({composeAutosuggestionShown: shown})
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
observe,
|
||||
once,
|
||||
onClick (item) {
|
||||
this.fire('autosuggestItemSelected')
|
||||
let { realm } = this.get()
|
||||
let { composeSelectionStart, composeAutosuggestionSearchText } = this.store.get()
|
||||
let startIndex = composeSelectionStart - composeAutosuggestionSearchText.length
|
||||
let endIndex = composeSelectionStart
|
||||
if (item.acct) {
|
||||
/* no await */ insertUsername(realm, item.acct, startIndex, endIndex)
|
||||
} else {
|
||||
/* no await */ insertEmojiAtPosition(realm, item, startIndex, endIndex)
|
||||
}
|
||||
},
|
||||
async searchAccounts (searchText) {
|
||||
searchText = searchText.substring(1)
|
||||
let { currentInstance } = this.store.get()
|
||||
let results = await searchAccountsByUsernameInDatabase(
|
||||
currentInstance, searchText, DATABASE_SEARCH_RESULTS_LIMIT)
|
||||
return results.slice(0, SEARCH_RESULTS_LIMIT)
|
||||
},
|
||||
searchEmoji (searchText) {
|
||||
searchText = searchText.toLowerCase().substring(1)
|
||||
let { currentCustomEmoji } = this.store.get()
|
||||
let results = currentCustomEmoji.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
|
||||
selectAutosuggestItem(item)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
composeSelectionStart: ({ $composeSelectionStart }) => $composeSelectionStart,
|
||||
composeFocused: ({ $composeFocused }) => $composeFocused,
|
||||
thisComposeFocused: ({ composeFocusedDeferred, realm }) => composeFocusedDeferred === realm,
|
||||
searchResults: ({ $composeAutosuggestionSearchResults }) => $composeAutosuggestionSearchResults || [],
|
||||
type: ({ $composeAutosuggestionType }) => $composeAutosuggestionType || 'account',
|
||||
selected: ({ $composeAutosuggestionSelected }) => $composeAutosuggestionSelected || 0,
|
||||
searchText: ({ text, composeSelectionStartDeferred, thisComposeFocused }) => {
|
||||
if (!thisComposeFocused) {
|
||||
return
|
||||
}
|
||||
let selectionStart = composeSelectionStartDeferred
|
||||
if (!text || selectionStart < MIN_PREFIX_LENGTH) {
|
||||
return
|
||||
}
|
||||
|
||||
let textUpToCursor = text.substring(0, selectionStart)
|
||||
let match = textUpToCursor.match(ACCOUNT_SEARCH_REGEX) || textUpToCursor.match(EMOJI_SEARCH_REGEX)
|
||||
return match && match[1]
|
||||
},
|
||||
shown: ({ thisComposeFocused, searchText, searchResults }) => {
|
||||
return !!(thisComposeFocused &&
|
||||
searchText &&
|
||||
searchResults.length)
|
||||
}
|
||||
/* eslint-disable camelcase */
|
||||
composeSelectionStart: ({ $autosuggestData_composeSelectionStart, $currentInstance, realm }) => (
|
||||
get($autosuggestData_composeSelectionStart, [$currentInstance, realm], 0)
|
||||
),
|
||||
composeFocused: ({ $autosuggestData_composeFocused, $currentInstance, realm }) => (
|
||||
get($autosuggestData_composeFocused, [$currentInstance, realm], false)
|
||||
),
|
||||
autosuggestSearchResults: ({ $autosuggestData_autosuggestSearchResults, $currentInstance, realm }) => (
|
||||
get($autosuggestData_autosuggestSearchResults, [$currentInstance, realm], [])
|
||||
),
|
||||
autosuggestType: ({ $autosuggestData_autosuggestType, $currentInstance, realm }) => (
|
||||
get($autosuggestData_autosuggestType, [$currentInstance, realm])
|
||||
),
|
||||
autosuggestSelected: ({ $autosuggestData_autosuggestSelected, $currentInstance, realm }) => (
|
||||
get($autosuggestData_autosuggestSelected, [$currentInstance, realm], 0)
|
||||
),
|
||||
autosuggestSearchText: ({ $autosuggestData_autosuggestSelected, $currentInstance, realm }) => (
|
||||
get($autosuggestData_autosuggestSelected, [$currentInstance, realm])
|
||||
),
|
||||
/* eslint-enable camelcase */
|
||||
shouldBeShown: ({ realm, $autosuggestShown, composeFocused }) => (
|
||||
!!($autosuggestShown && composeFocused)
|
||||
)
|
||||
},
|
||||
data: () => ({
|
||||
composeFocusedDeferred: void 0,
|
||||
composeSelectionStartDeferred: 0
|
||||
shown: false
|
||||
}),
|
||||
store: () => store,
|
||||
components: {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<li class="compose-autosuggest-list-item">
|
||||
<button class="compose-autosuggest-list-button {i === selected ? 'selected' : ''}"
|
||||
tabindex="0"
|
||||
on:click="fire('click', item)">
|
||||
on:click="onClick(event, item)">
|
||||
<div class="compose-autosuggest-list-grid">
|
||||
{#if type === 'account'}
|
||||
<Avatar
|
||||
|
@ -102,6 +102,13 @@
|
|||
|
||||
export default {
|
||||
store: () => store,
|
||||
methods: {
|
||||
onClick (event, item) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.fire('click', item)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Avatar
|
||||
}
|
||||
|
|
|
@ -33,8 +33,10 @@
|
|||
import debounce from 'lodash-es/debounce'
|
||||
import { mark, stop } from '../../_utils/marks'
|
||||
import { selectionChange } from '../../_utils/events'
|
||||
import { clickSelectedAutosuggestionUsername } from '../../_actions/compose'
|
||||
import { clickSelectedAutosuggestionEmoji } from '../../_actions/emoji'
|
||||
import {
|
||||
clickSelectedAutosuggestionUsername,
|
||||
clickSelectedAutosuggestionEmoji
|
||||
} from '../../_actions/autosuggest'
|
||||
import { observe } from 'svelte-extras'
|
||||
|
||||
export default {
|
||||
|
@ -95,14 +97,21 @@
|
|||
stop('autosize.destroy()')
|
||||
},
|
||||
onBlur () {
|
||||
this.store.set({composeFocused: null})
|
||||
scheduleIdleTask(() => {
|
||||
this.store.setForCurrentAutosuggest({composeFocused: false})
|
||||
})
|
||||
},
|
||||
onFocus () {
|
||||
let { realm } = this.get()
|
||||
this.store.set({composeFocused: realm})
|
||||
scheduleIdleTask(() => {
|
||||
let {realm} = this.get()
|
||||
this.store.set({currentComposeRealm: realm})
|
||||
this.store.setForCurrentAutosuggest({composeFocused: true})
|
||||
})
|
||||
},
|
||||
onSelectionChange (selectionStart) {
|
||||
this.store.set({composeSelectionStart: selectionStart})
|
||||
scheduleIdleTask(() => {
|
||||
this.store.setForCurrentAutosuggest({composeSelectionStart: selectionStart})
|
||||
})
|
||||
},
|
||||
onKeydown (e) {
|
||||
let { keyCode } = e
|
||||
|
@ -132,14 +141,14 @@
|
|||
},
|
||||
clickSelectedAutosuggestion (event) {
|
||||
let {
|
||||
composeAutosuggestionShown,
|
||||
composeAutosuggestionType
|
||||
autosuggestShown,
|
||||
autosuggestType
|
||||
} = this.store.get()
|
||||
if (!composeAutosuggestionShown) {
|
||||
if (!autosuggestShown) {
|
||||
return false
|
||||
}
|
||||
let { realm } = this.get()
|
||||
if (composeAutosuggestionType === 'account') {
|
||||
if (autosuggestType === 'account') {
|
||||
/* no await */ clickSelectedAutosuggestionUsername(realm)
|
||||
} else { // emoji
|
||||
/* no await */ clickSelectedAutosuggestionEmoji(realm)
|
||||
|
@ -150,33 +159,31 @@
|
|||
},
|
||||
incrementAutosuggestSelected (increment, event) {
|
||||
let {
|
||||
composeAutosuggestionShown,
|
||||
composeAutosuggestionSelected,
|
||||
composeAutosuggestionSearchResults
|
||||
autosuggestShown,
|
||||
autosuggestSelected,
|
||||
autosuggestSearchResults
|
||||
} = this.store.get()
|
||||
if (!composeAutosuggestionShown) {
|
||||
if (!autosuggestShown) {
|
||||
return
|
||||
}
|
||||
let selected = composeAutosuggestionSelected || 0
|
||||
let searchResults = composeAutosuggestionSearchResults || []
|
||||
selected += increment
|
||||
if (selected >= 0) {
|
||||
selected = selected % searchResults.length
|
||||
autosuggestSelected += increment
|
||||
if (autosuggestSelected >= 0) {
|
||||
autosuggestSelected = autosuggestSelected % autosuggestSearchResults.length
|
||||
} else {
|
||||
selected = searchResults.length + selected
|
||||
autosuggestSelected = autosuggestSearchResults.length + autosuggestSelected
|
||||
}
|
||||
this.store.set({composeAutosuggestionSelected: selected})
|
||||
this.store.setForCurrentAutosuggest({autosuggestSelected})
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
},
|
||||
clearAutosuggestions (event) {
|
||||
let { composeAutosuggestionShown } = this.store.get()
|
||||
if (!composeAutosuggestionShown) {
|
||||
let { autosuggestShown } = this.store.get()
|
||||
if (!autosuggestShown) {
|
||||
return
|
||||
}
|
||||
this.store.set({
|
||||
composeAutosuggestionSearchResults: [],
|
||||
composeAutosuggestionSelected: 0
|
||||
this.store.setForCurrentAutosuggest({
|
||||
autosuggestSearchResults: [],
|
||||
autosuggestSelected: 0
|
||||
})
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
const MIN_PREFIX_LENGTH = 1
|
||||
const ACCOUNT_SEARCH_REGEX = new RegExp(`(?:\\s|^)(@\\S{${MIN_PREFIX_LENGTH},})$`)
|
||||
const EMOJI_SEARCH_REGEX = new RegExp(`(?:\\s|^)(:[^:]{${MIN_PREFIX_LENGTH},})$`)
|
||||
|
||||
function computeForAutosuggest (store, key, defaultValue) {
|
||||
store.compute(key,
|
||||
['currentInstance', 'currentComposeRealm', `autosuggestData_${key}`],
|
||||
(currentInstance, currentComposeRealm, root) => {
|
||||
let instanceData = root && root[currentInstance]
|
||||
return (currentComposeRealm && instanceData && currentComposeRealm in instanceData) ? instanceData[currentComposeRealm] : defaultValue
|
||||
})
|
||||
}
|
||||
|
||||
export function autosuggestComputations (store) {
|
||||
computeForAutosuggest(store, 'composeFocused', false)
|
||||
computeForAutosuggest(store, 'composeSelectionStart', 0)
|
||||
computeForAutosuggest(store, 'autosuggestSelected', 0)
|
||||
computeForAutosuggest(store, 'autosuggestSearchResults', [])
|
||||
computeForAutosuggest(store, 'autosuggestType', null)
|
||||
|
||||
store.compute(
|
||||
'currentComposeText',
|
||||
['currentComposeData', 'currentComposeRealm'],
|
||||
(currentComposeData, currentComposeRealm) => (
|
||||
currentComposeData[currentComposeRealm] && currentComposeData[currentComposeRealm].text) || ''
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'autosuggestSearchText',
|
||||
['currentComposeText', 'composeSelectionStart'],
|
||||
(currentComposeText, composeSelectionStart) => {
|
||||
let selectionStart = composeSelectionStart
|
||||
if (!currentComposeText || selectionStart < MIN_PREFIX_LENGTH) {
|
||||
return ''
|
||||
}
|
||||
|
||||
let textUpToCursor = currentComposeText.substring(0, selectionStart)
|
||||
let match = textUpToCursor.match(ACCOUNT_SEARCH_REGEX) || textUpToCursor.match(EMOJI_SEARCH_REGEX)
|
||||
return (match && match[1]) || ''
|
||||
}
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'autosuggestNumSearchResults',
|
||||
['autosuggestSearchResults'],
|
||||
(autosuggestSearchResults) => autosuggestSearchResults.length
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'autosuggestShown',
|
||||
['composeFocused', 'autosuggestSearchText', 'autosuggestNumSearchResults'],
|
||||
(composeFocused, autosuggestSearchText, autosuggestNumSearchResults) => (
|
||||
!!(composeFocused && autosuggestSearchText && autosuggestNumSearchResults)
|
||||
)
|
||||
)
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { instanceComputations } from './instanceComputations'
|
||||
import { timelineComputations } from './timelineComputations'
|
||||
import { navComputations } from './navComputations'
|
||||
import { autosuggestComputations } from './autosuggestComputations'
|
||||
|
||||
export function computations (store) {
|
||||
instanceComputations(store)
|
||||
timelineComputations(store)
|
||||
navComputations(store)
|
||||
autosuggestComputations(store)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
export function autosuggestMixins (Store) {
|
||||
Store.prototype.setForAutosuggest = function (instanceName, realm, obj) {
|
||||
let valuesToSet = {}
|
||||
for (let key of Object.keys(obj)) {
|
||||
let rootKey = `autosuggestData_${key}`
|
||||
let root = this.get()[rootKey] || {}
|
||||
let instanceData = root[instanceName] = root[instanceName] || {}
|
||||
instanceData[realm] = obj[key]
|
||||
valuesToSet[rootKey] = root
|
||||
}
|
||||
|
||||
this.set(valuesToSet)
|
||||
}
|
||||
|
||||
Store.prototype.setForCurrentAutosuggest = function (obj) {
|
||||
let { currentInstance, currentComposeRealm } = this.get()
|
||||
this.setForAutosuggest(currentInstance, currentComposeRealm, obj)
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { timelineMixins } from './timelineMixins'
|
||||
import { instanceMixins } from './instanceMixins'
|
||||
import { statusMixins } from './statusMixins'
|
||||
import { autosuggestMixins } from './autosuggestMixins'
|
||||
|
||||
export function mixins (Store) {
|
||||
instanceMixins(Store)
|
||||
timelineMixins(Store)
|
||||
statusMixins(Store)
|
||||
autosuggestMixins(Store)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
searchAccountsByUsername as searchAccountsByUsernameInDatabase
|
||||
} from '../../_database/accountsAndRelationships'
|
||||
|
||||
const SEARCH_RESULTS_LIMIT = 4
|
||||
const DATABASE_SEARCH_RESULTS_LIMIT = 30
|
||||
|
||||
async function searchAccounts (store, searchText) {
|
||||
searchText = searchText.substring(1)
|
||||
let { currentInstance } = store.get()
|
||||
let results = await searchAccountsByUsernameInDatabase(
|
||||
currentInstance, searchText, DATABASE_SEARCH_RESULTS_LIMIT)
|
||||
return results.slice(0, SEARCH_RESULTS_LIMIT)
|
||||
}
|
||||
|
||||
function searchEmoji (store, searchText) {
|
||||
searchText = searchText.toLowerCase().substring(1)
|
||||
let { currentCustomEmoji } = store.get()
|
||||
let results = currentCustomEmoji.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
|
||||
}
|
||||
|
||||
export function autosuggestObservers (store) {
|
||||
store.observe('autosuggestSearchText', async autosuggestSearchText => {
|
||||
let { composeFocused } = store.get()
|
||||
if (!composeFocused || !autosuggestSearchText) {
|
||||
return
|
||||
}
|
||||
let type = autosuggestSearchText.startsWith('@') ? 'account' : 'emoji'
|
||||
let results = (type === 'account')
|
||||
? await searchAccounts(store, autosuggestSearchText)
|
||||
: await searchEmoji(store, autosuggestSearchText)
|
||||
store.setForCurrentAutosuggest({
|
||||
autosuggestSelected: 0,
|
||||
autosuggestSearchText: autosuggestSearchText,
|
||||
autosuggestSearchResults: results,
|
||||
autosuggestType: type
|
||||
})
|
||||
})
|
||||
}
|
|
@ -3,6 +3,7 @@ import { timelineObservers } from './timelineObservers'
|
|||
import { notificationObservers } from './notificationObservers'
|
||||
import { onlineObservers } from './onlineObservers'
|
||||
import { navObservers } from './navObservers'
|
||||
import { autosuggestObservers } from './autosuggestObservers'
|
||||
|
||||
export function observers (store) {
|
||||
instanceObservers(store)
|
||||
|
@ -10,4 +11,5 @@ export function observers (store) {
|
|||
notificationObservers(store)
|
||||
onlineObservers(store)
|
||||
navObservers(store)
|
||||
autosuggestObservers(store)
|
||||
}
|
||||
|
|
|
@ -76,7 +76,9 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
plugins: [
|
||||
new LodashModuleReplacementPlugin()
|
||||
new LodashModuleReplacementPlugin({
|
||||
paths: true
|
||||
})
|
||||
].concat(isDev ? [
|
||||
new webpack.HotModuleReplacementPlugin({
|
||||
requestTimeout: 120000
|
||||
|
|
Loading…
Reference in New Issue