diff options
author | Chocobozzz <me@florianbigard.com> | 2020-05-29 16:16:24 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-06-10 14:02:41 +0200 |
commit | 5fb2e2888ce032c638e4b75d07458642f0833e52 (patch) | |
tree | 8830d873569316889b8134027e9a43b198cca38f /client/src/app/shared | |
parent | 62e7be634bc189f942ae51cb4b080079ab503ff0 (diff) | |
download | PeerTube-5fb2e2888ce032c638e4b75d07458642f0833e52.tar.gz PeerTube-5fb2e2888ce032c638e4b75d07458642f0833e52.tar.zst PeerTube-5fb2e2888ce032c638e4b75d07458642f0833e52.zip |
First implem global search
Diffstat (limited to 'client/src/app/shared')
7 files changed, 69 insertions, 28 deletions
diff --git a/client/src/app/shared/actor/actor.model.ts b/client/src/app/shared/actor/actor.model.ts index 0e5060f67..a78303a2f 100644 --- a/client/src/app/shared/actor/actor.model.ts +++ b/client/src/app/shared/actor/actor.model.ts | |||
@@ -15,10 +15,14 @@ export abstract class Actor implements ActorServer { | |||
15 | 15 | ||
16 | avatarUrl: string | 16 | avatarUrl: string |
17 | 17 | ||
18 | static GET_ACTOR_AVATAR_URL (actor: { avatar?: { path: string } }) { | 18 | static GET_ACTOR_AVATAR_URL (actor: { avatar?: Avatar }) { |
19 | const absoluteAPIUrl = getAbsoluteAPIUrl() | 19 | if (actor?.avatar?.url) return actor.avatar.url |
20 | |||
21 | if (actor && actor.avatar) { | ||
22 | const absoluteAPIUrl = getAbsoluteAPIUrl() | ||
20 | 23 | ||
21 | if (actor && actor.avatar) return absoluteAPIUrl + actor.avatar.path | 24 | return absoluteAPIUrl + actor.avatar.path |
25 | } | ||
22 | 26 | ||
23 | return this.GET_DEFAULT_AVATAR_URL() | 27 | return this.GET_DEFAULT_AVATAR_URL() |
24 | } | 28 | } |
diff --git a/client/src/app/shared/angular/highlight.pipe.ts b/client/src/app/shared/angular/highlight.pipe.ts index fb6042280..50ee5c1bd 100644 --- a/client/src/app/shared/angular/highlight.pipe.ts +++ b/client/src/app/shared/angular/highlight.pipe.ts | |||
@@ -11,19 +11,17 @@ export class HighlightPipe implements PipeTransform { | |||
11 | /* use this for global search */ | 11 | /* use this for global search */ |
12 | static MULTI_MATCH = 'Multi-Match' | 12 | static MULTI_MATCH = 'Multi-Match' |
13 | 13 | ||
14 | // tslint:disable-next-line:no-empty | ||
15 | constructor () {} | ||
16 | |||
17 | transform ( | 14 | transform ( |
18 | contentString: string = null, | 15 | contentString: string = null, |
19 | stringToHighlight: string = null, | 16 | stringToHighlight: string = null, |
20 | option = 'Single-And-StartsWith-Match', | 17 | option = 'Single-And-StartsWith-Match', |
21 | caseSensitive = false, | 18 | caseSensitive = false, |
22 | highlightStyleName = 'search-highlight' | 19 | highlightStyleName = 'search-highlight' |
23 | ): SafeHtml { | 20 | ): SafeHtml { |
24 | if (stringToHighlight && contentString && option) { | 21 | if (stringToHighlight && contentString && option) { |
25 | let regex: any = '' | 22 | let regex: any = '' |
26 | const caseFlag: string = !caseSensitive ? 'i' : '' | 23 | const caseFlag: string = !caseSensitive ? 'i' : '' |
24 | |||
27 | switch (option) { | 25 | switch (option) { |
28 | case 'Single-Match': { | 26 | case 'Single-Match': { |
29 | regex = new RegExp(stringToHighlight, caseFlag) | 27 | regex = new RegExp(stringToHighlight, caseFlag) |
@@ -42,10 +40,12 @@ export class HighlightPipe implements PipeTransform { | |||
42 | regex = new RegExp(stringToHighlight, 'gi') | 40 | regex = new RegExp(stringToHighlight, 'gi') |
43 | } | 41 | } |
44 | } | 42 | } |
43 | |||
45 | const replaced = contentString.replace( | 44 | const replaced = contentString.replace( |
46 | regex, | 45 | regex, |
47 | (match) => `<span class="${highlightStyleName}">${match}</span>` | 46 | (match) => `<span class="${highlightStyleName}">${match}</span>` |
48 | ) | 47 | ) |
48 | |||
49 | return replaced | 49 | return replaced |
50 | } else { | 50 | } else { |
51 | return contentString | 51 | return contentString |
diff --git a/client/src/app/shared/forms/form-validators/custom-config-validators.service.ts b/client/src/app/shared/forms/form-validators/custom-config-validators.service.ts index abcbca817..fdb19e06a 100644 --- a/client/src/app/shared/forms/form-validators/custom-config-validators.service.ts +++ b/client/src/app/shared/forms/form-validators/custom-config-validators.service.ts | |||
@@ -14,6 +14,7 @@ export class CustomConfigValidatorsService { | |||
14 | readonly ADMIN_EMAIL: BuildFormValidator | 14 | readonly ADMIN_EMAIL: BuildFormValidator |
15 | readonly TRANSCODING_THREADS: BuildFormValidator | 15 | readonly TRANSCODING_THREADS: BuildFormValidator |
16 | readonly INDEX_URL: BuildFormValidator | 16 | readonly INDEX_URL: BuildFormValidator |
17 | readonly SEARCH_INDEX_URL: BuildFormValidator | ||
17 | 18 | ||
18 | constructor (private i18n: I18n) { | 19 | constructor (private i18n: I18n) { |
19 | this.INSTANCE_NAME = { | 20 | this.INSTANCE_NAME = { |
@@ -86,5 +87,12 @@ export class CustomConfigValidatorsService { | |||
86 | 'pattern': this.i18n('Index URL should be a URL') | 87 | 'pattern': this.i18n('Index URL should be a URL') |
87 | } | 88 | } |
88 | } | 89 | } |
90 | |||
91 | this.SEARCH_INDEX_URL = { | ||
92 | VALIDATORS: [ Validators.pattern(/^https?:\/\//) ], | ||
93 | MESSAGES: { | ||
94 | 'pattern': this.i18n('Search index URL should be a URL') | ||
95 | } | ||
96 | } | ||
89 | } | 97 | } |
90 | } | 98 | } |
diff --git a/client/src/app/shared/users/user-notification.model.ts b/client/src/app/shared/users/user-notification.model.ts index ba29cb462..7b8368d87 100644 --- a/client/src/app/shared/users/user-notification.model.ts +++ b/client/src/app/shared/users/user-notification.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { ActorInfo, FollowState, UserNotification as UserNotificationServer, UserNotificationType, VideoInfo } from '../../../../../shared' | 1 | import { ActorInfo, FollowState, UserNotification as UserNotificationServer, UserNotificationType, VideoInfo, Avatar } from '../../../../../shared' |
2 | import { Actor } from '@app/shared/actor/actor.model' | 2 | import { Actor } from '@app/shared/actor/actor.model' |
3 | 3 | ||
4 | export class UserNotification implements UserNotificationServer { | 4 | export class UserNotification implements UserNotificationServer { |
@@ -178,7 +178,7 @@ export class UserNotification implements UserNotificationServer { | |||
178 | return videoImport.targetUrl || videoImport.magnetUri || videoImport.torrentName | 178 | return videoImport.targetUrl || videoImport.magnetUri || videoImport.torrentName |
179 | } | 179 | } |
180 | 180 | ||
181 | private setAvatarUrl (actor: { avatarUrl?: string, avatar?: { path: string } }) { | 181 | private setAvatarUrl (actor: { avatarUrl?: string, avatar?: Avatar }) { |
182 | actor.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(actor) | 182 | actor.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(actor) |
183 | } | 183 | } |
184 | } | 184 | } |
diff --git a/client/src/app/shared/video/video-miniature.component.html b/client/src/app/shared/video/video-miniature.component.html index d354a2930..3e23cf18c 100644 --- a/client/src/app/shared/video/video-miniature.component.html +++ b/client/src/app/shared/video/video-miniature.component.html | |||
@@ -1,6 +1,6 @@ | |||
1 | <div class="video-miniature" [ngClass]="{ 'display-as-row': displayAsRow, 'fit-width': fitWidth }" (mouseenter)="loadActions()"> | 1 | <div class="video-miniature" [ngClass]="{ 'display-as-row': displayAsRow, 'fit-width': fitWidth }" (mouseenter)="loadActions()"> |
2 | <my-video-thumbnail | 2 | <my-video-thumbnail |
3 | [video]="video" [nsfw]="isVideoBlur" | 3 | [video]="video" [nsfw]="isVideoBlur" [routerLink]="videoLink" |
4 | [displayWatchLaterPlaylist]="isWatchLaterPlaylistDisplayed()" [inWatchLaterPlaylist]="inWatchLaterPlaylist" (watchLaterClick)="onWatchLaterClick($event)" | 4 | [displayWatchLaterPlaylist]="isWatchLaterPlaylistDisplayed()" [inWatchLaterPlaylist]="inWatchLaterPlaylist" (watchLaterClick)="onWatchLaterClick($event)" |
5 | > | 5 | > |
6 | <ng-container ngProjectAs="label-warning" *ngIf="displayOptions.privacyLabel && isUnlistedVideo()" i18n>Unlisted</ng-container> | 6 | <ng-container ngProjectAs="label-warning" *ngIf="displayOptions.privacyLabel && isUnlistedVideo()" i18n>Unlisted</ng-container> |
@@ -12,7 +12,7 @@ | |||
12 | <a | 12 | <a |
13 | tabindex="-1" | 13 | tabindex="-1" |
14 | class="video-miniature-name" | 14 | class="video-miniature-name" |
15 | [routerLink]="[ '/videos/watch', video.uuid ]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur }" | 15 | [routerLink]="videoLink" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur }" |
16 | >{{ video.name }}</a> | 16 | >{{ video.name }}</a> |
17 | 17 | ||
18 | <div class="d-inline-flex"> | 18 | <div class="d-inline-flex"> |
diff --git a/client/src/app/shared/video/video-miniature.component.ts b/client/src/app/shared/video/video-miniature.component.ts index a1d4f0e81..aa1726ca7 100644 --- a/client/src/app/shared/video/video-miniature.component.ts +++ b/client/src/app/shared/video/video-miniature.component.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { switchMap } from 'rxjs/operators' | ||
1 | import { | 2 | import { |
2 | ChangeDetectionStrategy, | 3 | ChangeDetectionStrategy, |
3 | ChangeDetectorRef, | 4 | ChangeDetectorRef, |
@@ -9,15 +10,14 @@ import { | |||
9 | OnInit, | 10 | OnInit, |
10 | Output | 11 | Output |
11 | } from '@angular/core' | 12 | } from '@angular/core' |
12 | import { User } from '../users' | ||
13 | import { Video } from './video.model' | ||
14 | import { AuthService, ServerService } from '@app/core' | 13 | import { AuthService, ServerService } from '@app/core' |
15 | import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '../../../../../shared' | ||
16 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
17 | import { VideoActionsDisplayType } from '@app/shared/video/video-actions-dropdown.component' | ||
18 | import { ScreenService } from '@app/shared/misc/screen.service' | 14 | import { ScreenService } from '@app/shared/misc/screen.service' |
19 | import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' | 15 | import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' |
20 | import { switchMap } from 'rxjs/operators' | 16 | import { VideoActionsDisplayType } from '@app/shared/video/video-actions-dropdown.component' |
17 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
18 | import { ServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '../../../../../shared' | ||
19 | import { User } from '../users' | ||
20 | import { Video } from './video.model' | ||
21 | 21 | ||
22 | export type OwnerDisplayType = 'account' | 'videoChannel' | 'auto' | 22 | export type OwnerDisplayType = 'account' | 'videoChannel' | 'auto' |
23 | export type MiniatureDisplayOptions = { | 23 | export type MiniatureDisplayOptions = { |
@@ -57,6 +57,8 @@ export class VideoMiniatureComponent implements OnInit { | |||
57 | @Input() displayVideoActions = true | 57 | @Input() displayVideoActions = true |
58 | @Input() fitWidth = false | 58 | @Input() fitWidth = false |
59 | 59 | ||
60 | @Input() useLazyLoadUrl = false | ||
61 | |||
60 | @Output() videoBlacklisted = new EventEmitter() | 62 | @Output() videoBlacklisted = new EventEmitter() |
61 | @Output() videoUnblacklisted = new EventEmitter() | 63 | @Output() videoUnblacklisted = new EventEmitter() |
62 | @Output() videoRemoved = new EventEmitter() | 64 | @Output() videoRemoved = new EventEmitter() |
@@ -82,6 +84,8 @@ export class VideoMiniatureComponent implements OnInit { | |||
82 | playlistElementId?: number | 84 | playlistElementId?: number |
83 | } | 85 | } |
84 | 86 | ||
87 | videoLink: any[] = [] | ||
88 | |||
85 | private ownerDisplayTypeChosen: 'account' | 'videoChannel' | 89 | private ownerDisplayTypeChosen: 'account' | 'videoChannel' |
86 | 90 | ||
87 | constructor ( | 91 | constructor ( |
@@ -103,7 +107,10 @@ export class VideoMiniatureComponent implements OnInit { | |||
103 | ngOnInit () { | 107 | ngOnInit () { |
104 | this.serverConfig = this.serverService.getTmpConfig() | 108 | this.serverConfig = this.serverService.getTmpConfig() |
105 | this.serverService.getConfig() | 109 | this.serverService.getConfig() |
106 | .subscribe(config => this.serverConfig = config) | 110 | .subscribe(config => { |
111 | this.serverConfig = config | ||
112 | this.buildVideoLink() | ||
113 | }) | ||
107 | 114 | ||
108 | this.setUpBy() | 115 | this.setUpBy() |
109 | 116 | ||
@@ -113,6 +120,21 @@ export class VideoMiniatureComponent implements OnInit { | |||
113 | } | 120 | } |
114 | } | 121 | } |
115 | 122 | ||
123 | buildVideoLink () { | ||
124 | if (this.useLazyLoadUrl && this.video.url) { | ||
125 | const remoteUriConfig = this.serverConfig.search.remoteUri | ||
126 | |||
127 | // Redirect on the external instance if not allowed to fetch remote data | ||
128 | const externalRedirect = (!this.authService.isLoggedIn() && !remoteUriConfig.anonymous) || !remoteUriConfig.users | ||
129 | const fromPath = window.location.pathname + window.location.search | ||
130 | |||
131 | this.videoLink = [ '/search/lazy-load-video', { url: this.video.url, externalRedirect, fromPath } ] | ||
132 | return | ||
133 | } | ||
134 | |||
135 | this.videoLink = [ '/videos/watch', this.video.uuid ] | ||
136 | } | ||
137 | |||
116 | displayOwnerAccount () { | 138 | displayOwnerAccount () { |
117 | return this.ownerDisplayTypeChosen === 'account' | 139 | return this.ownerDisplayTypeChosen === 'account' |
118 | } | 140 | } |
@@ -203,7 +225,7 @@ export class VideoMiniatureComponent implements OnInit { | |||
203 | } | 225 | } |
204 | 226 | ||
205 | isWatchLaterPlaylistDisplayed () { | 227 | isWatchLaterPlaylistDisplayed () { |
206 | return this.isUserLoggedIn() && this.inWatchLaterPlaylist !== undefined | 228 | return this.displayVideoActions && this.isUserLoggedIn() && this.inWatchLaterPlaylist !== undefined |
207 | } | 229 | } |
208 | 230 | ||
209 | private setUpBy () { | 231 | private setUpBy () { |
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index 546518cca..97759f9c1 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts | |||
@@ -33,10 +33,15 @@ export class Video implements VideoServerModel { | |||
33 | serverHost: string | 33 | serverHost: string |
34 | thumbnailPath: string | 34 | thumbnailPath: string |
35 | thumbnailUrl: string | 35 | thumbnailUrl: string |
36 | |||
36 | previewPath: string | 37 | previewPath: string |
37 | previewUrl: string | 38 | previewUrl: string |
39 | |||
38 | embedPath: string | 40 | embedPath: string |
39 | embedUrl: string | 41 | embedUrl: string |
42 | |||
43 | url?: string | ||
44 | |||
40 | views: number | 45 | views: number |
41 | likes: number | 46 | likes: number |
42 | dislikes: number | 47 | dislikes: number |
@@ -100,13 +105,15 @@ export class Video implements VideoServerModel { | |||
100 | this.name = hash.name | 105 | this.name = hash.name |
101 | 106 | ||
102 | this.thumbnailPath = hash.thumbnailPath | 107 | this.thumbnailPath = hash.thumbnailPath |
103 | this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath | 108 | this.thumbnailUrl = hash.thumbnailUrl || (absoluteAPIUrl + hash.thumbnailPath) |
104 | 109 | ||
105 | this.previewPath = hash.previewPath | 110 | this.previewPath = hash.previewPath |
106 | this.previewUrl = absoluteAPIUrl + hash.previewPath | 111 | this.previewUrl = hash.previewUrl || (absoluteAPIUrl + hash.previewPath) |
107 | 112 | ||
108 | this.embedPath = hash.embedPath | 113 | this.embedPath = hash.embedPath |
109 | this.embedUrl = absoluteAPIUrl + hash.embedPath | 114 | this.embedUrl = hash.embedUrl || (absoluteAPIUrl + hash.embedPath) |
115 | |||
116 | this.url = hash.url | ||
110 | 117 | ||
111 | this.views = hash.views | 118 | this.views = hash.views |
112 | this.likes = hash.likes | 119 | this.likes = hash.likes |