deemix-webui/src/components/pages/Search.vue

318 lines
7.9 KiB
Vue
Raw Normal View History

2020-06-29 16:49:33 +00:00
<template>
<div id="search_tab">
2020-11-28 13:18:36 +00:00
<div v-show="isQueryEmpty && !isSearching">
<h2>{{ $t('search.startSearching') }}</h2>
<p>{{ $t('search.description') }}</p>
2020-06-29 16:49:33 +00:00
</div>
2020-11-28 13:18:36 +00:00
<BaseLoadingPlaceholder text="Searching..." :hidden="!isSearching" />
<div v-show="!isQueryEmpty && !isSearching">
<BaseTabs>
<BaseTab
v-for="tab in tabs"
:key="tab.name"
@click="changeSearchTab(tab.searchType)"
:class="{ active: currentTab.name === tab.name }"
>
{{ tab.name }}
</BaseTab>
</BaseTabs>
<keep-alive>
<component
:is="currentTab.component"
:viewInfo="getViewInfo()"
want-headers
@add-to-queue="addToQueue"
@change-search-tab="changeSearchTab"
></component>
</keep-alive>
2020-06-29 16:49:33 +00:00
</div>
</div>
</template>
<script>
2020-11-28 13:50:38 +00:00
import { computed, onMounted, reactive, ref, toRefs, watch, defineComponent } from '@vue/composition-api'
import { uniqWith } from 'lodash-es'
2020-11-28 13:18:36 +00:00
import BaseLoadingPlaceholder from '@components/globals/BaseLoadingPlaceholder.vue'
import ResultsAll from '@components/search/ResultsAll.vue'
import ResultsAlbums from '@components/search/ResultsAlbums.vue'
import ResultsArtists from '@components/search/ResultsArtists.vue'
import ResultsPlaylists from '@components/search/ResultsPlaylists.vue'
import ResultsTracks from '@components/search/ResultsTracks.vue'
import { BaseTabs, BaseTab } from '@components/globals/BaseTabs'
import { socket } from '@/utils/socket'
import { sendAddToQueue } from '@/utils/downloads'
import { numberWithDots, convertDuration } from '@/utils/utils'
2020-06-29 16:49:33 +00:00
import { formatSingleTrack, formatAlbums, formatArtist, formatPlaylist } from '@/data/search'
import { standardizeData } from '@/data/standardize'
import { useMainSearch } from '@/use/main-search'
import { useSearch } from '@/use/search'
const resetObj = { data: [], next: 0, total: 0, hasLoaded: false }
const lastTab = ref(null)
export default defineComponent({
2020-06-29 16:49:33 +00:00
components: {
BaseLoadingPlaceholder,
BaseTabs,
BaseTab
2020-06-29 16:49:33 +00:00
},
props: {
performScrolledSearch: {
type: Boolean,
required: false
}
},
setup(props, ctx) {
const state = reactive({
currentTab: {
name: '',
searchType: '',
component: {},
viewInfo: '',
formatFunc: () => {}
},
results: {
query: '',
allTab: {
ORDER: [],
TOP_RESULT: [],
ALBUM: {
hasLoaded: false
},
ARTIST: {
hasLoaded: false
},
TRACK: {
hasLoaded: false
},
PLAYLIST: {
hasLoaded: false
}
},
trackTab: { ...resetObj },
albumTab: { ...resetObj },
artistTab: { ...resetObj },
playlistTab: { ...resetObj }
},
tabs: [
{
name: ctx.root.$i18n.t('globals.listTabs.all'),
searchType: 'all',
component: ResultsAll,
viewInfo: 'allTab'
},
{
name: ctx.root.$i18n.tc('globals.listTabs.track', 2),
searchType: 'track',
component: ResultsTracks,
viewInfo: 'trackTab',
formatFunc: formatSingleTrack
},
{
name: ctx.root.$i18n.tc('globals.listTabs.album', 2),
searchType: 'album',
component: ResultsAlbums,
viewInfo: 'albumTab',
formatFunc: formatAlbums
},
{
name: ctx.root.$i18n.tc('globals.listTabs.artist', 2),
searchType: 'artist',
component: ResultsArtists,
viewInfo: 'artistTab',
formatFunc: formatArtist
},
{
name: ctx.root.$i18n.tc('globals.listTabs.playlist', 2),
searchType: 'playlist',
component: ResultsPlaylists,
viewInfo: 'playlistTab',
formatFunc: formatPlaylist
}
]
})
const { searchResult, performMainSearch } = useMainSearch()
const { result, performSearch } = useSearch()
const searchedTerm = computed(() => ctx.root.$route.query.term)
const isQueryEmpty = computed(() => state.results.query === '')
2020-11-28 13:18:36 +00:00
const isSearching = ref(false)
2020-11-28 13:50:38 +00:00
const isMainSearchCached = computed(() => Object.keys(searchResult.value).length !== 0)
const isNewSearch = computed(() => searchResult.value.QUERY !== searchedTerm.value)
2020-11-28 13:50:38 +00:00
if (isMainSearchCached.value && !isNewSearch.value) {
2020-11-28 13:50:38 +00:00
onMounted(() => {
handleMainSearch(searchResult.value)
})
}
if (searchedTerm.value && (!isMainSearchCached.value || isNewSearch.value)) {
performMainSearch(searchedTerm.value)
2020-11-28 13:18:36 +00:00
isSearching.value = true
}
2020-11-28 13:50:38 +00:00
function handleMainSearch(newValue) {
// Hide loading placeholder
2020-11-28 13:18:36 +00:00
isSearching.value = false
state.results.query = newValue.QUERY
state.results.allTab = newValue
state.results.allTab.TRACK.hasLoaded = true
state.results.allTab.ALBUM.hasLoaded = true
state.results.allTab.ARTIST.hasLoaded = true
state.results.allTab.PLAYLIST.hasLoaded = true
if (lastTab.value && lastTab.value.searchType !== 'all') {
state.currentTab = lastTab.value
performSearch({
term: newValue.QUERY,
type: state.currentTab.searchType
})
} else {
state.currentTab = state.tabs.find(tab => tab.searchType === 'all')
}
2020-11-28 13:50:38 +00:00
}
// Main search watcher
watch(searchResult, handleMainSearch)
// Search watcher
watch(result, newValue => {
const { next: nextResult, total, type, data: newData } = newValue
const currentTabKey = `${type}Tab`
let next = total
// console.log({ currentTabKey, test: state.currentTab.searchType })
if (nextResult) {
next = parseInt(nextResult.match(/index=(\d*)/)[1])
}
// console.log({ next, total, type, newData })
if (state.results[currentTabKey].total !== total) {
state.results[currentTabKey].total = total
}
if (state.results[currentTabKey].next !== next) {
state.results[currentTabKey].next = next
// Preventing duplicate entries by filtering them by ID
const rawData = state.results[currentTabKey].data.concat(newData)
const filteredData = uniqWith(rawData, (obj1, obj2) => {
return obj1.id === obj2.id
})
state.results[currentTabKey].data = filteredData
}
state.results[currentTabKey].hasLoaded = true
})
state.currentTab = state.tabs.find(tab => tab.searchType === 'all')
return {
...toRefs(state),
2020-11-28 13:18:36 +00:00
isSearching,
isQueryEmpty,
searchResult,
performMainSearch,
performSearch
2020-06-29 16:49:33 +00:00
}
},
computed: {
loadedTabs() {
const tabsLoaded = []
for (const resultKey in this.results) {
if (this.results.hasOwnProperty(resultKey) && resultKey !== 'query') {
const currentResult = this.results[resultKey]
if (currentResult.hasLoaded) {
tabsLoaded.push(resultKey.replace(/Tab/g, ''))
}
}
}
return tabsLoaded
}
},
2020-06-29 16:49:33 +00:00
methods: {
numberWithDots,
convertDuration,
addToQueue(e) {
sendAddToQueue(e.currentTarget.dataset.link)
},
getViewInfo() {
if (this.currentTab.searchType === 'all') {
return this.results.allTab
}
return standardizeData(this.results[this.currentTab.viewInfo], this.currentTab.formatFunc)
},
changeSearchTab(tabName) {
tabName = tabName.toLowerCase()
const newTab = this.tabs.find(tab => {
return tab.searchType === tabName
})
if (!newTab) {
console.error(`No tab ${tabName} found`)
return
}
window.scrollTo(0, 0)
this.currentTab = newTab
lastTab.value = newTab
2020-06-29 16:49:33 +00:00
},
2020-09-17 16:31:07 +00:00
scrolledSearch() {
if (this.currentTab.searchType === 'all') return
const currentTabKey = `${this.currentTab.searchType}Tab`
const needToPerformScrolledSearch = this.results[currentTabKey].next < this.results[currentTabKey].total
2020-06-29 16:49:33 +00:00
if (needToPerformScrolledSearch) {
this.performSearch({
term: this.results.query,
type: this.currentTab.searchType,
start: this.results[`${this.currentTab.searchType}Tab`].next
})
2020-06-29 16:49:33 +00:00
}
},
isTabLoaded(tab) {
return this.loadedTabs.indexOf(tab.searchType) !== -1 || tab.searchType === 'all'
2020-06-29 16:49:33 +00:00
}
},
watch: {
2020-09-17 16:31:07 +00:00
performScrolledSearch(needToSearch) {
if (!needToSearch) return
2020-06-29 16:49:33 +00:00
2020-09-17 16:31:07 +00:00
this.scrolledSearch(needToSearch)
},
2020-11-28 13:50:38 +00:00
currentTab(newTab, old) {
if (this.isTabLoaded(newTab)) return
this.performSearch({
term: this.results.query,
type: newTab.searchType,
start: this.results[`${newTab.searchType}Tab`].next
})
}
2020-06-29 16:49:33 +00:00
}
})
2020-06-29 16:49:33 +00:00
</script>
<style>
</style>
2020-11-28 13:50:38 +00:00