diff options
Diffstat (limited to 'client/src/app/shared')
4 files changed, 89 insertions, 11 deletions
diff --git a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts index 10f5861b9..b902726fa 100644 --- a/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts +++ b/client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts | |||
@@ -39,23 +39,23 @@ export class AbuseListTableComponent extends RestTable implements OnInit { | |||
39 | title: $localize`Advanced filters`, | 39 | title: $localize`Advanced filters`, |
40 | children: [ | 40 | children: [ |
41 | { | 41 | { |
42 | queryParams: { search: 'state:pending' }, | 42 | value: 'state:pending', |
43 | label: $localize`Unsolved reports` | 43 | label: $localize`Unsolved reports` |
44 | }, | 44 | }, |
45 | { | 45 | { |
46 | queryParams: { search: 'state:accepted' }, | 46 | value: 'state:accepted', |
47 | label: $localize`Accepted reports` | 47 | label: $localize`Accepted reports` |
48 | }, | 48 | }, |
49 | { | 49 | { |
50 | queryParams: { search: 'state:rejected' }, | 50 | value: 'state:rejected', |
51 | label: $localize`Refused reports` | 51 | label: $localize`Refused reports` |
52 | }, | 52 | }, |
53 | { | 53 | { |
54 | queryParams: { search: 'videoIs:blacklisted' }, | 54 | value: 'videoIs:blacklisted', |
55 | label: $localize`Reports with blocked videos` | 55 | label: $localize`Reports with blocked videos` |
56 | }, | 56 | }, |
57 | { | 57 | { |
58 | queryParams: { search: 'videoIs:deleted' }, | 58 | value: 'videoIs:deleted', |
59 | label: $localize`Reports with deleted videos` | 59 | label: $localize`Reports with deleted videos` |
60 | } | 60 | } |
61 | ] | 61 | ] |
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.html b/client/src/app/shared/shared-forms/advanced-input-filter.component.html index c662b9bb6..7031cb53b 100644 --- a/client/src/app/shared/shared-forms/advanced-input-filter.component.html +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.html | |||
@@ -8,9 +8,11 @@ | |||
8 | <ng-container *ngFor="let group of filters"> | 8 | <ng-container *ngFor="let group of filters"> |
9 | <h6 class="dropdown-header">{{ group.title }}</h6> | 9 | <h6 class="dropdown-header">{{ group.title }}</h6> |
10 | 10 | ||
11 | <a *ngFor="let filter of group.children" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item"> | 11 | <button *ngFor="let filter of group.children" (click)="onFilterClick(filter)" class="dropdown-item"> |
12 | <my-global-icon [ngClass]="{ 'no-visible': !isFilterEnabled(filter) }" iconName="tick"></my-global-icon> | ||
13 | |||
12 | {{ filter.label }} | 14 | {{ filter.label }} |
13 | </a> | 15 | </button> |
14 | </ng-container> | 16 | </ng-container> |
15 | </div> | 17 | </div> |
16 | </div> | 18 | </div> |
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss index 07a43761c..ee1b3b508 100644 --- a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss | |||
@@ -1,6 +1,10 @@ | |||
1 | @use '_variables' as *; | 1 | @use '_variables' as *; |
2 | @use '_mixins' as *; | 2 | @use '_mixins' as *; |
3 | 3 | ||
4 | .dropdown-item { | ||
5 | font-size: 14px; | ||
6 | } | ||
7 | |||
4 | input { | 8 | input { |
5 | @include peertube-input-text(250px); | 9 | @include peertube-input-text(250px); |
6 | } | 10 | } |
@@ -8,3 +12,22 @@ input { | |||
8 | .input-group-text { | 12 | .input-group-text { |
9 | background-color: transparent; | 13 | background-color: transparent; |
10 | } | 14 | } |
15 | |||
16 | my-global-icon { | ||
17 | $size: 18px; | ||
18 | $margin: 2px; | ||
19 | |||
20 | @include margin-right($margin); | ||
21 | |||
22 | opacity: 1; | ||
23 | width: 18px; | ||
24 | height: 18px; | ||
25 | |||
26 | |||
27 | &.no-visible { | ||
28 | @include margin-right($size + $margin); | ||
29 | |||
30 | width: 0; | ||
31 | height: 0; | ||
32 | } | ||
33 | } | ||
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts index a12dddf7a..d8aeaa0fa 100644 --- a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts | |||
@@ -3,14 +3,17 @@ import { Subject } from 'rxjs' | |||
3 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' | 3 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' |
4 | import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | 4 | import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' |
5 | import { ActivatedRoute, Params, Router } from '@angular/router' | 5 | import { ActivatedRoute, Params, Router } from '@angular/router' |
6 | import { RestService } from '@app/core' | ||
6 | 7 | ||
7 | export type AdvancedInputFilter = { | 8 | export type AdvancedInputFilter = { |
8 | title: string | 9 | title: string |
9 | 10 | ||
10 | children: { | 11 | children: AdvancedInputFilterChild[] |
11 | label: string | 12 | } |
12 | queryParams: Params | 13 | |
13 | }[] | 14 | export type AdvancedInputFilterChild = { |
15 | label: string | ||
16 | value: string | ||
14 | } | 17 | } |
15 | 18 | ||
16 | const logger = debug('peertube:AdvancedInputFilterComponent') | 19 | const logger = debug('peertube:AdvancedInputFilterComponent') |
@@ -28,6 +31,8 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
28 | 31 | ||
29 | searchValue: string | 32 | searchValue: string |
30 | 33 | ||
34 | private enabledFilters = new Set<string>() | ||
35 | |||
31 | private searchStream: Subject<string> | 36 | private searchStream: Subject<string> |
32 | 37 | ||
33 | private viewInitialized = false | 38 | private viewInitialized = false |
@@ -35,6 +40,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
35 | 40 | ||
36 | constructor ( | 41 | constructor ( |
37 | private route: ActivatedRoute, | 42 | private route: ActivatedRoute, |
43 | private restService: RestService, | ||
38 | private router: Router | 44 | private router: Router |
39 | ) { } | 45 | ) { } |
40 | 46 | ||
@@ -62,6 +68,18 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
62 | return this.filters && this.filters.length !== 0 | 68 | return this.filters && this.filters.length !== 0 |
63 | } | 69 | } |
64 | 70 | ||
71 | isFilterEnabled (filter: AdvancedInputFilterChild) { | ||
72 | return this.enabledFilters.has(filter.value) | ||
73 | } | ||
74 | |||
75 | onFilterClick (filter: AdvancedInputFilterChild) { | ||
76 | const newSearch = this.isFilterEnabled(filter) | ||
77 | ? this.removeFilterToSearch(this.searchValue, filter) | ||
78 | : this.addFilterToSearch(this.searchValue, filter) | ||
79 | |||
80 | this.router.navigate([ '.' ], { relativeTo: this.route, queryParams: { search: newSearch.trim() } }) | ||
81 | } | ||
82 | |||
65 | private scheduleSearchUpdate (value: string) { | 83 | private scheduleSearchUpdate (value: string) { |
66 | this.searchValue = value | 84 | this.searchValue = value |
67 | this.searchStream.next(this.searchValue) | 85 | this.searchStream.next(this.searchValue) |
@@ -71,6 +89,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
71 | this.searchValue = value | 89 | this.searchValue = value |
72 | 90 | ||
73 | this.setQueryParams(this.searchValue) | 91 | this.setQueryParams(this.searchValue) |
92 | this.parseFilters(this.searchValue) | ||
74 | this.emitSearch() | 93 | this.emitSearch() |
75 | } | 94 | } |
76 | 95 | ||
@@ -84,6 +103,9 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
84 | if (this.searchValue === search) return | 103 | if (this.searchValue === search) return |
85 | 104 | ||
86 | this.searchValue = search | 105 | this.searchValue = search |
106 | |||
107 | this.parseFilters(this.searchValue) | ||
108 | |||
87 | this.emitSearch() | 109 | this.emitSearch() |
88 | }) | 110 | }) |
89 | } | 111 | } |
@@ -98,6 +120,7 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
98 | ) | 120 | ) |
99 | .subscribe(() => { | 121 | .subscribe(() => { |
100 | this.setQueryParams(this.searchValue) | 122 | this.setQueryParams(this.searchValue) |
123 | this.parseFilters(this.searchValue) | ||
101 | 124 | ||
102 | this.emitSearch() | 125 | this.emitSearch() |
103 | }) | 126 | }) |
@@ -120,4 +143,34 @@ export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | |||
120 | if (search) Object.assign(queryParams, { search }) | 143 | if (search) Object.assign(queryParams, { search }) |
121 | this.router.navigate([ ], { queryParams }) | 144 | this.router.navigate([ ], { queryParams }) |
122 | } | 145 | } |
146 | |||
147 | private removeFilterToSearch (search: string, removedFilter: AdvancedInputFilterChild) { | ||
148 | return search.replace(removedFilter.value, '') | ||
149 | } | ||
150 | |||
151 | private addFilterToSearch (search: string, newFilter: AdvancedInputFilterChild) { | ||
152 | const prefix = newFilter.value.split(':').shift() | ||
153 | |||
154 | // Tokenize search and remove a potential existing filter | ||
155 | const tokens = this.restService.tokenizeString(search) | ||
156 | .filter(t => !t.startsWith(prefix)) | ||
157 | |||
158 | tokens.push(newFilter.value) | ||
159 | |||
160 | return tokens.join(' ') | ||
161 | } | ||
162 | |||
163 | private parseFilters (search: string) { | ||
164 | const tokens = this.restService.tokenizeString(search) | ||
165 | |||
166 | this.enabledFilters = new Set() | ||
167 | |||
168 | for (const group of this.filters) { | ||
169 | for (const filter of group.children) { | ||
170 | if (tokens.includes(filter.value)) { | ||
171 | this.enabledFilters.add(filter.value) | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | } | ||
123 | } | 176 | } |