Filter by result type
This commit is contained in:
parent
d1a30ab252
commit
9bdd38c606
|
@ -20,6 +20,29 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group small-height">
|
||||
<div class="radio-label label-container">
|
||||
<label>{{ $gettext('Filter by result type') }}</label>
|
||||
|
||||
<button v-if="formResultType !== undefined" class="reset-button" @click="resetField('resultType')">{{ $gettext('Reset') }}</button>
|
||||
</div>
|
||||
|
||||
<div class="peertube-radio-container">
|
||||
<input id="resultTypeVideos" v-model="formResultType" type="radio" name="resultType" value="videos">
|
||||
<label for="resultTypeVideos" class="radio">{{ $gettext('Only videos') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="peertube-radio-container">
|
||||
<input id="resultTypeChannels" v-model="formResultType" type="radio" name="resultType" value="channels">
|
||||
<label for="resultTypeChannels" class="radio">{{ $gettext('Only channels') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="peertube-radio-container">
|
||||
<input id="resultTypePlaylists" v-model="formResultType" type="radio" name="resultType" value="playlists">
|
||||
<label for="resultTypePlaylists" class="radio">{{ $gettext('Only playlists') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group small-height">
|
||||
<div class="radio-label label-container">
|
||||
<label>{{ $gettext('Display only') }}</label>
|
||||
|
@ -124,12 +147,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="host">{{ $gettext('PeerTube instance') }}</label>
|
||||
|
||||
<input id="host" v-model="formHost" type="text" name="host" class="classic-input-text" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tagsAllOf">{{ $gettext('All of these tags') }}</label>
|
||||
<button v-if="formTagsAllOf.length !== 0" class="reset-button" @click="resetField('tagsAllOf')">
|
||||
|
@ -154,6 +171,12 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="host">{{ $gettext('PeerTube instance') }}</label>
|
||||
|
||||
<input id="host" v-model="formHost" type="text" name="host" class="classic-input-text" />
|
||||
</div>
|
||||
|
||||
<div class="button-block mt-3">
|
||||
<router-link class="peertube-button peertube-button-link peertube-primary-button" :to="{ path: '/search', query: { ...getUrlQuery() } }">
|
||||
{{ applyFiltersLabel }}
|
||||
|
@ -190,6 +213,8 @@
|
|||
|
||||
formIsLive: undefined,
|
||||
|
||||
formResultType: undefined,
|
||||
|
||||
activeFilters: 0
|
||||
}
|
||||
},
|
||||
|
@ -326,6 +351,7 @@
|
|||
nsfw: this.formNSFW,
|
||||
host: this.formHost || undefined,
|
||||
isLive: this.formIsLive,
|
||||
resultType: this.formResultType,
|
||||
publishedDateRange: this.formPublishedDateRange,
|
||||
durationRange: this.formDurationRange,
|
||||
categoryOneOf: this.formCategoryOneOf,
|
||||
|
@ -366,6 +392,9 @@
|
|||
if (query.isLive) this.formIsLive = query.isLive
|
||||
else this.formIsLive = undefined
|
||||
|
||||
if (query.resultType) this.formResultType = query.resultType
|
||||
else this.formResultType = undefined
|
||||
|
||||
if (query.host) this.formHost = query.host
|
||||
else this.formHost = ''
|
||||
},
|
||||
|
@ -378,6 +407,7 @@
|
|||
else if (field === 'licenceOneOf') this.formLicenceOneOf = undefined
|
||||
else if (field === 'languageOneOf') this.formLanguageOneOf = undefined
|
||||
else if (field === 'isLive') this.formIsLive = undefined
|
||||
else if (field === 'resultType') this.formResultType = undefined
|
||||
else if (field === 'tagsAllOf') this.formTagsAllOf = []
|
||||
else if (field === 'tagsOneOf') this.formTagsOneOf = []
|
||||
else if (field === 'host') this.formHost = ''
|
||||
|
|
|
@ -11,6 +11,8 @@ export interface SearchUrl {
|
|||
tagsAllOf?: string[]
|
||||
tagsOneOf?: string[]
|
||||
|
||||
resultType?: 'videos' | 'channels' | 'playlists'
|
||||
|
||||
isLive?: string
|
||||
|
||||
sort?: string
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
[type=radio]:checked + label,
|
||||
[type=radio]:not(:checked) + label {
|
||||
position: relative;
|
||||
padding-left: 28px;
|
||||
padding-left: 20px;
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
display: inline-block;
|
||||
|
@ -29,7 +29,7 @@
|
|||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
top: 2px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid #C6C6C6;
|
||||
|
@ -44,7 +44,7 @@
|
|||
height: 8px;
|
||||
background: $orange-main;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
top: 5px;
|
||||
left: 3px;
|
||||
border-radius: 100%;
|
||||
transition: all 0.2s ease;
|
||||
|
|
|
@ -35,27 +35,87 @@
|
|||
<filters v-if="displayFilters" id="filters"></filters>
|
||||
</form>
|
||||
|
||||
<div class="results">
|
||||
<div v-if="searched" class="results">
|
||||
<div class="results-summary">
|
||||
<span v-if="resultsCount === 0">
|
||||
{{ $gettext('No results found.') }}
|
||||
</span>
|
||||
<span v-if="totalResults === 0">{{ $gettext('No results found.') }}</span>
|
||||
|
||||
<span v-if="resultsCount">
|
||||
{{ $ngettext('%{resultsCount} result found:', '%{resultsCount} results found:', resultsCount, { resultsCount: resultsCount.toLocaleString() }) }}
|
||||
</span>
|
||||
<span v-if="totalResults">{{ $ngettext('%{totalResults} result found:', '%{totalResults} results found:', totalResults, { totalResults: totalResults.toLocaleString() }) }}</span>
|
||||
</div>
|
||||
|
||||
<div v-for="result in results" :key="getResultKey(result)">
|
||||
<video-result v-if="isVideo(result)" :video="result" class="mb-4" />
|
||||
<div v-if="(!hasResultTypeFilter() || getResultTypeFilter() === 'videos') && totalResults !== 0">
|
||||
<h2>
|
||||
<strong>
|
||||
{{ $ngettext('%{totalVideos} video', '%{totalVideos} videos', totalVideos, { totalVideos: totalVideos + '' }) }}
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<channel-result v-else-if="isChannel(result)" :channel="result" class="mb-4" />
|
||||
<template v-if="totalVideos">
|
||||
<div v-for="video in videos" :key="video.url" class="mb-4">
|
||||
<video-result :video="video"></video-result>
|
||||
</div>
|
||||
|
||||
<playlist-result v-else :playlist="result" class="mb-4" />
|
||||
<div class="mb-5 text-center">
|
||||
<router-link
|
||||
v-if="totalVideos > summaryResultsCount.videos"
|
||||
class="peertube-button peertube-button-link peertube-secondary-button"
|
||||
:to="{ path: '/search', query: { ...getMoreResultsQuery('videos') } }"
|
||||
>
|
||||
{{ $gettext('Display more videos') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<pagination v-model="currentPage" :max-page="getMaxPage()" :searched="searched" :pages="pages" />
|
||||
<div v-if="(!hasResultTypeFilter() || getResultTypeFilter() === 'channels') && totalResults !== 0">
|
||||
<h2>
|
||||
<strong>
|
||||
{{ $ngettext('%{totalChannels} channel', '%{totalChannels} channels', totalChannels, { totalChannels: totalChannels + '' }) }}
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<template v-if="totalChannels">
|
||||
<div v-for="channel in channels" :key="channel.url" class="mb-4">
|
||||
<channel-result :channel="channel"></channel-result>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 text-center">
|
||||
<router-link
|
||||
v-if="totalChannels > summaryResultsCount.channels"
|
||||
class="peertube-button peertube-button-link peertube-secondary-button"
|
||||
:to="{ path: '/search', query: { ...getMoreResultsQuery('channels') } }"
|
||||
>
|
||||
{{ $gettext('Display more channels') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="(!hasResultTypeFilter() || getResultTypeFilter() === 'playlists') && totalResults !== 0">
|
||||
<h2>
|
||||
<strong>
|
||||
{{ $ngettext('%{totalPlaylists} playlist', '%{totalPlaylists} playlists', totalPlaylists, { totalPlaylists: totalPlaylists + '' }) }}
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<template v-if="totalPlaylists">
|
||||
<div v-for="playlist in playlists" :key="playlist.url" class="mb-4">
|
||||
<playlist-result :playlist="playlist"></playlist-result>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 text-center">
|
||||
<router-link
|
||||
v-if="totalPlaylists > summaryResultsCount.playlists"
|
||||
class="peertube-button peertube-button-link peertube-secondary-button"
|
||||
:to="{ path: '/search', query: { ...getMoreResultsQuery('playlists') } }"
|
||||
>
|
||||
{{ $gettext('Display more playlists') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pagination v-if="hasResultTypeFilter()" v-model="currentPage" :max-page="getMaxPage()" :searched="searched" :pages="pages" />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -102,22 +162,30 @@
|
|||
searched: false,
|
||||
|
||||
indexName: '',
|
||||
results: [] as (EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist)[],
|
||||
|
||||
resultsCount: null as number,
|
||||
channelsCount: null as number,
|
||||
videosCount: null as number,
|
||||
playlistsCount: null as number,
|
||||
totalResults: null as number,
|
||||
|
||||
totalVideos: null as number,
|
||||
videos: null as EnhancedVideo[],
|
||||
|
||||
totalChannels: null as number,
|
||||
channels: null as EnhancedVideoChannel[],
|
||||
|
||||
totalPlaylists: null as number,
|
||||
playlists: null as EnhancedPlaylist[],
|
||||
|
||||
currentPage: 1,
|
||||
pages: [],
|
||||
resultsPerVideosPage: 10,
|
||||
resultsPerChannelsPage: 3,
|
||||
resultsPerPlaylistsPage: 3,
|
||||
|
||||
summaryResultsCount: {
|
||||
videos: 5,
|
||||
channels: 2,
|
||||
playlists: 2
|
||||
},
|
||||
filteredTypeResultsPerPage: 10,
|
||||
|
||||
activeFilters: 0,
|
||||
displayFilters: false,
|
||||
oldQuery: ''
|
||||
displayFilters: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -163,7 +231,9 @@
|
|||
methods: {
|
||||
|
||||
async doSearch () {
|
||||
this.results = []
|
||||
this.videos = []
|
||||
this.channels = []
|
||||
this.playlists = []
|
||||
this.searched = false
|
||||
|
||||
Nprogress.start()
|
||||
|
@ -179,15 +249,17 @@
|
|||
|
||||
this.activeFilters = this.countActiveFilters()
|
||||
|
||||
this.channelsCount = channelsResult.total
|
||||
this.videosCount = videosResult.total
|
||||
this.playlistsCount = playlistsResult.total
|
||||
|
||||
this.resultsCount = videosResult.total + channelsResult.total + playlistsResult.total
|
||||
this.totalVideos = videosResult.total
|
||||
this.videos = videosResult.data
|
||||
|
||||
this.results = channelsResult.data
|
||||
this.results = this.results.concat(playlistsResult.data)
|
||||
this.results = this.results.concat(videosResult.data)
|
||||
this.totalChannels = channelsResult.total
|
||||
this.channels = channelsResult.data
|
||||
|
||||
this.totalPlaylists = playlistsResult.total
|
||||
this.playlists = playlistsResult.data
|
||||
|
||||
this.totalResults = videosResult.total + channelsResult.total + playlistsResult.total
|
||||
|
||||
this.buildPages()
|
||||
this.searched = true
|
||||
|
@ -199,32 +271,6 @@
|
|||
}
|
||||
},
|
||||
|
||||
isVideo (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist): result is EnhancedVideo {
|
||||
if ((result as EnhancedVideo).language) return true
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
isPlaylist (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist): result is EnhancedPlaylist {
|
||||
if ((result as EnhancedPlaylist).videosLength !== undefined) return true
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
isChannel (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist): result is EnhancedVideoChannel {
|
||||
if ((result as EnhancedVideoChannel).followingCount !== undefined) return true
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
getResultKey (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist) {
|
||||
if (this.isVideo(result)) return (result as EnhancedVideo).uuid
|
||||
if (this.isChannel(result)) return result.id + (result as EnhancedVideoChannel).host
|
||||
if (this.isPlaylist(result)) return (result as EnhancedPlaylist).uuid
|
||||
|
||||
throw new Error('Unknown result')
|
||||
},
|
||||
|
||||
loadUrl () {
|
||||
const query = this.$route.query as SearchUrl
|
||||
const queryPage = extractQueryToInt(query.page)
|
||||
|
@ -247,6 +293,7 @@
|
|||
if (query.licenceOneOf) count++
|
||||
if (query.languageOneOf) count++
|
||||
if (query.isLive) count++
|
||||
if (query.resultType) count++
|
||||
if (Array.isArray(query.tagsAllOf) && query.tagsAllOf.length !== 0) count++
|
||||
if (Array.isArray(query.tagsOneOf) && query.tagsOneOf.length !== 0) count++
|
||||
|
||||
|
@ -256,7 +303,11 @@
|
|||
buildVideoSearchQuery () {
|
||||
const query = this.$route.query as SearchUrl
|
||||
|
||||
const { start, count } = pageToAPIParams(this.currentPage, this.resultsPerVideosPage)
|
||||
const resultsPerPage = this.getResultTypeFilter() === 'videos'
|
||||
? this.filteredTypeResultsPerPage
|
||||
: this.summaryResultsCount.videos
|
||||
|
||||
const { start, count } = pageToAPIParams(this.currentPage, resultsPerPage)
|
||||
const { durationMin, durationMax } = durationRangeToAPIParams(query.durationRange)
|
||||
const { startDate, endDate } = publishedDateRangeToAPIParams(query.publishedDateRange)
|
||||
|
||||
|
@ -300,7 +351,11 @@
|
|||
buildChannelSearchQuery () {
|
||||
const query = this.$route.query as SearchUrl
|
||||
|
||||
const { start, count } = pageToAPIParams(this.currentPage, this.resultsPerChannelsPage)
|
||||
const resultsPerPage = this.getResultTypeFilter() === 'channels'
|
||||
? this.filteredTypeResultsPerPage
|
||||
: this.summaryResultsCount.channels
|
||||
|
||||
const { start, count } = pageToAPIParams(this.currentPage, resultsPerPage)
|
||||
|
||||
return {
|
||||
search: query.search,
|
||||
|
@ -313,7 +368,12 @@
|
|||
|
||||
buildPlaylistSearchQuery () {
|
||||
const query = this.$route.query as SearchUrl
|
||||
const { start, count } = pageToAPIParams(this.currentPage, this.resultsPerChannelsPage)
|
||||
|
||||
const resultsPerPage = this.getResultTypeFilter() === 'playlists'
|
||||
? this.filteredTypeResultsPerPage
|
||||
: this.summaryResultsCount.playlists
|
||||
|
||||
const { start, count } = pageToAPIParams(this.currentPage, resultsPerPage)
|
||||
|
||||
return {
|
||||
search: query.search,
|
||||
|
@ -325,13 +385,17 @@
|
|||
},
|
||||
|
||||
searchVideos (): Promise<ResultList<EnhancedVideo>> {
|
||||
if (this.isVideoSearchDisabled()) {
|
||||
return Promise.resolve({ total: 0, data: [] })
|
||||
}
|
||||
|
||||
const query = this.buildVideoSearchQuery()
|
||||
|
||||
return searchVideos(query)
|
||||
},
|
||||
|
||||
searchChannels (): Promise<ResultList<EnhancedVideoChannel>> {
|
||||
if (this.isChannelOrPlaylistSearchDisabled()) {
|
||||
if (this.isChannelSearchDisabled()) {
|
||||
return Promise.resolve({ data: [], total: 0 })
|
||||
}
|
||||
|
||||
|
@ -341,7 +405,7 @@
|
|||
},
|
||||
|
||||
searchPlaylists (): Promise<ResultList<EnhancedPlaylist>> {
|
||||
if (this.isChannelOrPlaylistSearchDisabled()) {
|
||||
if (this.isPlaylistSearchDisabled()) {
|
||||
return Promise.resolve({ data: [], total: 0 })
|
||||
}
|
||||
|
||||
|
@ -350,21 +414,19 @@
|
|||
return searchVideoPlaylists(query)
|
||||
},
|
||||
|
||||
getChannelsMaxPage () {
|
||||
return Math.ceil(this.channelsCount / this.resultsPerChannelsPage)
|
||||
},
|
||||
|
||||
getPlaylistsMaxPage () {
|
||||
return Math.ceil(this.playlistsCount / this.resultsPerPlaylistsPage)
|
||||
},
|
||||
|
||||
getVideosMaxPage () {
|
||||
return Math.ceil(this.videosCount / this.resultsPerVideosPage)
|
||||
},
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getMaxPage () {
|
||||
const resultType = (this.$route.query as SearchUrl).resultType
|
||||
|
||||
let maxPage: number
|
||||
|
||||
if (resultType === 'videos') maxPage = Math.ceil(this.totalVideos / this.filteredTypeResultsPerPage)
|
||||
else if (resultType === 'channels') maxPage = Math.ceil(this.totalChannels / this.filteredTypeResultsPerPage)
|
||||
else if (resultType === 'playlists') maxPage = Math.ceil(this.totalPlaylists / this.filteredTypeResultsPerPage)
|
||||
|
||||
// Limit to 10 pages
|
||||
return Math.min(10, Math.max(this.getPlaylistsMaxPage(), this.getChannelsMaxPage(), this.getVideosMaxPage()))
|
||||
return Math.min(10, maxPage)
|
||||
},
|
||||
|
||||
buildPages () {
|
||||
|
@ -379,11 +441,51 @@
|
|||
this.displayFilters = !this.displayFilters
|
||||
},
|
||||
|
||||
isChannelOrPlaylistSearchDisabled () {
|
||||
const query = this.$route.query as SearchUrl
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getResultTypeFilter () {
|
||||
return (this.$route.query as SearchUrl).resultType
|
||||
},
|
||||
|
||||
hasResultTypeFilter () {
|
||||
return !!this.getResultTypeFilter()
|
||||
},
|
||||
|
||||
getMoreResultsQuery (type: 'videos' | 'channels' | 'playlists'): SearchUrl {
|
||||
return {
|
||||
...this.$route.query,
|
||||
|
||||
resultType: type
|
||||
}
|
||||
},
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
isVideoSearchDisabled () {
|
||||
const { resultType } = this.$route.query as SearchUrl
|
||||
|
||||
if (resultType !== undefined && resultType !== 'videos') return true
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
isChannelSearchDisabled () {
|
||||
const { resultType, host } = this.$route.query as SearchUrl
|
||||
|
||||
if (resultType !== undefined && resultType !== 'channels') return true
|
||||
|
||||
// We can search against host for playlists and channels
|
||||
if (query.host) return this.countActiveFilters() > 1
|
||||
if (host) return this.countActiveFilters() > 1
|
||||
|
||||
return this.countActiveFilters() > 0
|
||||
},
|
||||
|
||||
isPlaylistSearchDisabled () {
|
||||
const { resultType, host } = this.$route.query as SearchUrl
|
||||
if (resultType !== undefined && resultType !== 'playlists') return true
|
||||
|
||||
// We can search against host for playlists and channels
|
||||
if (host) return this.countActiveFilters() > 1
|
||||
|
||||
return this.countActiveFilters() > 0
|
||||
}
|
||||
|
@ -463,4 +565,8 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include font-size(1.125rem);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue