aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+search
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-29 15:19:22 +0200
committerChocobozzz <me@florianbigard.com>2021-07-29 15:19:22 +0200
commitaf7fd04a6706fb781e4622167b08dc6c9376f06a (patch)
tree4d6a84cd67143e07d762ba967f9d29e947e7436c /client/src/app/+search
parent9c9a236b541a286e165d67341e4ddd6ea2fabdf1 (diff)
downloadPeerTube-af7fd04a6706fb781e4622167b08dc6c9376f06a.tar.gz
PeerTube-af7fd04a6706fb781e4622167b08dc6c9376f06a.tar.zst
PeerTube-af7fd04a6706fb781e4622167b08dc6c9376f06a.zip
Add ability to filter by host in search page
Diffstat (limited to 'client/src/app/+search')
-rw-r--r--client/src/app/+search/search-filters.component.html16
-rw-r--r--client/src/app/+search/search-filters.component.ts8
-rw-r--r--client/src/app/+search/search.component.html2
-rw-r--r--client/src/app/+search/search.component.scss4
-rw-r--r--client/src/app/+search/search.component.ts24
5 files changed, 42 insertions, 12 deletions
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 @@
63 </div> 63 </div>
64 64
65 <div class="peertube-radio-container" *ngFor="let date of publishedDateRanges"> 65 <div class="peertube-radio-container" *ngFor="let date of publishedDateRanges">
66 <input type="radio" (change)="onInputUpdated()" name="publishedDateRange" [id]="date.id" [value]="date.id" [(ngModel)]="publishedDateRange"> 66 <input type="radio" (change)="onDurationOrPublishedUpdated()" name="publishedDateRange" [id]="date.id" [value]="date.id" [(ngModel)]="publishedDateRange">
67 <label [for]="date.id" class="radio">{{ date.label }}</label> 67 <label [for]="date.id" class="radio">{{ date.label }}</label>
68 </div> 68 </div>
69 </div> 69 </div>
@@ -79,7 +79,7 @@
79 <div class="row"> 79 <div class="row">
80 <div class="pl-0 col-sm-6"> 80 <div class="pl-0 col-sm-6">
81 <input 81 <input
82 (change)="onInputUpdated()" 82 (change)="onDurationOrPublishedUpdated()"
83 (keydown.enter)="$event.preventDefault()" 83 (keydown.enter)="$event.preventDefault()"
84 type="text" id="original-publication-after" name="original-publication-after" 84 type="text" id="original-publication-after" name="original-publication-after"
85 i18n-placeholder placeholder="After..." 85 i18n-placeholder placeholder="After..."
@@ -89,7 +89,7 @@
89 </div> 89 </div>
90 <div class="pr-0 col-sm-6"> 90 <div class="pr-0 col-sm-6">
91 <input 91 <input
92 (change)="onInputUpdated()" 92 (change)="onDurationOrPublishedUpdated()"
93 (keydown.enter)="$event.preventDefault()" 93 (keydown.enter)="$event.preventDefault()"
94 type="text" id="original-publication-before" name="original-publication-before" 94 type="text" id="original-publication-before" name="original-publication-before"
95 i18n-placeholder placeholder="Before..." 95 i18n-placeholder placeholder="Before..."
@@ -112,7 +112,7 @@
112 </div> 112 </div>
113 113
114 <div class="peertube-radio-container" *ngFor="let duration of durationRanges"> 114 <div class="peertube-radio-container" *ngFor="let duration of durationRanges">
115 <input type="radio" (change)="onInputUpdated()" name="durationRange" [id]="duration.id" [value]="duration.id" [(ngModel)]="durationRange"> 115 <input type="radio" (change)="onDurationOrPublishedUpdated()" name="durationRange" [id]="duration.id" [value]="duration.id" [(ngModel)]="durationRange">
116 <label [for]="duration.id" class="radio">{{ duration.label }}</label> 116 <label [for]="duration.id" class="radio">{{ duration.label }}</label>
117 </div> 117 </div>
118 </div> 118 </div>
@@ -174,6 +174,14 @@
174 <my-select-tags name="tagsOneOf" labelForId="tagsOneOf" id="tagsOneOf" [(ngModel)]="advancedSearch.tagsOneOf"></my-select-tags> 174 <my-select-tags name="tagsOneOf" labelForId="tagsOneOf" id="tagsOneOf" [(ngModel)]="advancedSearch.tagsOneOf"></my-select-tags>
175 </div> 175 </div>
176 176
177 <div class="form-group">
178 <label i18n for="host">PeerTube instance host</label>
179
180 <input (change)="onDurationOrPublishedUpdated()" (keydown.enter)="$event.preventDefault()" type="text" id="host" name="host"
181 placeholder="example.com" [(ngModel)]="advancedSearch.host" class="form-control"
182 >
183 </div>
184
177 <div class="form-group" *ngIf="isSearchTargetEnabled()"> 185 <div class="form-group" *ngIf="isSearchTargetEnabled()">
178 <div class="radio-label label-container"> 186 <div class="radio-label label-container">
179 <label i18n>Search target</label> 187 <label i18n>Search target</label>
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 {
108 this.loadOriginallyPublishedAtYears() 108 this.loadOriginallyPublishedAtYears()
109 } 109 }
110 110
111 onInputUpdated () { 111 onDurationOrPublishedUpdated () {
112 this.updateModelFromDurationRange() 112 this.updateModelFromDurationRange()
113 this.updateModelFromPublishedRange() 113 this.updateModelFromPublishedRange()
114 this.updateModelFromOriginallyPublishedAtYears() 114 this.updateModelFromOriginallyPublishedAtYears()
115 } 115 }
116 116
117 formUpdated () { 117 formUpdated () {
118 this.onInputUpdated() 118 this.onDurationOrPublishedUpdated()
119 this.filtered.emit(this.advancedSearch) 119 this.filtered.emit(this.advancedSearch)
120 } 120 }
121 121
@@ -127,7 +127,7 @@ export class SearchFiltersComponent implements OnInit {
127 this.durationRange = undefined 127 this.durationRange = undefined
128 this.publishedDateRange = undefined 128 this.publishedDateRange = undefined
129 129
130 this.onInputUpdated() 130 this.onDurationOrPublishedUpdated()
131 } 131 }
132 132
133 resetField (fieldName: string, value?: any) { 133 resetField (fieldName: string, value?: any) {
@@ -136,7 +136,7 @@ export class SearchFiltersComponent implements OnInit {
136 136
137 resetLocalField (fieldName: string, value?: any) { 137 resetLocalField (fieldName: string, value?: any) {
138 this[fieldName] = value 138 this[fieldName] = value
139 this.onInputUpdated() 139 this.onDurationOrPublishedUpdated()
140 } 140 }
141 141
142 resetOriginalPublicationYears () { 142 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 @@
24 24
25 <div class="results-filter collapse-transition" [ngbCollapse]="isSearchFilterCollapsed"> 25 <div class="results-filter collapse-transition" [ngbCollapse]="isSearchFilterCollapsed">
26 <my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters> 26 <my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters>
27
28 <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
27 </div> 29 </div>
28 </div> 30 </div>
29 31
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 @@
15 padding: 40px; 15 padding: 40px;
16} 16}
17 17
18.alert-danger {
19 margin-top: 10px;
20}
21
18.results-header { 22.results-header {
19 font-size: 16px; 23 font-size: 16px;
20 padding-bottom: 20px; 24 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'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { AuthService, HooksService, MetaService, Notifier, ServerService, User, UserService } from '@app/core' 5import { AuthService, HooksService, MetaService, Notifier, ServerService, User, UserService } from '@app/core'
6import { immutableAssign } from '@app/helpers' 6import { immutableAssign } from '@app/helpers'
7import { validateHost } from '@app/shared/form-validators/host-validators'
7import { Video, VideoChannel } from '@app/shared/shared-main' 8import { Video, VideoChannel } from '@app/shared/shared-main'
8import { AdvancedSearch, SearchService } from '@app/shared/shared-search' 9import { AdvancedSearch, SearchService } from '@app/shared/shared-search'
9import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' 10import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature'
@@ -16,6 +17,8 @@ import { HTMLServerConfig, SearchTargetType } from '@shared/models'
16 templateUrl: './search.component.html' 17 templateUrl: './search.component.html'
17}) 18})
18export class SearchComponent implements OnInit, OnDestroy { 19export class SearchComponent implements OnInit, OnDestroy {
20 error: string
21
19 results: (Video | VideoChannel)[] = [] 22 results: (Video | VideoChannel)[] = []
20 23
21 pagination = { 24 pagination = {
@@ -89,8 +92,10 @@ export class SearchComponent implements OnInit, OnDestroy {
89 this.advancedSearch.searchTarget = this.getDefaultSearchTarget() 92 this.advancedSearch.searchTarget = this.getDefaultSearchTarget()
90 } 93 }
91 94
92 // Don't hide filters if we have some of them AND the user just came on the webpage 95 this.error = this.checkFieldsAndGetError()
93 this.isSearchFilterCollapsed = this.isInitialLoad === false || !this.advancedSearch.containsValues() 96
97 // Don't hide filters if we have some of them AND the user just came on the webpage, or we have an error
98 this.isSearchFilterCollapsed = !this.error && (this.isInitialLoad === false || !this.advancedSearch.containsValues())
94 this.isInitialLoad = false 99 this.isInitialLoad = false
95 100
96 this.search() 101 this.search()
@@ -126,6 +131,9 @@ export class SearchComponent implements OnInit, OnDestroy {
126 } 131 }
127 132
128 search () { 133 search () {
134 this.error = this.checkFieldsAndGetError()
135 if (this.error) return
136
129 this.isSearching = true 137 this.isSearching = true
130 138
131 forkJoin([ 139 forkJoin([
@@ -280,7 +288,7 @@ export class SearchComponent implements OnInit, OnDestroy {
280 const params = { 288 const params = {
281 search: this.currentSearch, 289 search: this.currentSearch,
282 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage }), 290 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage }),
283 searchTarget: this.advancedSearch.searchTarget 291 advancedSearch: this.advancedSearch
284 } 292 }
285 293
286 return this.hooks.wrapObsFun( 294 return this.hooks.wrapObsFun(
@@ -298,7 +306,7 @@ export class SearchComponent implements OnInit, OnDestroy {
298 const params = { 306 const params = {
299 search: this.currentSearch, 307 search: this.currentSearch,
300 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.playlistsPerPage }), 308 componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.playlistsPerPage }),
301 searchTarget: this.advancedSearch.searchTarget 309 advancedSearch: this.advancedSearch
302 } 310 }
303 311
304 return this.hooks.wrapObsFun( 312 return this.hooks.wrapObsFun(
@@ -319,4 +327,12 @@ export class SearchComponent implements OnInit, OnDestroy {
319 327
320 return 'local' 328 return 'local'
321 } 329 }
330
331 private checkFieldsAndGetError () {
332 if (this.advancedSearch.host && !validateHost(this.advancedSearch.host)) {
333 return $localize`PeerTube instance host filter is invalid`
334 }
335
336 return undefined
337 }
322} 338}