]>
Commit | Line | Data |
---|---|---|
6af662a5 RK |
1 | import { |
2 | Component, | |
6af662a5 RK |
3 | OnInit, |
4 | OnDestroy, | |
9b8a7aa8 RK |
5 | QueryList, |
6 | ViewChild, | |
7 | ElementRef | |
6af662a5 | 8 | } from '@angular/core' |
9b8a7aa8 | 9 | import { Router, Params, ActivatedRoute } from '@angular/router' |
9677fca7 | 10 | import { AuthService, ServerService } from '@app/core' |
9b8a7aa8 | 11 | import { first, tap } from 'rxjs/operators' |
6af662a5 | 12 | import { ListKeyManager } from '@angular/cdk/a11y' |
9677fca7 | 13 | import { UP_ARROW, DOWN_ARROW, ENTER } from '@angular/cdk/keycodes' |
52cc0d54 RK |
14 | import { SuggestionComponent, Result } from './suggestion.component' |
15 | import { of } from 'rxjs' | |
9677fca7 | 16 | import { ServerConfig } from '@shared/models' |
f409f0c3 RK |
17 | |
18 | @Component({ | |
19 | selector: 'my-search-typeahead', | |
20 | templateUrl: './search-typeahead.component.html', | |
21 | styleUrls: [ './search-typeahead.component.scss' ] | |
22 | }) | |
8a979d73 | 23 | export class SearchTypeaheadComponent implements OnInit, OnDestroy { |
9b8a7aa8 | 24 | @ViewChild('searchVideo', { static: true }) searchInput: ElementRef<HTMLInputElement> |
f409f0c3 RK |
25 | |
26 | hasChannel = false | |
27 | inChannel = false | |
6af662a5 | 28 | newSearch = true |
f409f0c3 | 29 | |
9b8a7aa8 | 30 | search = '' |
9677fca7 | 31 | serverConfig: ServerConfig |
f409f0c3 | 32 | |
f409f0c3 RK |
33 | inThisChannelText: string |
34 | ||
6af662a5 | 35 | keyboardEventsManager: ListKeyManager<SuggestionComponent> |
8a979d73 | 36 | results: Result[] = [] |
f409f0c3 RK |
37 | |
38 | constructor ( | |
39 | private authService: AuthService, | |
40 | private router: Router, | |
52cc0d54 | 41 | private route: ActivatedRoute, |
9b8a7aa8 RK |
42 | private serverService: ServerService |
43 | ) {} | |
f409f0c3 RK |
44 | |
45 | ngOnInit () { | |
8a979d73 RK |
46 | const query = this.route.snapshot.queryParams |
47 | if (query['search']) this.search = query['search'] | |
48 | ||
9677fca7 RK |
49 | this.serverService.getConfig() |
50 | .subscribe(config => this.serverConfig = config) | |
f409f0c3 RK |
51 | } |
52 | ||
6af662a5 RK |
53 | ngOnDestroy () { |
54 | if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe() | |
55 | } | |
56 | ||
6af662a5 | 57 | get activeResult () { |
8a979d73 | 58 | return this.keyboardEventsManager?.activeItem?.result |
6af662a5 RK |
59 | } |
60 | ||
8a979d73 | 61 | get areInstructionsDisplayed () { |
9b8a7aa8 RK |
62 | return !this.search |
63 | } | |
64 | ||
6af662a5 | 65 | get showHelp () { |
8a979d73 | 66 | return this.search && this.newSearch && this.activeResult?.type === 'search-global' |
6af662a5 RK |
67 | } |
68 | ||
8a979d73 | 69 | get canSearchAnyURI () { |
9b8a7aa8 RK |
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() | |
9677fca7 RK |
78 | } |
79 | ||
f409f0c3 | 80 | computeResults () { |
6af662a5 | 81 | this.newSearch = true |
52cc0d54 | 82 | let results: Result[] = [] |
f409f0c3 | 83 | |
9b8a7aa8 | 84 | if (this.search) { |
f409f0c3 | 85 | results = [ |
52cc0d54 | 86 | /* Channel search is still unimplemented. Uncomment when it is. |
f409f0c3 | 87 | { |
9b8a7aa8 | 88 | text: this.search, |
f409f0c3 RK |
89 | type: 'search-channel' |
90 | }, | |
52cc0d54 | 91 | */ |
6af662a5 | 92 | { |
9b8a7aa8 | 93 | text: this.search, |
52cc0d54 RK |
94 | type: 'search-instance', |
95 | default: true | |
6af662a5 | 96 | }, |
52cc0d54 | 97 | /* Global search is still unimplemented. Uncomment when it is. |
f409f0c3 | 98 | { |
9b8a7aa8 | 99 | text: this.search, |
f409f0c3 RK |
100 | type: 'search-global' |
101 | }, | |
52cc0d54 | 102 | */ |
f409f0c3 RK |
103 | ...results |
104 | ] | |
105 | } | |
106 | ||
107 | this.results = results.filter( | |
52cc0d54 | 108 | (result: Result) => { |
f409f0c3 RK |
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 | |
9b8a7aa8 RK |
112 | if (this.inChannel) return result.type !== 'channel' |
113 | // all other result types are kept | |
f409f0c3 RK |
114 | return true |
115 | } | |
116 | ) | |
117 | } | |
118 | ||
52cc0d54 RK |
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 | ||
6af662a5 RK |
129 | initKeyboardEventsManager (event: { items: QueryList<SuggestionComponent>, index?: number }) { |
130 | if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe() | |
8a979d73 | 131 | |
6af662a5 | 132 | this.keyboardEventsManager = new ListKeyManager(event.items) |
8a979d73 | 133 | |
6af662a5 RK |
134 | if (event.index !== undefined) { |
135 | this.keyboardEventsManager.setActiveItem(event.index) | |
52cc0d54 RK |
136 | } else { |
137 | this.keyboardEventsManager.setFirstItemActive() | |
6af662a5 | 138 | } |
8a979d73 | 139 | |
6af662a5 | 140 | this.keyboardEventsManager.change.subscribe( |
52cc0d54 | 141 | _ => this.setEventItems(event) |
6af662a5 RK |
142 | ) |
143 | } | |
144 | ||
8a979d73 | 145 | handleKeyUp (event: KeyboardEvent) { |
f409f0c3 | 146 | event.stopImmediatePropagation() |
8a979d73 RK |
147 | if (!this.keyboardEventsManager) return |
148 | ||
149 | switch (event.key) { | |
150 | case "ArrowDown": | |
151 | case "ArrowUp": | |
f409f0c3 | 152 | this.keyboardEventsManager.onKeydown(event) |
8a979d73 RK |
153 | break |
154 | case "Enter": | |
6af662a5 | 155 | this.newSearch = false |
52cc0d54 | 156 | this.doSearch() |
8a979d73 | 157 | break |
f409f0c3 RK |
158 | } |
159 | } | |
52cc0d54 RK |
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 | ||
9b8a7aa8 | 168 | Object.assign(queryParams, { search: this.search }) |
52cc0d54 RK |
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 | } | |
f409f0c3 | 186 | } |