First implem global search
[oweals/peertube.git] / client / src / app / shared / video / video-miniature.component.ts
1 import { switchMap } from 'rxjs/operators'
2 import {
3   ChangeDetectionStrategy,
4   ChangeDetectorRef,
5   Component,
6   EventEmitter,
7   Inject,
8   Input,
9   LOCALE_ID,
10   OnInit,
11   Output
12 } from '@angular/core'
13 import { AuthService, ServerService } from '@app/core'
14 import { ScreenService } from '@app/shared/misc/screen.service'
15 import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
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
22 export type OwnerDisplayType = 'account' | 'videoChannel' | 'auto'
23 export type MiniatureDisplayOptions = {
24   date?: boolean
25   views?: boolean
26   by?: boolean
27   avatar?: boolean
28   privacyLabel?: boolean
29   privacyText?: boolean
30   state?: boolean
31   blacklistInfo?: boolean
32   nsfw?: boolean
33 }
34
35 @Component({
36   selector: 'my-video-miniature',
37   styleUrls: [ './video-miniature.component.scss' ],
38   templateUrl: './video-miniature.component.html',
39   changeDetection: ChangeDetectionStrategy.OnPush
40 })
41 export class VideoMiniatureComponent implements OnInit {
42   @Input() user: User
43   @Input() video: Video
44
45   @Input() ownerDisplayType: OwnerDisplayType = 'account'
46   @Input() displayOptions: MiniatureDisplayOptions = {
47     date: true,
48     views: true,
49     by: true,
50     avatar: false,
51     privacyLabel: false,
52     privacyText: false,
53     state: false,
54     blacklistInfo: false
55   }
56   @Input() displayAsRow = false
57   @Input() displayVideoActions = true
58   @Input() fitWidth = false
59
60   @Input() useLazyLoadUrl = false
61
62   @Output() videoBlacklisted = new EventEmitter()
63   @Output() videoUnblacklisted = new EventEmitter()
64   @Output() videoRemoved = new EventEmitter()
65
66   videoActionsDisplayOptions: VideoActionsDisplayType = {
67     playlist: true,
68     download: false,
69     update: true,
70     blacklist: true,
71     delete: true,
72     report: true,
73     duplicate: true
74   }
75   showActions = false
76   serverConfig: ServerConfig
77
78   addToWatchLaterText: string
79   addedToWatchLaterText: string
80   inWatchLaterPlaylist: boolean
81
82   watchLaterPlaylist: {
83     id: number
84     playlistElementId?: number
85   }
86
87   videoLink: any[] = []
88
89   private ownerDisplayTypeChosen: 'account' | 'videoChannel'
90
91   constructor (
92     private screenService: ScreenService,
93     private serverService: ServerService,
94     private i18n: I18n,
95     private authService: AuthService,
96     private videoPlaylistService: VideoPlaylistService,
97     private cd: ChangeDetectorRef,
98     @Inject(LOCALE_ID) private localeId: string
99   ) {
100
101   }
102
103   get isVideoBlur () {
104     return this.video.isVideoNSFWForUser(this.user, this.serverConfig)
105   }
106
107   ngOnInit () {
108     this.serverConfig = this.serverService.getTmpConfig()
109     this.serverService.getConfig()
110         .subscribe(config => {
111           this.serverConfig = config
112           this.buildVideoLink()
113         })
114
115     this.setUpBy()
116
117     // We rely on mouseenter to lazy load actions
118     if (this.screenService.isInTouchScreen()) {
119       this.loadActions()
120     }
121   }
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
138   displayOwnerAccount () {
139     return this.ownerDisplayTypeChosen === 'account'
140   }
141
142   displayOwnerVideoChannel () {
143     return this.ownerDisplayTypeChosen === 'videoChannel'
144   }
145
146   isUnlistedVideo () {
147     return this.video.privacy.id === VideoPrivacy.UNLISTED
148   }
149
150   isPrivateVideo () {
151     return this.video.privacy.id === VideoPrivacy.PRIVATE
152   }
153
154   getStateLabel (video: Video) {
155     if (!video.state) return ''
156
157     if (video.privacy.id !== VideoPrivacy.PRIVATE && video.state.id === VideoState.PUBLISHED) {
158       return this.i18n('Published')
159     }
160
161     if (video.scheduledUpdate) {
162       const updateAt = new Date(video.scheduledUpdate.updateAt.toString()).toLocaleString(this.localeId)
163       return this.i18n('Publication scheduled on ') + updateAt
164     }
165
166     if (video.state.id === VideoState.TO_TRANSCODE && video.waitTranscoding === true) {
167       return this.i18n('Waiting transcoding')
168     }
169
170     if (video.state.id === VideoState.TO_TRANSCODE) {
171       return this.i18n('To transcode')
172     }
173
174     if (video.state.id === VideoState.TO_IMPORT) {
175       return this.i18n('To import')
176     }
177
178     return ''
179   }
180
181   loadActions () {
182     if (this.displayVideoActions) this.showActions = true
183
184     this.loadWatchLater()
185   }
186
187   onVideoBlacklisted () {
188     this.videoBlacklisted.emit()
189   }
190
191   onVideoUnblacklisted () {
192     this.videoUnblacklisted.emit()
193   }
194
195   onVideoRemoved () {
196     this.videoRemoved.emit()
197   }
198
199   isUserLoggedIn () {
200     return this.authService.isLoggedIn()
201   }
202
203   onWatchLaterClick (currentState: boolean) {
204     if (currentState === true) this.removeFromWatchLater()
205     else this.addToWatchLater()
206
207     this.inWatchLaterPlaylist = !currentState
208   }
209
210   addToWatchLater () {
211     const body = { videoId: this.video.id }
212
213     this.videoPlaylistService.addVideoInPlaylist(this.watchLaterPlaylist.id, body).subscribe(
214       res => {
215         this.watchLaterPlaylist.playlistElementId = res.videoPlaylistElement.id
216       }
217     )
218   }
219
220   removeFromWatchLater () {
221     this.videoPlaylistService.removeVideoFromPlaylist(this.watchLaterPlaylist.id, this.watchLaterPlaylist.playlistElementId, this.video.id)
222         .subscribe(
223           _ => { /* empty */ }
224         )
225   }
226
227   isWatchLaterPlaylistDisplayed () {
228     return this.displayVideoActions && this.isUserLoggedIn() && this.inWatchLaterPlaylist !== undefined
229   }
230
231   private setUpBy () {
232     if (this.ownerDisplayType === 'account' || this.ownerDisplayType === 'videoChannel') {
233       this.ownerDisplayTypeChosen = this.ownerDisplayType
234       return
235     }
236
237     // If the video channel name an UUID (not really displayable, we changed this behaviour in v1.0.0-beta.12)
238     // -> Use the account name
239     if (
240       this.video.channel.name === `${this.video.account.name}_channel` ||
241       this.video.channel.name.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
242     ) {
243       this.ownerDisplayTypeChosen = 'account'
244     } else {
245       this.ownerDisplayTypeChosen = 'videoChannel'
246     }
247   }
248
249   private loadWatchLater () {
250     if (!this.isUserLoggedIn() || this.inWatchLaterPlaylist !== undefined) return
251
252     this.authService.userInformationLoaded
253         .pipe(switchMap(() => this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id)))
254         .subscribe(existResult => {
255           const watchLaterPlaylist = this.authService.getUser().specialPlaylists.find(p => p.type === VideoPlaylistType.WATCH_LATER)
256           const existsInWatchLater = existResult.find(r => r.playlistId === watchLaterPlaylist.id)
257           this.inWatchLaterPlaylist = false
258
259           this.watchLaterPlaylist = {
260             id: watchLaterPlaylist.id
261           }
262
263           if (existsInWatchLater) {
264             this.inWatchLaterPlaylist = true
265             this.watchLaterPlaylist.playlistElementId = existsInWatchLater.playlistElementId
266           }
267
268           this.cd.markForCheck()
269         })
270
271     this.videoPlaylistService.runPlaylistCheck(this.video.id)
272   }
273 }