Filter by result type

This commit is contained in:
Chocobozzz 2022-12-28 13:41:10 +01:00
parent d1a30ab252
commit 9bdd38c606
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
4 changed files with 223 additions and 85 deletions

View File

@ -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 = ''

View File

@ -11,6 +11,8 @@ export interface SearchUrl {
tagsAllOf?: string[]
tagsOneOf?: string[]
resultType?: 'videos' | 'channels' | 'playlists'
isLive?: string
sort?: string

View File

@ -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;

View File

@ -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>