xcv58/Tab-Manager-v2

View on GitHub
packages/extension/src/js/stores/SearchStore.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import { MutableRefObject } from 'react'
import { makeAutoObservable } from 'mobx'
import { browser } from 'libs'
import Store from 'stores'
import log from 'libs/log'
import { matchSorter } from 'match-sorter'
import debounce from 'lodash.debounce'
import Tab from './Tab'
 
const hasCommandPrefix = (value: string) => value.startsWith('>')
 
const DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24
 
export type HistoryItem = {
id: string
lastVisitTime?: number
title?: string
typedCount?: number
url?: string
visitCount?: number
}
 
export default class SearchStore {
store: Store
 
constructor(store: Store) {
makeAutoObservable(this)
 
this.store = store
}
 
init = async () => {
if (this.store.userStore.preserveSearch) {
const { query } = await browser.storage.local.get({ query: this.query })
this.search(query)
}
}
 
searchEl: MutableRefObject<HTMLInputElement> = null
 
query = ''
 
_query = ''
 
// The _tabQuery is used only on tab content highlight
_tabQuery = ''
 
historyTabs: HistoryItem[] = []
 
typing = false
 
get isCommand() {
return hasCommandPrefix(this.query)
}
 
get matchedTabs(): Tab[] {
return this.fuzzySearch()
}
 
get matchedSet() {
return new Set(this.matchedTabs.map((x) => x.id))
}
 
get allTabSelected() {
return (
this.matchedTabs.every(this.store.tabStore.isTabSelected) &&
this.matchedTabs.length > 0
)
}
 
Similar blocks of code found in 3 locations. Consider refactoring.
get someTabSelected() {
return (
!this.allTabSelected &&
this.matchedTabs.some(this.store.tabStore.isTabSelected)
)
}
 
setSearchEl = (searchEl: MutableRefObject<HTMLInputElement>) => {
this.searchEl = searchEl
}
 
focus = () => {
this.searchEl.current.click()
}
 
startCommandSearch = async () => {
const { lastCommand } = await browser.storage.local.get({ lastCommand: '' })
this.search(`>${lastCommand}`)
this.focus()
const inputEl = this.searchEl.current.querySelector('input')
if (inputEl) {
inputEl.setSelectionRange(1, 1 + lastCommand.length)
}
}
 
blur = () => {
const inputEl = this.searchEl.current.querySelector('input')
if (inputEl) {
inputEl.blur()
}
}
 
startType = () => {
this.typing = true
}
 
stopType = () => {
this.typing = false
if (this.isCommand) {
this.query = this._query
}
}
 
search = (query: string) => {
log.debug('SearchStore.search:', { query, 'this.query': this.query })
if (this.query === query) {
return
}
this.query = query
if (!this.isCommand) {
this.updateQuery()
this.updateTabQuery()
if (this.store.userStore.preserveSearch) {
browser.storage.local.set({ query })
}
} else {
browser.storage.local.set({ lastCommand: query.slice(1) })
}
}
 
_updateQuery = async () => {
log.debug('_updateQuery:', { _query: this._query, query: this.query })
this._query = this.query
if (!this.matchedSet.has(this.store.focusStore.focusedTabId)) {
this.store.focusStore.defocus()
}
if (this.store.userStore.searchHistory) {
const historyTabs = await browser.history.search({
text: this._query,
startTime: Date.now() - DAY_IN_MILLISECONDS * 7,
})
this.historyTabs = historyTabs
}
}
 
_updateTabQuery = () => {
log.debug('_updateTabQuery:', {
_tabQuery: this._tabQuery,
query: this.query,
})
this._tabQuery = this.query
}
 
updateQuery = debounce(this._updateQuery, 200)
 
updateTabQuery = debounce(this._updateTabQuery, 500)
 
clear = () => this.search('')
 
fuzzySearch = () => {
log.debug('SearchStore.fuzzySearch:', { _query: this._query })
const { tabs } = this.store.windowStore
if (!this._query) {
return tabs
}
const keys = ['title']
if (this.store.userStore.showUrl) {
keys.push('url')
}
return matchSorter(tabs, this._query, { keys })
}
 
selectAll = () => {
this.store.tabStore.selectAll(this.matchedTabs)
}
 
invertSelect = () => {
this.store.tabStore.invertSelect(this.matchedTabs)
}
 
unselectAll = () => {
this.store.tabStore.unselectAll()
}
 
toggleSelectAll = () => {
if (this.allTabSelected) {
this.unselectAll()
} else {
this.selectAll()
}
}
}