From: Chocobozzz Date: Thu, 29 Jul 2021 13:19:22 +0000 (+0200) Subject: Add ability to filter by host in search page X-Git-Tag: v3.4.0-rc.1~167 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=af7fd04a6706fb781e4622167b08dc6c9376f06a;hp=9c9a236b541a286e165d67341e4ddd6ea2fabdf1;p=github%2FChocobozzz%2FPeerTube.git Add ability to filter by host in search page --- diff --git a/client/src/app/+search/search-filters.component.html b/client/src/app/+search/search-filters.component.html index 421bc7f6f..4b87a2102 100644 --- a/client/src/app/+search/search-filters.component.html +++ b/client/src/app/+search/search-filters.component.html @@ -63,7 +63,7 @@
- +
@@ -79,7 +79,7 @@
- +
@@ -174,6 +174,14 @@
+
+ + + +
+
diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts index afa523b91..5972ba553 100644 --- a/client/src/app/+search/search-filters.component.ts +++ b/client/src/app/+search/search-filters.component.ts @@ -108,14 +108,14 @@ export class SearchFiltersComponent implements OnInit { this.loadOriginallyPublishedAtYears() } - onInputUpdated () { + onDurationOrPublishedUpdated () { this.updateModelFromDurationRange() this.updateModelFromPublishedRange() this.updateModelFromOriginallyPublishedAtYears() } formUpdated () { - this.onInputUpdated() + this.onDurationOrPublishedUpdated() this.filtered.emit(this.advancedSearch) } @@ -127,7 +127,7 @@ export class SearchFiltersComponent implements OnInit { this.durationRange = undefined this.publishedDateRange = undefined - this.onInputUpdated() + this.onDurationOrPublishedUpdated() } resetField (fieldName: string, value?: any) { @@ -136,7 +136,7 @@ export class SearchFiltersComponent implements OnInit { resetLocalField (fieldName: string, value?: any) { this[fieldName] = value - this.onInputUpdated() + this.onDurationOrPublishedUpdated() } resetOriginalPublicationYears () { diff --git a/client/src/app/+search/search.component.html b/client/src/app/+search/search.component.html index b28abca6a..dc8b4d595 100644 --- a/client/src/app/+search/search.component.html +++ b/client/src/app/+search/search.component.html @@ -24,6 +24,8 @@
+ +
{{ error }}
diff --git a/client/src/app/+search/search.component.scss b/client/src/app/+search/search.component.scss index fca704d27..b521825e5 100644 --- a/client/src/app/+search/search.component.scss +++ b/client/src/app/+search/search.component.scss @@ -15,6 +15,10 @@ padding: 40px; } +.alert-danger { + margin-top: 10px; +} + .results-header { font-size: 16px; padding-bottom: 20px; diff --git a/client/src/app/+search/search.component.ts b/client/src/app/+search/search.component.ts index 235bbfa4c..250062e0c 100644 --- a/client/src/app/+search/search.component.ts +++ b/client/src/app/+search/search.component.ts @@ -4,6 +4,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { AuthService, HooksService, MetaService, Notifier, ServerService, User, UserService } from '@app/core' import { immutableAssign } from '@app/helpers' +import { validateHost } from '@app/shared/form-validators/host-validators' import { Video, VideoChannel } from '@app/shared/shared-main' import { AdvancedSearch, SearchService } from '@app/shared/shared-search' import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' @@ -16,6 +17,8 @@ import { HTMLServerConfig, SearchTargetType } from '@shared/models' templateUrl: './search.component.html' }) export class SearchComponent implements OnInit, OnDestroy { + error: string + results: (Video | VideoChannel)[] = [] pagination = { @@ -89,8 +92,10 @@ export class SearchComponent implements OnInit, OnDestroy { this.advancedSearch.searchTarget = this.getDefaultSearchTarget() } - // Don't hide filters if we have some of them AND the user just came on the webpage - this.isSearchFilterCollapsed = this.isInitialLoad === false || !this.advancedSearch.containsValues() + this.error = this.checkFieldsAndGetError() + + // Don't hide filters if we have some of them AND the user just came on the webpage, or we have an error + this.isSearchFilterCollapsed = !this.error && (this.isInitialLoad === false || !this.advancedSearch.containsValues()) this.isInitialLoad = false this.search() @@ -126,6 +131,9 @@ export class SearchComponent implements OnInit, OnDestroy { } search () { + this.error = this.checkFieldsAndGetError() + if (this.error) return + this.isSearching = true forkJoin([ @@ -280,7 +288,7 @@ export class SearchComponent implements OnInit, OnDestroy { const params = { search: this.currentSearch, componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage }), - searchTarget: this.advancedSearch.searchTarget + advancedSearch: this.advancedSearch } return this.hooks.wrapObsFun( @@ -298,7 +306,7 @@ export class SearchComponent implements OnInit, OnDestroy { const params = { search: this.currentSearch, componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.playlistsPerPage }), - searchTarget: this.advancedSearch.searchTarget + advancedSearch: this.advancedSearch } return this.hooks.wrapObsFun( @@ -319,4 +327,12 @@ export class SearchComponent implements OnInit, OnDestroy { return 'local' } + + private checkFieldsAndGetError () { + if (this.advancedSearch.host && !validateHost(this.advancedSearch.host)) { + return $localize`PeerTube instance host filter is invalid` + } + + return undefined + } } diff --git a/client/src/app/shared/form-validators/host-validators.ts b/client/src/app/shared/form-validators/host-validators.ts index d750113ef..6f410a50a 100644 --- a/client/src/app/shared/form-validators/host-validators.ts +++ b/client/src/app/shared/form-validators/host-validators.ts @@ -1,7 +1,7 @@ import { AbstractControl, ValidatorFn, Validators } from '@angular/forms' import { BuildFormValidator } from './form-validator.model' -function validateHost (value: string) { +export function validateHost (value: string) { // Thanks to http://stackoverflow.com/a/106223 const HOST_REGEXP = new RegExp( '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$' @@ -10,7 +10,7 @@ function validateHost (value: string) { return HOST_REGEXP.test(value) } -function validateHandle (value: string) { +export function validateHandle (value: string) { if (!value) return false return value.includes('@') diff --git a/client/src/app/shared/shared-search/advanced-search.model.ts b/client/src/app/shared/shared-search/advanced-search.model.ts index 2c83f53b6..9c55f6cd8 100644 --- a/client/src/app/shared/shared-search/advanced-search.model.ts +++ b/client/src/app/shared/shared-search/advanced-search.model.ts @@ -1,4 +1,11 @@ -import { BooleanBothQuery, BooleanQuery, SearchTargetType, VideosSearchQuery } from '@shared/models' +import { + BooleanBothQuery, + BooleanQuery, + SearchTargetType, + VideoChannelsSearchQuery, + VideoPlaylistsSearchQuery, + VideosSearchQuery +} from '@shared/models' export class AdvancedSearch { startDate: string // ISO 8601 @@ -23,6 +30,8 @@ export class AdvancedSearch { isLive: BooleanQuery + host: string + sort: string searchTarget: SearchTargetType @@ -45,6 +54,8 @@ export class AdvancedSearch { isLive?: BooleanQuery + host?: string + durationMin?: string durationMax?: string sort?: string @@ -68,6 +79,8 @@ export class AdvancedSearch { this.durationMin = parseInt(options.durationMin, 10) this.durationMax = parseInt(options.durationMax, 10) + this.host = options.host || undefined + this.searchTarget = options.searchTarget || undefined if (isNaN(this.durationMin)) this.durationMin = undefined @@ -101,6 +114,7 @@ export class AdvancedSearch { this.durationMin = undefined this.durationMax = undefined this.isLive = undefined + this.host = undefined this.sort = '-match' } @@ -120,12 +134,13 @@ export class AdvancedSearch { durationMin: this.durationMin, durationMax: this.durationMax, isLive: this.isLive, + host: this.host, sort: this.sort, searchTarget: this.searchTarget } } - toAPIObject (): VideosSearchQuery { + toVideosAPIObject (): VideosSearchQuery { let isLive: boolean if (this.isLive) isLive = this.isLive === 'true' @@ -142,12 +157,27 @@ export class AdvancedSearch { tagsAllOf: this.tagsAllOf, durationMin: this.durationMin, durationMax: this.durationMax, + host: this.host, isLive, sort: this.sort, searchTarget: this.searchTarget } } + toPlaylistAPIObject (): VideoPlaylistsSearchQuery { + return { + host: this.host, + searchTarget: this.searchTarget + } + } + + toChannelAPIObject (): VideoChannelsSearchQuery { + return { + host: this.host, + searchTarget: this.searchTarget + } + } + size () { let acc = 0 diff --git a/client/src/app/shared/shared-search/search.service.ts b/client/src/app/shared/shared-search/search.service.ts index ad258f5e5..2c26eb2e5 100644 --- a/client/src/app/shared/shared-search/search.service.ts +++ b/client/src/app/shared/shared-search/search.service.ts @@ -7,7 +7,6 @@ import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/sha import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' import { ResultList, - SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel, VideoPlaylist as VideoPlaylistServerModel @@ -33,8 +32,8 @@ export class SearchService { } searchVideos (parameters: { - search: string, - componentPagination?: ComponentPaginationLight, + search: string + componentPagination?: ComponentPaginationLight advancedSearch?: AdvancedSearch }): Observable> { const { search, componentPagination, advancedSearch } = parameters @@ -52,7 +51,7 @@ export class SearchService { if (search) params = params.append('search', search) if (advancedSearch) { - const advancedSearchObject = advancedSearch.toAPIObject() + const advancedSearchObject = advancedSearch.toVideosAPIObject() params = this.restService.addObjectParams(params, advancedSearchObject) } @@ -65,11 +64,11 @@ export class SearchService { } searchVideoChannels (parameters: { - search: string, - searchTarget?: SearchTargetType, + search: string + advancedSearch?: AdvancedSearch componentPagination?: ComponentPaginationLight }): Observable> { - const { search, componentPagination, searchTarget } = parameters + const { search, advancedSearch, componentPagination } = parameters const url = SearchService.BASE_SEARCH_URL + 'video-channels' @@ -82,8 +81,9 @@ export class SearchService { params = this.restService.addRestGetParams(params, pagination) params = params.append('search', search) - if (searchTarget) { - params = params.append('searchTarget', searchTarget as string) + if (advancedSearch) { + const advancedSearchObject = advancedSearch.toChannelAPIObject() + params = this.restService.addObjectParams(params, advancedSearchObject) } return this.authHttp @@ -95,11 +95,11 @@ export class SearchService { } searchVideoPlaylists (parameters: { - search: string, - searchTarget?: SearchTargetType, + search: string + advancedSearch?: AdvancedSearch componentPagination?: ComponentPaginationLight }): Observable> { - const { search, componentPagination, searchTarget } = parameters + const { search, advancedSearch, componentPagination } = parameters const url = SearchService.BASE_SEARCH_URL + 'video-playlists' @@ -112,8 +112,9 @@ export class SearchService { params = this.restService.addRestGetParams(params, pagination) params = params.append('search', search) - if (searchTarget) { - params = params.append('searchTarget', searchTarget as string) + if (advancedSearch) { + const advancedSearchObject = advancedSearch.toPlaylistAPIObject() + params = this.restService.addObjectParams(params, advancedSearchObject) } return this.authHttp