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 --- client/src/app/search/advanced-search.model.ts | 160 ------------ .../src/app/search/channel-lazy-load.resolver.ts | 43 ---- client/src/app/search/highlight.pipe.ts | 54 ----- client/src/app/search/index.ts | 3 - .../src/app/search/search-filters.component.html | 193 --------------- .../src/app/search/search-filters.component.scss | 69 ------ client/src/app/search/search-filters.component.ts | 269 --------------------- client/src/app/search/search-routing.module.ts | 41 ---- client/src/app/search/search.component.html | 63 ----- client/src/app/search/search.component.scss | 191 --------------- client/src/app/search/search.component.ts | 260 -------------------- client/src/app/search/search.module.ts | 43 ---- client/src/app/search/search.service.ts | 89 ------- client/src/app/search/video-lazy-load.resolver.ts | 43 ---- 14 files changed, 1521 deletions(-) delete mode 100644 client/src/app/search/advanced-search.model.ts delete mode 100644 client/src/app/search/channel-lazy-load.resolver.ts delete mode 100644 client/src/app/search/highlight.pipe.ts delete mode 100644 client/src/app/search/index.ts delete mode 100644 client/src/app/search/search-filters.component.html delete mode 100644 client/src/app/search/search-filters.component.scss delete mode 100644 client/src/app/search/search-filters.component.ts delete mode 100644 client/src/app/search/search-routing.module.ts delete mode 100644 client/src/app/search/search.component.html delete mode 100644 client/src/app/search/search.component.scss delete mode 100644 client/src/app/search/search.component.ts delete mode 100644 client/src/app/search/search.module.ts delete mode 100644 client/src/app/search/search.service.ts delete mode 100644 client/src/app/search/video-lazy-load.resolver.ts (limited to 'client/src/app/search') diff --git a/client/src/app/search/advanced-search.model.ts b/client/src/app/search/advanced-search.model.ts deleted file mode 100644 index 516854a8c..000000000 --- a/client/src/app/search/advanced-search.model.ts +++ /dev/null @@ -1,160 +0,0 @@ -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/search/channel-lazy-load.resolver.ts b/client/src/app/search/channel-lazy-load.resolver.ts deleted file mode 100644 index 5b6961e98..000000000 --- a/client/src/app/search/channel-lazy-load.resolver.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { map } from 'rxjs/operators' -import { Injectable } from '@angular/core' -import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router' -import { SearchService } from './search.service' - -@Injectable() -export class ChannelLazyLoadResolver implements Resolve { - constructor ( - private router: Router, - private searchService: SearchService - ) { } - - resolve (route: ActivatedRouteSnapshot) { - const url = route.params.url - const externalRedirect = route.params.externalRedirect - const fromPath = route.params.fromPath - - if (!url) { - console.error('Could not find url param.', { params: route.params }) - return this.router.navigateByUrl('/404') - } - - if (externalRedirect === 'true') { - window.open(url) - this.router.navigateByUrl(fromPath) - return - } - - return this.searchService.searchVideoChannels({ search: url }) - .pipe( - map(result => { - if (result.data.length !== 1) { - console.error('Cannot find result for this URL') - return this.router.navigateByUrl('/404') - } - - const channel = result.data[0] - - return this.router.navigateByUrl('/video-channels/' + channel.nameWithHost) - }) - ) - } -} diff --git a/client/src/app/search/highlight.pipe.ts b/client/src/app/search/highlight.pipe.ts deleted file mode 100644 index 50ee5c1bd..000000000 --- a/client/src/app/search/highlight.pipe.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { PipeTransform, Pipe } from '@angular/core' -import { SafeHtml } from '@angular/platform-browser' - -// Thanks https://gist.github.com/adamrecsko/0f28f474eca63e0279455476cc11eca7#gistcomment-2917369 -@Pipe({ name: 'highlight' }) -export class HighlightPipe implements PipeTransform { - /* use this for single match search */ - static SINGLE_MATCH = 'Single-Match' - /* use this for single match search with a restriction that target should start with search string */ - static SINGLE_AND_STARTS_WITH_MATCH = 'Single-And-StartsWith-Match' - /* use this for global search */ - static MULTI_MATCH = 'Multi-Match' - - transform ( - contentString: string = null, - stringToHighlight: string = null, - option = 'Single-And-StartsWith-Match', - caseSensitive = false, - highlightStyleName = 'search-highlight' - ): SafeHtml { - if (stringToHighlight && contentString && option) { - let regex: any = '' - const caseFlag: string = !caseSensitive ? 'i' : '' - - switch (option) { - case 'Single-Match': { - regex = new RegExp(stringToHighlight, caseFlag) - break - } - case 'Single-And-StartsWith-Match': { - regex = new RegExp('^' + stringToHighlight, caseFlag) - break - } - case 'Multi-Match': { - regex = new RegExp(stringToHighlight, 'g' + caseFlag) - break - } - default: { - // default will be a global case-insensitive match - regex = new RegExp(stringToHighlight, 'gi') - } - } - - const replaced = contentString.replace( - regex, - (match) => `${match}` - ) - - return replaced - } else { - return contentString - } - } -} diff --git a/client/src/app/search/index.ts b/client/src/app/search/index.ts deleted file mode 100644 index 40f4e021f..000000000 --- a/client/src/app/search/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './search-routing.module' -export * from './search.component' -export * from './search.module' diff --git a/client/src/app/search/search-filters.component.html b/client/src/app/search/search-filters.component.html deleted file mode 100644 index e20aef8fb..000000000 --- a/client/src/app/search/search-filters.component.html +++ /dev/null @@ -1,193 +0,0 @@ -
- -
-
-
-
- - -
- -
- - -
-
- -
-
- - -
- -
- - -
- -
- - -
-
- -
-
- - -
- -
- - -
-
- -
-
- - -
- -
-
- -
-
- -
-
-
- -
- -
-
-
- - -
- -
- - -
-
- -
- - -
- -
-
- -
- - -
- -
-
- -
- - -
- -
-
-
- -
-
- - - -
- -
- - - -
- -
-
- -
- -
- - -
- -
- - -
-
-
-
- -
- - - -
-
diff --git a/client/src/app/search/search-filters.component.scss b/client/src/app/search/search-filters.component.scss deleted file mode 100644 index a88a1c0b0..000000000 --- a/client/src/app/search/search-filters.component.scss +++ /dev/null @@ -1,69 +0,0 @@ -@import '_variables'; -@import '_mixins'; - -form { - margin-top: 40px; -} - -.radio-label { - font-size: 15px; - font-weight: $font-bold; -} - -.peertube-radio-container { - @include peertube-radio-container; - - display: inline-block; - margin-right: 30px; -} - -.peertube-select-container { - @include peertube-select-container(auto); - - margin-bottom: 1rem; -} - -.form-group { - margin-bottom: 25px; -} - -input[type=text] { - @include peertube-input-text(100%); - display: block; -} - -input[type=submit] { - @include peertube-button-link; - @include orange-button; -} - -.submit-button { - text-align: right; -} - -.reset-button { - @include peertube-button; - - font-weight: $font-semibold; - display: inline-block; - padding: 0 10px 0 10px; - white-space: nowrap; - background: transparent; - - margin-right: 1rem; -} - -.reset-button-small { - font-size: 80%; - height: unset; - line-height: unset; - margin: unset; - margin-bottom: 0.5rem; -} - -.label-container { - display: flex; - white-space: nowrap; -} - -@include ng2-tags; diff --git a/client/src/app/search/search-filters.component.ts b/client/src/app/search/search-filters.component.ts deleted file mode 100644 index 14a5d0484..000000000 --- a/client/src/app/search/search-filters.component.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' -import { ValidatorFn } from '@angular/forms' -import { ServerService } from '@app/core' -import { AdvancedSearch } from '@app/search/advanced-search.model' -import { VideoValidatorsService } from '@app/shared/shared-forms' -import { I18n } from '@ngx-translate/i18n-polyfill' -import { ServerConfig, VideoConstant } from '@shared/models' - -@Component({ - selector: 'my-search-filters', - styleUrls: [ './search-filters.component.scss' ], - templateUrl: './search-filters.component.html' -}) -export class SearchFiltersComponent implements OnInit { - @Input() advancedSearch: AdvancedSearch = new AdvancedSearch() - - @Output() filtered = new EventEmitter() - - videoCategories: VideoConstant[] = [] - videoLicences: VideoConstant[] = [] - videoLanguages: VideoConstant[] = [] - - tagValidators: ValidatorFn[] - tagValidatorsMessages: { [ name: string ]: string } - - publishedDateRanges: { id: string, label: string }[] = [] - sorts: { id: string, label: string }[] = [] - durationRanges: { id: string, label: string }[] = [] - - publishedDateRange: string - durationRange: string - - originallyPublishedStartYear: string - originallyPublishedEndYear: string - - private serverConfig: ServerConfig - - constructor ( - private i18n: I18n, - private videoValidatorsService: VideoValidatorsService, - private serverService: ServerService - ) { - this.tagValidators = this.videoValidatorsService.VIDEO_TAGS.VALIDATORS - this.tagValidatorsMessages = this.videoValidatorsService.VIDEO_TAGS.MESSAGES - this.publishedDateRanges = [ - { - id: 'any_published_date', - label: this.i18n('Any') - }, - { - id: 'today', - label: this.i18n('Today') - }, - { - id: 'last_7days', - label: this.i18n('Last 7 days') - }, - { - id: 'last_30days', - label: this.i18n('Last 30 days') - }, - { - id: 'last_365days', - label: this.i18n('Last 365 days') - } - ] - - this.durationRanges = [ - { - id: 'any_duration', - label: this.i18n('Any') - }, - { - id: 'short', - label: this.i18n('Short (< 4 min)') - }, - { - id: 'medium', - label: this.i18n('Medium (4-10 min)') - }, - { - id: 'long', - label: this.i18n('Long (> 10 min)') - } - ] - - this.sorts = [ - { - id: '-match', - label: this.i18n('Relevance') - }, - { - id: '-publishedAt', - label: this.i18n('Publish date') - }, - { - id: '-views', - label: this.i18n('Views') - } - ] - } - - ngOnInit () { - this.serverConfig = this.serverService.getTmpConfig() - this.serverService.getConfig() - .subscribe(config => this.serverConfig = config) - - this.serverService.getVideoCategories().subscribe(categories => this.videoCategories = categories) - this.serverService.getVideoLicences().subscribe(licences => this.videoLicences = licences) - this.serverService.getVideoLanguages().subscribe(languages => this.videoLanguages = languages) - - this.loadFromDurationRange() - this.loadFromPublishedRange() - this.loadOriginallyPublishedAtYears() - } - - inputUpdated () { - this.updateModelFromDurationRange() - this.updateModelFromPublishedRange() - this.updateModelFromOriginallyPublishedAtYears() - } - - formUpdated () { - this.inputUpdated() - this.filtered.emit(this.advancedSearch) - } - - reset () { - this.advancedSearch.reset() - this.durationRange = undefined - this.publishedDateRange = undefined - this.originallyPublishedStartYear = undefined - this.originallyPublishedEndYear = undefined - this.inputUpdated() - } - - resetField (fieldName: string, value?: any) { - this.advancedSearch[fieldName] = value - } - - resetLocalField (fieldName: string, value?: any) { - this[fieldName] = value - this.inputUpdated() - } - - resetOriginalPublicationYears () { - this.originallyPublishedStartYear = this.originallyPublishedEndYear = undefined - } - - isSearchTargetEnabled () { - return this.serverConfig.search.searchIndex.enabled && this.serverConfig.search.searchIndex.disableLocalSearch !== true - } - - private loadOriginallyPublishedAtYears () { - this.originallyPublishedStartYear = this.advancedSearch.originallyPublishedStartDate - ? new Date(this.advancedSearch.originallyPublishedStartDate).getFullYear().toString() - : null - - this.originallyPublishedEndYear = this.advancedSearch.originallyPublishedEndDate - ? new Date(this.advancedSearch.originallyPublishedEndDate).getFullYear().toString() - : null - } - - private loadFromDurationRange () { - if (this.advancedSearch.durationMin || this.advancedSearch.durationMax) { - const fourMinutes = 60 * 4 - const tenMinutes = 60 * 10 - - if (this.advancedSearch.durationMin === fourMinutes && this.advancedSearch.durationMax === tenMinutes) { - this.durationRange = 'medium' - } else if (this.advancedSearch.durationMax === fourMinutes) { - this.durationRange = 'short' - } else if (this.advancedSearch.durationMin === tenMinutes) { - this.durationRange = 'long' - } - } - } - - private loadFromPublishedRange () { - if (this.advancedSearch.startDate) { - const date = new Date(this.advancedSearch.startDate) - const now = new Date() - - const diff = Math.abs(date.getTime() - now.getTime()) - - const dayMS = 1000 * 3600 * 24 - const numberOfDays = diff / dayMS - - if (numberOfDays >= 365) this.publishedDateRange = 'last_365days' - else if (numberOfDays >= 30) this.publishedDateRange = 'last_30days' - else if (numberOfDays >= 7) this.publishedDateRange = 'last_7days' - else if (numberOfDays >= 0) this.publishedDateRange = 'today' - } - } - - private updateModelFromOriginallyPublishedAtYears () { - const baseDate = new Date() - baseDate.setHours(0, 0, 0, 0) - baseDate.setMonth(0, 1) - - if (this.originallyPublishedStartYear) { - const year = parseInt(this.originallyPublishedStartYear, 10) - const start = new Date(baseDate) - start.setFullYear(year) - - this.advancedSearch.originallyPublishedStartDate = start.toISOString() - } else { - this.advancedSearch.originallyPublishedStartDate = null - } - - if (this.originallyPublishedEndYear) { - const year = parseInt(this.originallyPublishedEndYear, 10) - const end = new Date(baseDate) - end.setFullYear(year) - - this.advancedSearch.originallyPublishedEndDate = end.toISOString() - } else { - this.advancedSearch.originallyPublishedEndDate = null - } - } - - private updateModelFromDurationRange () { - if (!this.durationRange) return - - const fourMinutes = 60 * 4 - const tenMinutes = 60 * 10 - - switch (this.durationRange) { - case 'short': - this.advancedSearch.durationMin = undefined - this.advancedSearch.durationMax = fourMinutes - break - - case 'medium': - this.advancedSearch.durationMin = fourMinutes - this.advancedSearch.durationMax = tenMinutes - break - - case 'long': - this.advancedSearch.durationMin = tenMinutes - this.advancedSearch.durationMax = undefined - break - } - } - - private updateModelFromPublishedRange () { - if (!this.publishedDateRange) return - - // today - const date = new Date() - date.setHours(0, 0, 0, 0) - - switch (this.publishedDateRange) { - case 'last_7days': - date.setDate(date.getDate() - 7) - break - - case 'last_30days': - date.setDate(date.getDate() - 30) - break - - case 'last_365days': - date.setDate(date.getDate() - 365) - break - } - - this.advancedSearch.startDate = date.toISOString() - } -} diff --git a/client/src/app/search/search-routing.module.ts b/client/src/app/search/search-routing.module.ts deleted file mode 100644 index 9da900e9a..000000000 --- a/client/src/app/search/search-routing.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { NgModule } from '@angular/core' -import { RouterModule, Routes } from '@angular/router' -import { SearchComponent } from '@app/search/search.component' -import { MetaGuard } from '@ngx-meta/core' -import { VideoLazyLoadResolver } from './video-lazy-load.resolver' -import { ChannelLazyLoadResolver } from './channel-lazy-load.resolver' - -const searchRoutes: Routes = [ - { - path: 'search', - component: SearchComponent, - canActivate: [ MetaGuard ], - data: { - meta: { - title: 'Search' - } - } - }, - { - path: 'search/lazy-load-video', - component: SearchComponent, - canActivate: [ MetaGuard ], - resolve: { - data: VideoLazyLoadResolver - } - }, - { - path: 'search/lazy-load-channel', - component: SearchComponent, - canActivate: [ MetaGuard ], - resolve: { - data: ChannelLazyLoadResolver - } - } -] - -@NgModule({ - imports: [ RouterModule.forChild(searchRoutes) ], - exports: [ RouterModule ] -}) -export class SearchRoutingModule {} diff --git a/client/src/app/search/search.component.html b/client/src/app/search/search.component.html deleted file mode 100644 index 9bff024ad..000000000 --- a/client/src/app/search/search.component.html +++ /dev/null @@ -1,63 +0,0 @@ -
-
-
-
- {{ pagination.totalItems | myNumberFormatter }} {pagination.totalItems, plural, =1 {result} other {results}} - - on this instance - on the vidiverse - - - for {{ currentSearch }} - -
- -
- - - Filters - {{ numberOfFilters() }} - -
-
- -
- -
-
- -
- No results found -
- - -
- - Avatar - - -
- -
{{ result.displayName }}
-
{{ result.nameWithHost }}
-
- -
{{ result.followersCount }} subscribers
-
- - -
- -
- -
-
- -
diff --git a/client/src/app/search/search.component.scss b/client/src/app/search/search.component.scss deleted file mode 100644 index 6e59adb60..000000000 --- a/client/src/app/search/search.component.scss +++ /dev/null @@ -1,191 +0,0 @@ -@import '_variables'; -@import '_mixins'; - -.search-result { - padding: 40px; - - .results-header { - font-size: 16px; - padding-bottom: 20px; - margin-bottom: 30px; - border-bottom: 1px solid #DADADA; - - .first-line { - display: flex; - flex-direction: row; - - .results-counter { - flex-grow: 1; - - .search-value { - font-weight: $font-semibold; - } - } - - .results-filter-button { - cursor: pointer; - - .icon.icon-filter { - @include icon(20px); - - position: relative; - top: -1px; - margin-right: 5px; - background-image: url('../../assets/images/search/filter.svg'); - } - } - } - } - - .entry { - display: flex; - min-height: 130px; - padding-bottom: 20px; - margin-bottom: 20px; - - &.video-channel { - img { - $image-size: 130px; - $margin-size: ($video-thumbnail-width - $image-size) / 2; // So we have the same width than the video miniature - - @include avatar($image-size); - - margin: 0 ($margin-size + 10) 0 $margin-size; - } - - .video-channel-info { - flex-grow: 1; - width: fit-content; - - .video-channel-names { - @include disable-default-a-behaviour; - - display: flex; - align-items: baseline; - color: pvar(--mainForegroundColor); - width: fit-content; - - .video-channel-display-name { - font-weight: $font-semibold; - font-size: 18px; - } - - .video-channel-name { - font-size: 14px; - color: $grey-actor-name; - margin-left: 5px; - } - } - } - } - } -} - -@media screen and (min-width: $small-view) and (max-width: breakpoint(xl)) { - .video-channel-info .video-channel-names { - flex-direction: column !important; - - .video-channel-name { - @include ellipsis; // Ellipsis and max-width on channel-name to not break screen - - max-width: 250px; - margin-left: 0 !important; - } - } - - :host-context(.main-col:not(.expanded)) { - // Override the min-width: 500px to not break screen - ::ng-deep .video-miniature-information { - min-width: 300px !important; - } - } -} - -@media screen and (min-width: $small-view) and (max-width: breakpoint(lg)) { - :host-context(.main-col:not(.expanded)) { - .video-channel-info .video-channel-names { - .video-channel-name { - max-width: 160px; - } - } - - // Override the min-width: 500px to not break screen - ::ng-deep .video-miniature-information { - min-width: $video-thumbnail-width !important; - } - } - - :host-context(.expanded) { - // Override the min-width: 500px to not break screen - ::ng-deep .video-miniature-information { - min-width: 300px !important; - } - } -} - -@media screen and (max-width: $small-view) { - .search-result { - .entry.video-channel, - .entry.video { - flex-direction: column; - height: auto; - justify-content: center; - align-items: center; - text-align: center; - - img { - margin: 0; - } - - img { - margin: 0; - } - - .video-channel-info .video-channel-names { - align-items: center; - flex-direction: column !important; - - .video-channel-name { - margin-left: 0 !important; - } - } - - my-subscribe-button { - margin-top: 5px; - } - } - } -} - -@media screen and (max-width: $mobile-view) { - .search-result { - padding: 20px 10px; - - .results-header { - font-size: 15px !important; - } - - .entry { - &.video { - .video-info-name, - .video-info-account { - margin: auto; - } - - my-video-thumbnail { - margin-right: 0 !important; - - ::ng-deep .video-thumbnail { - width: 100%; - height: auto; - - img { - width: 100%; - height: auto; - } - } - } - } - } - } -} diff --git a/client/src/app/search/search.component.ts b/client/src/app/search/search.component.ts deleted file mode 100644 index 83b06e0ce..000000000 --- a/client/src/app/search/search.component.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { forkJoin, of, Subscription } from 'rxjs' -import { Component, OnDestroy, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' -import { AuthService, ComponentPagination, HooksService, Notifier, ServerService, User, UserService } from '@app/core' -import { immutableAssign } from '@app/helpers' -import { Video, VideoChannel } from '@app/shared/shared-main' -import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature' -import { MetaService } from '@ngx-meta/core' -import { I18n } from '@ngx-translate/i18n-polyfill' -import { SearchTargetType, ServerConfig } from '@shared/models' -import { AdvancedSearch } from './advanced-search.model' -import { SearchService } from './search.service' - -@Component({ - selector: 'my-search', - styleUrls: [ './search.component.scss' ], - templateUrl: './search.component.html' -}) -export class SearchComponent implements OnInit, OnDestroy { - results: (Video | VideoChannel)[] = [] - - pagination: ComponentPagination = { - currentPage: 1, - itemsPerPage: 10, // Only for videos, use another variable for channels - totalItems: null - } - advancedSearch: AdvancedSearch = new AdvancedSearch() - isSearchFilterCollapsed = true - currentSearch: string - - videoDisplayOptions: MiniatureDisplayOptions = { - date: true, - views: true, - by: true, - avatar: false, - privacyLabel: false, - privacyText: false, - state: false, - blacklistInfo: false - } - - errorMessage: string - serverConfig: ServerConfig - - userMiniature: User - - private subActivatedRoute: Subscription - private isInitialLoad = false // set to false to show the search filters on first arrival - private firstSearch = true - - private channelsPerPage = 2 - - private lastSearchTarget: SearchTargetType - - constructor ( - private i18n: I18n, - private route: ActivatedRoute, - private router: Router, - private metaService: MetaService, - private notifier: Notifier, - private searchService: SearchService, - private authService: AuthService, - private userService: UserService, - private hooks: HooksService, - private serverService: ServerService - ) { } - - ngOnInit () { - this.serverService.getConfig() - .subscribe(config => this.serverConfig = config) - - this.subActivatedRoute = this.route.queryParams.subscribe( - async queryParams => { - const querySearch = queryParams['search'] - const searchTarget = queryParams['searchTarget'] - - // Search updated, reset filters - if (this.currentSearch !== querySearch || searchTarget !== this.advancedSearch.searchTarget) { - this.resetPagination() - this.advancedSearch.reset() - - this.currentSearch = querySearch || undefined - this.updateTitle() - } - - this.advancedSearch = new AdvancedSearch(queryParams) - if (!this.advancedSearch.searchTarget) { - this.advancedSearch.searchTarget = await this.serverService.getDefaultSearchTarget() - } - - // Don't hide filters if we have some of them AND the user just came on the webpage - this.isSearchFilterCollapsed = this.isInitialLoad === false || !this.advancedSearch.containsValues() - this.isInitialLoad = false - - this.search() - }, - - err => this.notifier.error(err.text) - ) - - this.userService.getAnonymousOrLoggedUser() - .subscribe(user => this.userMiniature = user) - - this.hooks.runAction('action:search.init', 'search') - } - - ngOnDestroy () { - if (this.subActivatedRoute) this.subActivatedRoute.unsubscribe() - } - - isVideoChannel (d: VideoChannel | Video): d is VideoChannel { - return d instanceof VideoChannel - } - - isVideo (v: VideoChannel | Video): v is Video { - return v instanceof Video - } - - isUserLoggedIn () { - return this.authService.isLoggedIn() - } - - search () { - forkJoin([ - this.getVideosObs(), - this.getVideoChannelObs() - ]).subscribe( - ([videosResult, videoChannelsResult]) => { - this.results = this.results - .concat(videoChannelsResult.data) - .concat(videosResult.data) - - this.pagination.totalItems = videosResult.total + videoChannelsResult.total - this.lastSearchTarget = this.advancedSearch.searchTarget - - // Focus on channels if there are no enough videos - if (this.firstSearch === true && videosResult.data.length < this.pagination.itemsPerPage) { - this.resetPagination() - this.firstSearch = false - - this.channelsPerPage = 10 - this.search() - } - - this.firstSearch = false - }, - - err => { - if (this.advancedSearch.searchTarget !== 'search-index') { - this.notifier.error(err.message) - return - } - - this.notifier.error( - this.i18n('Search index is unavailable. Retrying with instance results instead.'), - this.i18n('Search error') - ) - this.advancedSearch.searchTarget = 'local' - this.search() - } - ) - } - - onNearOfBottom () { - // Last page - if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return - - this.pagination.currentPage += 1 - this.search() - } - - onFiltered () { - this.resetPagination() - - this.updateUrlFromAdvancedSearch() - } - - numberOfFilters () { - return this.advancedSearch.size() - } - - // Add VideoChannel for typings, but the template already checks "video" argument is a video - removeVideoFromArray (video: Video | VideoChannel) { - this.results = this.results.filter(r => !this.isVideo(r) || r.id !== video.id) - } - - getChannelUrl (channel: VideoChannel) { - if (this.advancedSearch.searchTarget === 'search-index' && channel.url) { - const remoteUriConfig = this.serverConfig.search.remoteUri - - // Redirect on the external instance if not allowed to fetch remote data - const externalRedirect = (!this.authService.isLoggedIn() && !remoteUriConfig.anonymous) || !remoteUriConfig.users - const fromPath = window.location.pathname + window.location.search - - return [ '/search/lazy-load-channel', { url: channel.url, externalRedirect, fromPath } ] - } - - return [ '/video-channels', channel.nameWithHost ] - } - - hideActions () { - return this.lastSearchTarget === 'search-index' - } - - private resetPagination () { - this.pagination.currentPage = 1 - this.pagination.totalItems = null - this.channelsPerPage = 2 - - this.results = [] - } - - private updateTitle () { - const suffix = this.currentSearch ? ' ' + this.currentSearch : '' - this.metaService.setTitle(this.i18n('Search') + suffix) - } - - private updateUrlFromAdvancedSearch () { - const search = this.currentSearch || undefined - - this.router.navigate([], { - relativeTo: this.route, - queryParams: Object.assign({}, this.advancedSearch.toUrlObject(), { search }) - }) - } - - private getVideosObs () { - const params = { - search: this.currentSearch, - componentPagination: this.pagination, - advancedSearch: this.advancedSearch - } - - return this.hooks.wrapObsFun( - this.searchService.searchVideos.bind(this.searchService), - params, - 'search', - 'filter:api.search.videos.list.params', - 'filter:api.search.videos.list.result' - ) - } - - private getVideoChannelObs () { - if (!this.currentSearch) return of({ data: [], total: 0 }) - - const params = { - search: this.currentSearch, - componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage }), - searchTarget: this.advancedSearch.searchTarget - } - - return this.hooks.wrapObsFun( - this.searchService.searchVideoChannels.bind(this.searchService), - params, - 'search', - 'filter:api.search.video-channels.list.params', - 'filter:api.search.video-channels.list.result' - ) - } -} diff --git a/client/src/app/search/search.module.ts b/client/src/app/search/search.module.ts deleted file mode 100644 index 65c954de8..000000000 --- a/client/src/app/search/search.module.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { TagInputModule } from 'ngx-chips' -import { NgModule } from '@angular/core' -import { SharedFormModule } from '@app/shared/shared-forms' -import { SharedMainModule } from '@app/shared/shared-main' -import { SharedUserSubscriptionModule } from '@app/shared/shared-user-subscription' -import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature' -import { ChannelLazyLoadResolver } from './channel-lazy-load.resolver' -import { HighlightPipe } from './highlight.pipe' -import { SearchFiltersComponent } from './search-filters.component' -import { SearchRoutingModule } from './search-routing.module' -import { SearchComponent } from './search.component' -import { SearchService } from './search.service' -import { VideoLazyLoadResolver } from './video-lazy-load.resolver' - -@NgModule({ - imports: [ - TagInputModule, - - SearchRoutingModule, - SharedMainModule, - SharedFormModule, - SharedUserSubscriptionModule, - SharedVideoMiniatureModule - ], - - declarations: [ - SearchComponent, - SearchFiltersComponent - ], - - exports: [ - TagInputModule, - SearchComponent - ], - - providers: [ - SearchService, - VideoLazyLoadResolver, - ChannelLazyLoadResolver, - HighlightPipe - ] -}) -export class SearchModule { } diff --git a/client/src/app/search/search.service.ts b/client/src/app/search/search.service.ts deleted file mode 100644 index 36342034f..000000000 --- a/client/src/app/search/search.service.ts +++ /dev/null @@ -1,89 +0,0 @@ -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 { AdvancedSearch } from '@app/search/advanced-search.model' -import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' -import { ResultList, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models' -import { SearchTargetType } from '@shared/models/search/search-target-query.model' -import { environment } from '../../environments/environment' - -@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/search/video-lazy-load.resolver.ts b/client/src/app/search/video-lazy-load.resolver.ts deleted file mode 100644 index 8d846d367..000000000 --- a/client/src/app/search/video-lazy-load.resolver.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { map } from 'rxjs/operators' -import { Injectable } from '@angular/core' -import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router' -import { SearchService } from './search.service' - -@Injectable() -export class VideoLazyLoadResolver implements Resolve { - constructor ( - private router: Router, - private searchService: SearchService - ) { } - - resolve (route: ActivatedRouteSnapshot) { - const url = route.params.url - const externalRedirect = route.params.externalRedirect - const fromPath = route.params.fromPath - - if (!url) { - console.error('Could not find url param.', { params: route.params }) - return this.router.navigateByUrl('/404') - } - - if (externalRedirect === 'true') { - window.open(url) - this.router.navigateByUrl(fromPath) - return - } - - return this.searchService.searchVideos({ search: url }) - .pipe( - map(result => { - if (result.data.length !== 1) { - console.error('Cannot find result for this URL') - return this.router.navigateByUrl('/404') - } - - const video = result.data[0] - - return this.router.navigateByUrl('/videos/watch/' + video.uuid) - }) - ) - } -} -- cgit v1.2.3