2019-05-06 02:16:02 +00:00
|
|
|
import { database } from '../_database/database'
|
|
|
|
import { store } from '../_store/store'
|
|
|
|
import { search } from '../_api/search'
|
|
|
|
import { SEARCH_RESULTS_LIMIT } from '../_static/autosuggest'
|
|
|
|
import { concat } from '../_utils/arrays'
|
|
|
|
import uniqBy from 'lodash-es/uniqBy'
|
|
|
|
import { scheduleIdleTask } from '../_utils/scheduleIdleTask'
|
2019-05-07 03:29:43 +00:00
|
|
|
import { PromiseThrottler } from '../_utils/PromiseThrottler'
|
2019-05-06 02:16:02 +00:00
|
|
|
|
|
|
|
const DATABASE_SEARCH_RESULTS_LIMIT = 30
|
2019-05-07 03:29:43 +00:00
|
|
|
const promiseThrottler = new PromiseThrottler(200) // Mastodon FE also uses 200ms
|
2019-05-06 02:16:02 +00:00
|
|
|
|
2019-05-06 15:34:12 +00:00
|
|
|
function byUsername (a, b) {
|
2019-08-03 20:49:37 +00:00
|
|
|
const usernameA = a.acct.toLowerCase()
|
|
|
|
const usernameB = b.acct.toLowerCase()
|
2019-05-06 15:34:12 +00:00
|
|
|
|
|
|
|
return usernameA < usernameB ? -1 : usernameA === usernameB ? 0 : 1
|
2019-05-06 02:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function byAccountId (a) {
|
|
|
|
return a.id
|
|
|
|
}
|
|
|
|
|
|
|
|
export function doAccountSearch (searchText) {
|
|
|
|
let canceled = false
|
|
|
|
let localResults
|
|
|
|
let remoteResults
|
2019-08-03 20:49:37 +00:00
|
|
|
const { currentInstance, accessToken } = store.get()
|
2019-05-07 03:29:43 +00:00
|
|
|
let controller = typeof AbortController === 'function' && new AbortController()
|
|
|
|
|
|
|
|
function abortFetch () {
|
|
|
|
if (controller) {
|
|
|
|
controller.abort()
|
|
|
|
controller = null
|
|
|
|
}
|
|
|
|
}
|
2019-05-06 02:16:02 +00:00
|
|
|
|
|
|
|
async function searchAccountsLocally (searchText) {
|
|
|
|
localResults = await database.searchAccountsByUsername(
|
|
|
|
currentInstance, searchText.substring(1), DATABASE_SEARCH_RESULTS_LIMIT)
|
|
|
|
}
|
|
|
|
|
|
|
|
async function searchAccountsRemotely (searchText) {
|
2019-05-07 03:29:43 +00:00
|
|
|
// Throttle our XHRs to be a good citizen and not spam the server with one XHR per keystroke
|
|
|
|
await promiseThrottler.next()
|
|
|
|
if (canceled) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
remoteResults = (await search(
|
|
|
|
currentInstance, accessToken, searchText, false, SEARCH_RESULTS_LIMIT, controller && controller.signal
|
|
|
|
)).accounts
|
2019-05-06 02:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function mergeAndTruncateResults () {
|
2019-05-06 15:34:12 +00:00
|
|
|
// Always include local results; they are more likely to be relevant
|
|
|
|
// because the user has seen their content before. Otherwise, sort by username.
|
|
|
|
let results = (localResults || [])
|
|
|
|
.slice()
|
|
|
|
.sort(byUsername)
|
2019-05-06 02:16:02 +00:00
|
|
|
.slice(0, SEARCH_RESULTS_LIMIT)
|
2019-05-06 15:34:12 +00:00
|
|
|
|
|
|
|
if (results.length < SEARCH_RESULTS_LIMIT) {
|
2019-08-03 20:49:37 +00:00
|
|
|
const topRemoteResults = (remoteResults || [])
|
2019-05-06 15:34:12 +00:00
|
|
|
.sort(byUsername)
|
|
|
|
.slice(0, SEARCH_RESULTS_LIMIT - results.length)
|
|
|
|
results = concat(results, topRemoteResults)
|
|
|
|
results = uniqBy(results, byAccountId)
|
|
|
|
}
|
|
|
|
|
|
|
|
return results
|
2019-05-06 02:16:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function onNewResults () {
|
|
|
|
if (canceled) {
|
|
|
|
return
|
|
|
|
}
|
2019-08-03 20:49:37 +00:00
|
|
|
const results = mergeAndTruncateResults()
|
2019-05-06 02:16:02 +00:00
|
|
|
store.setForCurrentAutosuggest({
|
|
|
|
autosuggestType: 'account',
|
|
|
|
autosuggestSelected: 0,
|
|
|
|
autosuggestSearchResults: results
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduleIdleTask(() => {
|
|
|
|
if (canceled) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// run the two searches in parallel
|
|
|
|
searchAccountsLocally(searchText).then(onNewResults).catch(err => {
|
|
|
|
console.error('could not search locally', err)
|
|
|
|
})
|
|
|
|
searchAccountsRemotely(searchText).then(onNewResults).catch(err => {
|
|
|
|
console.error('could not search remotely', err)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
return {
|
|
|
|
cancel: () => {
|
|
|
|
canceled = true
|
2019-05-07 03:29:43 +00:00
|
|
|
abortFetch()
|
2019-05-06 02:16:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|