clean up tags autocomplete desktop
This commit is contained in:
@@ -22,6 +22,7 @@ import {resetToTab} from '../../Navigation'
|
||||
import {ImageSizesCache} from './cache/image-sizes'
|
||||
import {MutedThreads} from './muted-threads'
|
||||
import {reset as resetNavigation} from '../../Navigation'
|
||||
import {RecentTagsModel} from './ui/tags-autocomplete'
|
||||
|
||||
// TEMPORARY (APP-700)
|
||||
// remove after backend testing finishes
|
||||
@@ -53,6 +54,7 @@ export class RootStoreModel {
|
||||
linkMetas = new LinkMetasCache(this)
|
||||
imageSizes = new ImageSizesCache()
|
||||
mutedThreads = new MutedThreads()
|
||||
recentTags = new RecentTagsModel()
|
||||
|
||||
constructor(agent: BskyAgent) {
|
||||
this.agent = agent
|
||||
@@ -77,6 +79,7 @@ export class RootStoreModel {
|
||||
preferences: this.preferences.serialize(),
|
||||
invitedUsers: this.invitedUsers.serialize(),
|
||||
mutedThreads: this.mutedThreads.serialize(),
|
||||
recentTags: this.recentTags.serialize(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +112,9 @@ export class RootStoreModel {
|
||||
if (hasProp(v, 'mutedThreads')) {
|
||||
this.mutedThreads.hydrate(v.mutedThreads)
|
||||
}
|
||||
if (hasProp(v, 'recentTags')) {
|
||||
this.recentTags.hydrate(v.recentTags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,25 +2,44 @@ import {makeAutoObservable, runInAction} from 'mobx'
|
||||
import AwaitLock from 'await-lock'
|
||||
import {RootStoreModel} from '../root-store'
|
||||
import Fuse from 'fuse.js'
|
||||
import {isObj, hasProp, isStrArray} from 'lib/type-guards'
|
||||
|
||||
export class TagsAutocompleteView {
|
||||
// state
|
||||
isLoading = false
|
||||
isActive = false
|
||||
prefix = ''
|
||||
export class RecentTagsModel {
|
||||
_tags: string[] = []
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this, {}, {autoBind: true})
|
||||
}
|
||||
|
||||
get tags() {
|
||||
return this._tags
|
||||
}
|
||||
|
||||
add(tag: string) {
|
||||
this._tags = Array.from(new Set([tag, ...this._tags]))
|
||||
}
|
||||
|
||||
remove(tag: string) {
|
||||
this._tags = this._tags.filter(t => t !== tag)
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return {_tags: this._tags}
|
||||
}
|
||||
|
||||
hydrate(v: unknown) {
|
||||
if (isObj(v) && hasProp(v, '_tags') && isStrArray(v._tags)) {
|
||||
this._tags = Array.from(new Set(v._tags))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TagsAutocompleteModel {
|
||||
lock = new AwaitLock()
|
||||
|
||||
isActive = false
|
||||
query = ''
|
||||
searchedTags: string[] = []
|
||||
recentTags: string[] = [
|
||||
'js',
|
||||
'javascript',
|
||||
'art',
|
||||
'music',
|
||||
]
|
||||
profileTags: string[] = [
|
||||
'bikes',
|
||||
'beer',
|
||||
]
|
||||
profileTags: string[] = []
|
||||
|
||||
constructor(public rootStore: RootStoreModel) {
|
||||
makeAutoObservable(
|
||||
@@ -32,56 +51,64 @@ export class TagsAutocompleteView {
|
||||
)
|
||||
}
|
||||
|
||||
setActive(isActive: boolean) {
|
||||
this.isActive = isActive
|
||||
}
|
||||
|
||||
commitRecentTag(tag: string) {
|
||||
this.rootStore.recentTags.add(tag)
|
||||
}
|
||||
|
||||
get suggestions() {
|
||||
if (!this.isActive) {
|
||||
return []
|
||||
}
|
||||
|
||||
const items = [
|
||||
...this.recentTags,
|
||||
...this.profileTags,
|
||||
...this.searchedTags,
|
||||
]
|
||||
const items = Array.from(
|
||||
new Set([
|
||||
...this.rootStore.recentTags.tags.slice(0, 3),
|
||||
...this.profileTags.slice(0, 3),
|
||||
...this.searchedTags,
|
||||
]),
|
||||
)
|
||||
|
||||
if (!this.prefix) {
|
||||
return items.slice(0, 8)
|
||||
if (!this.query) {
|
||||
return items.slice(0, 9)
|
||||
}
|
||||
|
||||
const fuse = new Fuse(items)
|
||||
const results = fuse.search(this.prefix)
|
||||
const results = fuse.search(this.query)
|
||||
|
||||
return results.slice(0, 8).map(r => r.item)
|
||||
return results.slice(0, 9).map(r => r.item)
|
||||
}
|
||||
|
||||
setActive(v: boolean) {
|
||||
this.isActive = v
|
||||
}
|
||||
async search(query: string) {
|
||||
this.query = query.trim()
|
||||
|
||||
async setPrefix(prefix: string) {
|
||||
this.prefix = prefix.trim()
|
||||
await this.lock.acquireAsync()
|
||||
|
||||
try {
|
||||
if (this.prefix) {
|
||||
if (this.prefix !== this.prefix) {
|
||||
return // another prefix was set before we got our chance
|
||||
}
|
||||
await this._search()
|
||||
} else {
|
||||
// this.searchRes = []
|
||||
}
|
||||
// another query was set before we got our chance
|
||||
if (this.query !== this.query) return
|
||||
await this._search()
|
||||
} finally {
|
||||
this.lock.release()
|
||||
}
|
||||
}
|
||||
|
||||
// internal
|
||||
// =
|
||||
|
||||
async _search() {
|
||||
runInAction(() => {
|
||||
this.searchedTags = [
|
||||
'code',
|
||||
'dev',
|
||||
'javascript',
|
||||
'react',
|
||||
'typescript',
|
||||
'mobx',
|
||||
'mobx-state-tree',
|
||||
'mobx-react',
|
||||
'mobx-react-lite',
|
||||
'mobx-react-form',
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user