]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/shared/shared-forms/advanced-input-filter.component.ts
Support ICU in TS components
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / shared-forms / advanced-input-filter.component.ts
CommitLineData
2e46eb97
C
1import * as debug from 'debug'
2import { Subject } from 'rxjs'
3import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
f676e0e3 4import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
2e46eb97 5import { ActivatedRoute, Params, Router } from '@angular/router'
dd6d2a7c 6import { RestService } from '@app/core'
1fd61899
C
7
8export type AdvancedInputFilter = {
978c87e7
C
9 title: string
10
dd6d2a7c
C
11 children: AdvancedInputFilterChild[]
12}
13
14export type AdvancedInputFilterChild = {
15 label: string
16 value: string
1fd61899
C
17}
18
2e46eb97
C
19const logger = debug('peertube:AdvancedInputFilterComponent')
20
1fd61899
C
21@Component({
22 selector: 'my-advanced-input-filter',
23 templateUrl: './advanced-input-filter.component.html',
24 styleUrls: [ './advanced-input-filter.component.scss' ]
25})
f676e0e3 26export class AdvancedInputFilterComponent implements OnInit, AfterViewInit {
1fd61899 27 @Input() filters: AdvancedInputFilter[] = []
dd24f1bb 28 @Input() emitOnInit = true
1fd61899 29
2e46eb97
C
30 @Output() search = new EventEmitter<string>()
31
32 searchValue: string
33
dd6d2a7c
C
34 private enabledFilters = new Set<string>()
35
2e46eb97
C
36 private searchStream: Subject<string>
37
f676e0e3
C
38 private viewInitialized = false
39 private emitSearchAfterViewInit = false
40
2e46eb97
C
41 constructor (
42 private route: ActivatedRoute,
dd6d2a7c 43 private restService: RestService,
2e46eb97
C
44 private router: Router
45 ) { }
46
47 ngOnInit () {
48 this.initSearchStream()
49 this.listenToRouteSearchChange()
50 }
1fd61899 51
f676e0e3
C
52 ngAfterViewInit () {
53 this.viewInitialized = true
54
55 // Init after view init to not send an event too early
dd24f1bb 56 if (this.emitOnInit && this.emitSearchAfterViewInit) this.emitSearch()
f676e0e3
C
57 }
58
2e46eb97 59 onInputSearch (event: Event) {
f676e0e3 60 this.scheduleSearchUpdate((event.target as HTMLInputElement).value)
1fd61899
C
61 }
62
63 onResetTableFilter () {
f676e0e3 64 this.immediateSearchUpdate('')
2e46eb97
C
65 }
66
67 hasFilters () {
7e76cc38 68 return this.filters && this.filters.length !== 0
2e46eb97
C
69 }
70
dd6d2a7c
C
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
f676e0e3 83 private scheduleSearchUpdate (value: string) {
2e46eb97
C
84 this.searchValue = value
85 this.searchStream.next(this.searchValue)
86 }
87
f676e0e3
C
88 private immediateSearchUpdate (value: string) {
89 this.searchValue = value
90
91 this.setQueryParams(this.searchValue)
dd6d2a7c 92 this.parseFilters(this.searchValue)
f676e0e3
C
93 this.emitSearch()
94 }
95
2e46eb97
C
96 private listenToRouteSearchChange () {
97 this.route.queryParams
98 .subscribe(params => {
99 const search = params.search || ''
100
101 logger('On route search change "%s".', search)
102
4beda9e1
C
103 if (this.searchValue === search) return
104
f676e0e3 105 this.searchValue = search
dd6d2a7c
C
106
107 this.parseFilters(this.searchValue)
108
f676e0e3 109 this.emitSearch()
2e46eb97
C
110 })
111 }
112
113 private initSearchStream () {
114 this.searchStream = new Subject()
115
116 this.searchStream
117 .pipe(
f676e0e3 118 debounceTime(300),
2e46eb97
C
119 distinctUntilChanged()
120 )
121 .subscribe(() => {
2e46eb97 122 this.setQueryParams(this.searchValue)
dd6d2a7c 123 this.parseFilters(this.searchValue)
f676e0e3
C
124
125 this.emitSearch()
2e46eb97
C
126 })
127 }
128
f676e0e3
C
129 private emitSearch () {
130 if (!this.viewInitialized) {
131 this.emitSearchAfterViewInit = true
132 return
133 }
134
135 logger('On search "%s".', this.searchValue)
136
137 this.search.emit(this.searchValue)
138 }
139
2e46eb97
C
140 private setQueryParams (search: string) {
141 const queryParams: Params = {}
142
143 if (search) Object.assign(queryParams, { search })
144 this.router.navigate([ ], { queryParams })
1fd61899 145 }
dd6d2a7c
C
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 }
1fd61899 176}