]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/app/header/search-typeahead.component.ts
Simplify code for search-typeahead
[github/Chocobozzz/PeerTube.git] / client / src / app / header / search-typeahead.component.ts
1 import {
2 Component,
3 OnInit,
4 OnDestroy,
5 QueryList,
6 ViewChild,
7 ElementRef
8 } from '@angular/core'
9 import { Router, Params, ActivatedRoute } from '@angular/router'
10 import { AuthService, ServerService } from '@app/core'
11 import { first, tap } from 'rxjs/operators'
12 import { ListKeyManager } from '@angular/cdk/a11y'
13 import { UP_ARROW, DOWN_ARROW, ENTER } from '@angular/cdk/keycodes'
14 import { SuggestionComponent, Result } from './suggestion.component'
15 import { of } from 'rxjs'
16 import { ServerConfig } from '@shared/models'
17
18 @Component({
19 selector: 'my-search-typeahead',
20 templateUrl: './search-typeahead.component.html',
21 styleUrls: [ './search-typeahead.component.scss' ]
22 })
23 export class SearchTypeaheadComponent implements OnInit, OnDestroy {
24 @ViewChild('searchVideo', { static: true }) searchInput: ElementRef<HTMLInputElement>
25
26 hasChannel = false
27 inChannel = false
28 newSearch = true
29
30 search = ''
31 serverConfig: ServerConfig
32
33 inThisChannelText: string
34
35 keyboardEventsManager: ListKeyManager<SuggestionComponent>
36 results: Result[] = []
37
38 constructor (
39 private authService: AuthService,
40 private router: Router,
41 private route: ActivatedRoute,
42 private serverService: ServerService
43 ) {}
44
45 ngOnInit () {
46 const query = this.route.snapshot.queryParams
47 if (query['search']) this.search = query['search']
48
49 this.serverService.getConfig()
50 .subscribe(config => this.serverConfig = config)
51 }
52
53 ngOnDestroy () {
54 if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe()
55 }
56
57 get activeResult () {
58 return this.keyboardEventsManager?.activeItem?.result
59 }
60
61 get areInstructionsDisplayed () {
62 return !this.search
63 }
64
65 get showHelp () {
66 return this.search && this.newSearch && this.activeResult?.type === 'search-global'
67 }
68
69 get canSearchAnyURI () {
70 if (!this.serverConfig) return false
71 return this.authService.isLoggedIn()
72 ? this.serverConfig.search.remoteUri.users
73 : this.serverConfig.search.remoteUri.anonymous
74 }
75
76 onSearchChange () {
77 this.computeResults()
78 }
79
80 computeResults () {
81 this.newSearch = true
82 let results: Result[] = []
83
84 if (this.search) {
85 results = [
86 /* Channel search is still unimplemented. Uncomment when it is.
87 {
88 text: this.search,
89 type: 'search-channel'
90 },
91 */
92 {
93 text: this.search,
94 type: 'search-instance',
95 default: true
96 },
97 /* Global search is still unimplemented. Uncomment when it is.
98 {
99 text: this.search,
100 type: 'search-global'
101 },
102 */
103 ...results
104 ]
105 }
106
107 this.results = results.filter(
108 (result: Result) => {
109 // if we're not in a channel or one of its videos/playlits, show all channel-related results
110 if (!(this.hasChannel || this.inChannel)) return !result.type.includes('channel')
111 // if we're in a channel, show all channel-related results except for the channel redirection itself
112 if (this.inChannel) return result.type !== 'channel'
113 // all other result types are kept
114 return true
115 }
116 )
117 }
118
119 setEventItems (event: { items: QueryList<SuggestionComponent>, index?: number }) {
120 event.items.forEach(e => {
121 if (this.keyboardEventsManager.activeItem && this.keyboardEventsManager.activeItem === e) {
122 this.keyboardEventsManager.activeItem.active = true
123 } else {
124 e.active = false
125 }
126 })
127 }
128
129 initKeyboardEventsManager (event: { items: QueryList<SuggestionComponent>, index?: number }) {
130 if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe()
131
132 this.keyboardEventsManager = new ListKeyManager(event.items)
133
134 if (event.index !== undefined) {
135 this.keyboardEventsManager.setActiveItem(event.index)
136 } else {
137 this.keyboardEventsManager.setFirstItemActive()
138 }
139
140 this.keyboardEventsManager.change.subscribe(
141 _ => this.setEventItems(event)
142 )
143 }
144
145 handleKeyUp (event: KeyboardEvent) {
146 event.stopImmediatePropagation()
147 if (!this.keyboardEventsManager) return
148
149 switch (event.key) {
150 case "ArrowDown":
151 case "ArrowUp":
152 this.keyboardEventsManager.onKeydown(event)
153 break
154 case "Enter":
155 this.newSearch = false
156 this.doSearch()
157 break
158 }
159 }
160
161 doSearch () {
162 const queryParams: Params = {}
163
164 if (window.location.pathname === '/search' && this.route.snapshot.queryParams) {
165 Object.assign(queryParams, this.route.snapshot.queryParams)
166 }
167
168 Object.assign(queryParams, { search: this.search })
169
170 const o = this.authService.isLoggedIn()
171 ? this.loadUserLanguagesIfNeeded(queryParams)
172 : of(true)
173
174 o.subscribe(() => this.router.navigate([ '/search' ], { queryParams }))
175 }
176
177 private loadUserLanguagesIfNeeded (queryParams: any) {
178 if (queryParams && queryParams.languageOneOf) return of(queryParams)
179
180 return this.authService.userInformationLoaded
181 .pipe(
182 first(),
183 tap(() => Object.assign(queryParams, { languageOneOf: this.authService.getUser().videoLanguages }))
184 )
185 }
186 }