Fix angular 9 build
[oweals/peertube.git] / client / src / app / search / search.component.ts
1 import { Component, OnDestroy, OnInit } from '@angular/core'
2 import { ActivatedRoute, Router } from '@angular/router'
3 import { AuthService, Notifier } from '@app/core'
4 import { forkJoin, of, Subscription } from 'rxjs'
5 import { SearchService } from '@app/search/search.service'
6 import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
7 import { I18n } from '@ngx-translate/i18n-polyfill'
8 import { MetaService } from '@ngx-meta/core'
9 import { AdvancedSearch } from '@app/search/advanced-search.model'
10 import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
11 import { immutableAssign } from '@app/shared/misc/utils'
12 import { Video } from '@app/shared/video/video.model'
13 import { HooksService } from '@app/core/plugins/hooks.service'
14
15 @Component({
16   selector: 'my-search',
17   styleUrls: [ './search.component.scss' ],
18   templateUrl: './search.component.html'
19 })
20 export class SearchComponent implements OnInit, OnDestroy {
21   results: (Video | VideoChannel)[] = []
22
23   pagination: ComponentPagination = {
24     currentPage: 1,
25     itemsPerPage: 10, // Only for videos, use another variable for channels
26     totalItems: null
27   }
28   advancedSearch: AdvancedSearch = new AdvancedSearch()
29   isSearchFilterCollapsed = true
30   currentSearch: string
31
32   private subActivatedRoute: Subscription
33   private isInitialLoad = false // set to false to show the search filters on first arrival
34   private firstSearch = true
35
36   private channelsPerPage = 2
37
38   constructor (
39     private i18n: I18n,
40     private route: ActivatedRoute,
41     private router: Router,
42     private metaService: MetaService,
43     private notifier: Notifier,
44     private searchService: SearchService,
45     private authService: AuthService,
46     private hooks: HooksService
47   ) { }
48
49   get user () {
50     return this.authService.getUser()
51   }
52
53   ngOnInit () {
54     this.subActivatedRoute = this.route.queryParams.subscribe(
55       queryParams => {
56         const querySearch = queryParams['search']
57
58         // Search updated, reset filters
59         if (this.currentSearch !== querySearch) {
60           this.resetPagination()
61           this.advancedSearch.reset()
62
63           this.currentSearch = querySearch || undefined
64           this.updateTitle()
65         }
66
67         this.advancedSearch = new AdvancedSearch(queryParams)
68
69         // Don't hide filters if we have some of them AND the user just came on the webpage
70         this.isSearchFilterCollapsed = this.isInitialLoad === false || !this.advancedSearch.containsValues()
71         this.isInitialLoad = false
72
73         this.search()
74       },
75
76       err => this.notifier.error(err.text)
77     )
78
79     this.hooks.runAction('action:search.init', 'search')
80   }
81
82   ngOnDestroy () {
83     if (this.subActivatedRoute) this.subActivatedRoute.unsubscribe()
84   }
85
86   isVideoChannel (d: VideoChannel | Video): d is VideoChannel {
87     return d instanceof VideoChannel
88   }
89
90   isVideo (v: VideoChannel | Video): v is Video {
91     return v instanceof Video
92   }
93
94   isUserLoggedIn () {
95     return this.authService.isLoggedIn()
96   }
97
98   search () {
99     forkJoin([
100       this.getVideosObs(),
101       this.getVideoChannelObs()
102     ])
103       .subscribe(
104         ([ videosResult, videoChannelsResult ]) => {
105           this.results = this.results
106                              .concat(videoChannelsResult.data)
107                              .concat(videosResult.data)
108           this.pagination.totalItems = videosResult.total + videoChannelsResult.total
109
110           // Focus on channels if there are no enough videos
111           if (this.firstSearch === true && videosResult.data.length < this.pagination.itemsPerPage) {
112             this.resetPagination()
113             this.firstSearch = false
114
115             this.channelsPerPage = 10
116             this.search()
117           }
118
119           this.firstSearch = false
120         },
121
122         err => this.notifier.error(err.message)
123       )
124   }
125
126   onNearOfBottom () {
127     // Last page
128     if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
129
130     this.pagination.currentPage += 1
131     this.search()
132   }
133
134   onFiltered () {
135     this.resetPagination()
136
137     this.updateUrlFromAdvancedSearch()
138   }
139
140   numberOfFilters () {
141     return this.advancedSearch.size()
142   }
143
144   removeVideoFromArray (video: Video) {
145     this.results = this.results.filter(r => !this.isVideo(r) || r.id !== video.id)
146   }
147
148   private resetPagination () {
149     this.pagination.currentPage = 1
150     this.pagination.totalItems = null
151     this.channelsPerPage = 2
152
153     this.results = []
154   }
155
156   private updateTitle () {
157     const suffix = this.currentSearch ? ' ' + this.currentSearch : ''
158     this.metaService.setTitle(this.i18n('Search') + suffix)
159   }
160
161   private updateUrlFromAdvancedSearch () {
162     const search = this.currentSearch || undefined
163
164     this.router.navigate([], {
165       relativeTo: this.route,
166       queryParams: Object.assign({}, this.advancedSearch.toUrlObject(), { search })
167     })
168   }
169
170   private getVideosObs () {
171     const params = {
172       search: this.currentSearch,
173       componentPagination: this.pagination,
174       advancedSearch: this.advancedSearch
175     }
176
177     return this.hooks.wrapObsFun(
178       this.searchService.searchVideos.bind(this.searchService),
179       params,
180       'search',
181       'filter:api.search.videos.list.params',
182       'filter:api.search.videos.list.result'
183     )
184   }
185
186   private getVideoChannelObs () {
187     if (!this.currentSearch) return of({ data: [], total: 0 })
188
189     const params = {
190       search: this.currentSearch,
191       componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage })
192     }
193
194     return this.hooks.wrapObsFun(
195       this.searchService.searchVideoChannels.bind(this.searchService),
196       params,
197       'search',
198       'filter:api.search.video-channels.list.params',
199       'filter:api.search.video-channels.list.result'
200     )
201   }
202 }