From 1942f11d5ee6926ad93dc1b79fae18325ba5de18 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 23 Jun 2020 14:49:20 +0200 Subject: Lazy load all routes --- .../shared/shared-search/advanced-search.model.ts | 160 +++++++++++++++++++++ client/src/app/shared/shared-search/index.ts | 3 + .../src/app/shared/shared-search/search.service.ts | 88 ++++++++++++ .../shared/shared-search/shared-search.module.ts | 20 +++ 4 files changed, 271 insertions(+) create mode 100644 client/src/app/shared/shared-search/advanced-search.model.ts create mode 100644 client/src/app/shared/shared-search/index.ts create mode 100644 client/src/app/shared/shared-search/search.service.ts create mode 100644 client/src/app/shared/shared-search/shared-search.module.ts (limited to 'client/src/app/shared') diff --git a/client/src/app/shared/shared-search/advanced-search.model.ts b/client/src/app/shared/shared-search/advanced-search.model.ts new file mode 100644 index 000000000..516854a8c --- /dev/null +++ b/client/src/app/shared/shared-search/advanced-search.model.ts @@ -0,0 +1,160 @@ +import { NSFWQuery, SearchTargetType } from '@shared/models' + +export class AdvancedSearch { + startDate: string // ISO 8601 + endDate: string // ISO 8601 + + originallyPublishedStartDate: string // ISO 8601 + originallyPublishedEndDate: string // ISO 8601 + + nsfw: NSFWQuery + + categoryOneOf: string + + licenceOneOf: string + + languageOneOf: string + + tagsOneOf: string + tagsAllOf: string + + durationMin: number // seconds + durationMax: number // seconds + + sort: string + + searchTarget: SearchTargetType + + // Filters we don't want to count, because they are mandatory + private silentFilters = new Set([ 'sort', 'searchTarget' ]) + + constructor (options?: { + startDate?: string + endDate?: string + originallyPublishedStartDate?: string + originallyPublishedEndDate?: string + nsfw?: NSFWQuery + categoryOneOf?: string + licenceOneOf?: string + languageOneOf?: string + tagsOneOf?: string + tagsAllOf?: string + durationMin?: string + durationMax?: string + sort?: string + searchTarget?: SearchTargetType + }) { + if (!options) return + + this.startDate = options.startDate || undefined + this.endDate = options.endDate || undefined + this.originallyPublishedStartDate = options.originallyPublishedStartDate || undefined + this.originallyPublishedEndDate = options.originallyPublishedEndDate || undefined + + this.nsfw = options.nsfw || undefined + this.categoryOneOf = options.categoryOneOf || undefined + this.licenceOneOf = options.licenceOneOf || undefined + this.languageOneOf = options.languageOneOf || undefined + this.tagsOneOf = options.tagsOneOf || undefined + this.tagsAllOf = options.tagsAllOf || undefined + this.durationMin = parseInt(options.durationMin, 10) + this.durationMax = parseInt(options.durationMax, 10) + + this.searchTarget = options.searchTarget || undefined + + if (isNaN(this.durationMin)) this.durationMin = undefined + if (isNaN(this.durationMax)) this.durationMax = undefined + + this.sort = options.sort || '-match' + } + + containsValues () { + const exceptions = new Set([ 'sort', 'searchTarget' ]) + + const obj = this.toUrlObject() + for (const k of Object.keys(obj)) { + if (this.silentFilters.has(k)) continue + + if (obj[k] !== undefined && obj[k] !== '') return true + } + + return false + } + + reset () { + this.startDate = undefined + this.endDate = undefined + this.originallyPublishedStartDate = undefined + this.originallyPublishedEndDate = undefined + this.nsfw = undefined + this.categoryOneOf = undefined + this.licenceOneOf = undefined + this.languageOneOf = undefined + this.tagsOneOf = undefined + this.tagsAllOf = undefined + this.durationMin = undefined + this.durationMax = undefined + + this.sort = '-match' + } + + toUrlObject () { + return { + startDate: this.startDate, + endDate: this.endDate, + originallyPublishedStartDate: this.originallyPublishedStartDate, + originallyPublishedEndDate: this.originallyPublishedEndDate, + nsfw: this.nsfw, + categoryOneOf: this.categoryOneOf, + licenceOneOf: this.licenceOneOf, + languageOneOf: this.languageOneOf, + tagsOneOf: this.tagsOneOf, + tagsAllOf: this.tagsAllOf, + durationMin: this.durationMin, + durationMax: this.durationMax, + sort: this.sort, + searchTarget: this.searchTarget + } + } + + toAPIObject () { + return { + startDate: this.startDate, + endDate: this.endDate, + originallyPublishedStartDate: this.originallyPublishedStartDate, + originallyPublishedEndDate: this.originallyPublishedEndDate, + nsfw: this.nsfw, + categoryOneOf: this.intoArray(this.categoryOneOf), + licenceOneOf: this.intoArray(this.licenceOneOf), + languageOneOf: this.intoArray(this.languageOneOf), + tagsOneOf: this.intoArray(this.tagsOneOf), + tagsAllOf: this.intoArray(this.tagsAllOf), + durationMin: this.durationMin, + durationMax: this.durationMax, + sort: this.sort, + searchTarget: this.searchTarget + } + } + + size () { + let acc = 0 + + const obj = this.toUrlObject() + for (const k of Object.keys(obj)) { + if (this.silentFilters.has(k)) continue + + if (obj[k] !== undefined && obj[k] !== '') acc++ + } + + return acc + } + + private intoArray (value: any) { + if (!value) return undefined + if (Array.isArray(value)) return value + + if (typeof value === 'string') return value.split(',') + + return [ value ] + } +} diff --git a/client/src/app/shared/shared-search/index.ts b/client/src/app/shared/shared-search/index.ts new file mode 100644 index 000000000..f687f6767 --- /dev/null +++ b/client/src/app/shared/shared-search/index.ts @@ -0,0 +1,3 @@ +export * from './advanced-search.model' +export * from './search.service' +export * from './shared-search.module' diff --git a/client/src/app/shared/shared-search/search.service.ts b/client/src/app/shared/shared-search/search.service.ts new file mode 100644 index 000000000..96b954c99 --- /dev/null +++ b/client/src/app/shared/shared-search/search.service.ts @@ -0,0 +1,88 @@ +import { Observable } from 'rxjs' +import { catchError, map, switchMap } from 'rxjs/operators' +import { HttpClient, HttpParams } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core' +import { peertubeLocalStorage } from '@app/helpers' +import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' +import { ResultList, SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models' +import { environment } from '../../../environments/environment' +import { AdvancedSearch } from './advanced-search.model' + +@Injectable() +export class SearchService { + static BASE_SEARCH_URL = environment.apiUrl + '/api/v1/search/' + + constructor ( + private authHttp: HttpClient, + private restExtractor: RestExtractor, + private restService: RestService, + private videoService: VideoService + ) { + // Add ability to override search endpoint if the user updated this local storage key + const searchUrl = peertubeLocalStorage.getItem('search-url') + if (searchUrl) SearchService.BASE_SEARCH_URL = searchUrl + } + + searchVideos (parameters: { + search: string, + componentPagination?: ComponentPaginationLight, + advancedSearch?: AdvancedSearch + }): Observable> { + const { search, componentPagination, advancedSearch } = parameters + + const url = SearchService.BASE_SEARCH_URL + 'videos' + let pagination: RestPagination + + if (componentPagination) { + pagination = this.restService.componentPaginationToRestPagination(componentPagination) + } + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination) + + if (search) params = params.append('search', search) + + if (advancedSearch) { + const advancedSearchObject = advancedSearch.toAPIObject() + params = this.restService.addObjectParams(params, advancedSearchObject) + } + + return this.authHttp + .get>(url, { params }) + .pipe( + switchMap(res => this.videoService.extractVideos(res)), + catchError(err => this.restExtractor.handleError(err)) + ) + } + + searchVideoChannels (parameters: { + search: string, + searchTarget?: SearchTargetType, + componentPagination?: ComponentPaginationLight + }): Observable> { + const { search, componentPagination, searchTarget } = parameters + + const url = SearchService.BASE_SEARCH_URL + 'video-channels' + + let pagination: RestPagination + if (componentPagination) { + pagination = this.restService.componentPaginationToRestPagination(componentPagination) + } + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination) + params = params.append('search', search) + + if (searchTarget) { + params = params.append('searchTarget', searchTarget as string) + } + + return this.authHttp + .get>(url, { params }) + .pipe( + map(res => VideoChannelService.extractVideoChannels(res)), + catchError(err => this.restExtractor.handleError(err)) + ) + } +} diff --git a/client/src/app/shared/shared-search/shared-search.module.ts b/client/src/app/shared/shared-search/shared-search.module.ts new file mode 100644 index 000000000..134300d88 --- /dev/null +++ b/client/src/app/shared/shared-search/shared-search.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core' +import { SharedMainModule } from '../shared-main' +import { SearchService } from './search.service' + +@NgModule({ + imports: [ + SharedMainModule + ], + + declarations: [ + ], + + exports: [ + ], + + providers: [ + SearchService + ] +}) +export class SharedSearchModule { } -- cgit v1.2.3