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