-import {
- Component,
- ViewChild,
- ElementRef,
- AfterViewInit,
- OnInit,
- OnDestroy,
- QueryList
-} from '@angular/core'
-import { Router, NavigationEnd } from '@angular/router'
-import { AuthService } from '@app/core'
-import { I18n } from '@ngx-translate/i18n-polyfill'
-import { filter } from 'rxjs/operators'
+import { Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChild } from '@angular/core'
+import { ActivatedRoute, Params, Router } from '@angular/router'
+import { AuthService, ServerService } from '@app/core'
+import { first, tap } from 'rxjs/operators'
import { ListKeyManager } from '@angular/cdk/a11y'
-import { UP_ARROW, DOWN_ARROW, ENTER, TAB } from '@angular/cdk/keycodes'
-import { SuggestionComponent } from './suggestion.component'
+import { Result, SuggestionComponent } from './suggestion.component'
+import { of } from 'rxjs'
+import { ServerConfig } from '@shared/models'
@Component({
selector: 'my-search-typeahead',
templateUrl: './search-typeahead.component.html',
styleUrls: [ './search-typeahead.component.scss' ]
})
-export class SearchTypeaheadComponent implements OnInit, OnDestroy, AfterViewInit {
- @ViewChild('contentWrapper', { static: true }) contentWrapper: ElementRef
+export class SearchTypeaheadComponent implements OnInit, OnDestroy {
+ @ViewChild('searchVideo', { static: true }) searchInput: ElementRef<HTMLInputElement>
hasChannel = false
inChannel = false
newSearch = true
- searchInput: HTMLInputElement
- URIPolicy: 'only-followed' | 'any' = 'any'
+ search = ''
+ serverConfig: ServerConfig
- URIPolicyText: string
- inAllText: string
inThisChannelText: string
- globalSearchIndex = 'https://index.joinpeertube.org'
keyboardEventsManager: ListKeyManager<SuggestionComponent>
- results: any[] = []
+ results: Result[] = []
constructor (
private authService: AuthService,
private router: Router,
- private i18n: I18n
- ) {
- this.URIPolicyText = this.i18n('Determines whether you can resolve any distant content, or if your instance only allows doing so for instances it follows.')
- this.inAllText = this.i18n('In all PeerTube')
- this.inThisChannelText = this.i18n('In this channel')
- }
+ private route: ActivatedRoute,
+ private serverService: ServerService
+ ) {}
ngOnInit () {
- this.router.events
- .pipe(filter(event => event instanceof NavigationEnd))
- .subscribe((event: NavigationEnd) => {
- this.hasChannel = event.url.startsWith('/videos/watch')
- this.inChannel = event.url.startsWith('/video-channels')
- this.computeResults()
- })
+ this.route.queryParams
+ .pipe(first(params => params.search !== undefined && params.search !== null))
+ .subscribe(params => this.search = params.search)
+ this.serverService.getConfig()
+ .subscribe(config => this.serverConfig = config)
}
ngOnDestroy () {
if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe()
}
- ngAfterViewInit () {
- this.searchInput = this.contentWrapper.nativeElement.childNodes[0]
- this.searchInput.addEventListener('input', this.computeResults.bind(this))
- this.searchInput.addEventListener('keyup', this.handleKeyUp.bind(this))
+ get activeResult () {
+ return this.keyboardEventsManager?.activeItem?.result
}
- get hasSearch () {
- return !!this.searchInput && !!this.searchInput.value
+ get areInstructionsDisplayed () {
+ return !this.search
}
- get activeResult () {
- return this.keyboardEventsManager && this.keyboardEventsManager.activeItem && this.keyboardEventsManager.activeItem.result
+ get showHelp () {
+ return this.search && this.newSearch && this.activeResult?.type === 'search-global'
}
- get showHelp () {
- return this.hasSearch && this.newSearch && this.activeResult && this.activeResult.type === 'search-global' || false
+ get canSearchAnyURI () {
+ if (!this.serverConfig) return false
+ return this.authService.isLoggedIn()
+ ? this.serverConfig.search.remoteUri.users
+ : this.serverConfig.search.remoteUri.anonymous
+ }
+
+ onSearchChange () {
+ this.computeResults()
}
computeResults () {
this.newSearch = true
- let results = [
- {
- text: 'MaƮtre poney',
- type: 'channel'
- }
- ]
+ let results: Result[] = []
- if (this.hasSearch) {
+ if (this.search) {
results = [
+ /* Channel search is still unimplemented. Uncomment when it is.
{
- text: this.searchInput.value,
+ text: this.search,
type: 'search-channel'
},
+ */
{
- text: this.searchInput.value,
- type: 'search-instance'
+ text: this.search,
+ type: 'search-instance',
+ default: true
},
+ /* Global search is still unimplemented. Uncomment when it is.
{
- text: this.searchInput.value,
+ text: this.search,
type: 'search-global'
},
+ */
...results
]
}
this.results = results.filter(
- result => {
+ (result: Result) => {
// if we're not in a channel or one of its videos/playlits, show all channel-related results
if (!(this.hasChannel || this.inChannel)) return !result.type.includes('channel')
// if we're in a channel, show all channel-related results except for the channel redirection itself
- if (this.inChannel) return !(result.type === 'channel')
+ if (this.inChannel) return result.type !== 'channel'
+ // all other result types are kept
return true
}
)
}
+ setEventItems (event: { items: QueryList<SuggestionComponent>, index?: number }) {
+ event.items.forEach(e => {
+ if (this.keyboardEventsManager.activeItem && this.keyboardEventsManager.activeItem === e) {
+ this.keyboardEventsManager.activeItem.active = true
+ } else {
+ e.active = false
+ }
+ })
+ }
+
initKeyboardEventsManager (event: { items: QueryList<SuggestionComponent>, index?: number }) {
if (this.keyboardEventsManager) this.keyboardEventsManager.change.unsubscribe()
+
this.keyboardEventsManager = new ListKeyManager(event.items)
+
if (event.index !== undefined) {
this.keyboardEventsManager.setActiveItem(event.index)
- event.items.forEach(e => e.active = false)
- this.keyboardEventsManager.activeItem.active = true
+ } else {
+ this.keyboardEventsManager.setFirstItemActive()
}
+
this.keyboardEventsManager.change.subscribe(
- val => {
- event.items.forEach(e => e.active = false)
- this.keyboardEventsManager.activeItem.active = true
- }
+ _ => this.setEventItems(event)
)
}
- isUserLoggedIn () {
- return this.authService.isLoggedIn()
- }
-
- handleKeyUp (event: KeyboardEvent, indexSelected?: number) {
+ handleKeyUp (event: KeyboardEvent) {
event.stopImmediatePropagation()
- if (this.keyboardEventsManager) {
- if (event.keyCode === TAB) {
- this.keyboardEventsManager.setNextItemActive()
- return false
- } else if (event.keyCode === DOWN_ARROW || event.keyCode === UP_ARROW) {
+ if (!this.keyboardEventsManager) return
+
+ switch (event.key) {
+ case 'ArrowDown':
+ case 'ArrowUp':
this.keyboardEventsManager.onKeydown(event)
- return false
- } else if (event.keyCode === ENTER) {
+ break
+ case 'Enter':
this.newSearch = false
- // this.router.navigate(this.keyboardEventsManager.activeItem.result)
- return false
- }
+ this.doSearch()
+ break
+ }
+ }
+
+ doSearch () {
+ const queryParams: Params = {}
+
+ if (window.location.pathname === '/search' && this.route.snapshot.queryParams) {
+ Object.assign(queryParams, this.route.snapshot.queryParams)
}
+
+ Object.assign(queryParams, { search: this.search })
+
+ const o = this.authService.isLoggedIn()
+ ? this.loadUserLanguagesIfNeeded(queryParams)
+ : of(true)
+
+ o.subscribe(() => this.router.navigate([ '/search' ], { queryParams }))
+ }
+
+ private loadUserLanguagesIfNeeded (queryParams: any) {
+ if (queryParams && queryParams.languageOneOf) return of(queryParams)
+
+ return this.authService.userInformationLoaded
+ .pipe(
+ first(),
+ tap(() => Object.assign(queryParams, { languageOneOf: this.authService.getUser().videoLanguages }))
+ )
}
}