aboutsummaryrefslogtreecommitdiffhomepage
path: root/client
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-03-11 16:23:33 +0100
committerChocobozzz <chocobozzz@cpy.re>2019-03-18 11:17:59 +0100
commitc5a1ae500e68b759f76851552be6dd10631d34f4 (patch)
treef741daceab3b506f18f7cc14492a25425ebdf68d /client
parentf0a3988066f72a28bb44520af072f18d91d77dde (diff)
downloadPeerTube-c5a1ae500e68b759f76851552be6dd10631d34f4.tar.gz
PeerTube-c5a1ae500e68b759f76851552be6dd10631d34f4.tar.zst
PeerTube-c5a1ae500e68b759f76851552be6dd10631d34f4.zip
Playlist videos component
Diffstat (limited to 'client')
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html57
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss94
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts120
-rw-r--r--client/src/app/menu/menu.component.html2
-rw-r--r--client/src/app/menu/menu.component.scss5
-rw-r--r--client/src/app/shared/buttons/action-dropdown.component.html2
-rw-r--r--client/src/app/shared/images/global-icon.component.ts3
-rw-r--r--client/src/app/shared/video-playlist/video-add-to-playlist.component.html4
-rw-r--r--client/src/app/shared/video-playlist/video-add-to-playlist.component.scss5
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-miniature.component.scss3
-rw-r--r--client/src/app/shared/video/video.model.ts6
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html2
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.scss10
-rw-r--r--client/src/assets/images/global/more-horizontal.html (renamed from client/src/assets/images/video/more.html)0
-rw-r--r--client/src/assets/images/global/more-vertical.html11
-rw-r--r--client/src/sass/include/_miniature.scss14
-rw-r--r--client/src/sass/include/_mixins.scss12
-rw-r--r--client/src/sass/primeng-custom.scss14
18 files changed, 324 insertions, 40 deletions
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html
index 28ea7a857..e2d09a36d 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html
@@ -1,16 +1,61 @@
1<div class="no-results">No videos in this playlist.</div> 1<div i18n class="no-results" *ngIf="pagination.totalItems === 0">No videos in this playlist.</div>
2 2
3<div class="videos" myInfiniteScroller (nearOfBottom)="onNearOfBottom()"> 3<div class="videos" myInfiniteScroller (nearOfBottom)="onNearOfBottom()">
4 <div *ngFor="let video of videos" class="video"> 4 <div *ngFor="let video of videos" class="video">
5 <my-video-thumbnail [video]="video"></my-video-thumbnail> 5 <div class="position">{{ video.playlistElement.position }}</div>
6 6
7 <div class="video-info"> 7 <my-video-thumbnail [video]="video" [nsfw]="isVideoBlur(video)"></my-video-thumbnail>
8 <div class="position">{{ video.playlistElement.position }}</div>
9
10 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
11 8
9 <div class="video-info">
12 <a tabindex="-1" class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> 10 <a tabindex="-1" class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
13 <a tabindex="-1" class="video-info-account" [routerLink]="[ '/accounts', video.byAccount ]">{{ video.byAccount }}</a> 11 <a tabindex="-1" class="video-info-account" [routerLink]="[ '/accounts', video.byAccount ]">{{ video.byAccount }}</a>
12 <span tabindex="-1" class="video-info-timestamp">{{ formatTimestamp(video)}}</span>
13 </div>
14
15 <div class="more" ngbDropdown #moreDropdown="ngbDropdown" placement="bottom-right" (openChange)="onDropdownOpenChange()" autoClose="outside">
16 <my-global-icon iconName="more-vertical" ngbDropdownToggle role="button" class="icon-more"></my-global-icon>
17
18 <div ngbDropdownMenu>
19 <div class="dropdown-item" (click)="toggleDisplayTimestampsOptions($event, video)">
20 <my-global-icon iconName="edit"></my-global-icon> <ng-container i18n>Edit starts/stops at</ng-container>
21 </div>
22
23 <div class="timestamp-options" *ngIf="displayTimestampOptions">
24 <div>
25 <my-peertube-checkbox
26 inputName="startAt" [(ngModel)]="timestampOptions.startTimestampEnabled"
27 i18n-labelText labelText="Start at"
28 ></my-peertube-checkbox>
29
30 <my-timestamp-input
31 [timestamp]="timestampOptions.startTimestamp"
32 [maxTimestamp]="video.duration"
33 [disabled]="!timestampOptions.startTimestampEnabled"
34 [(ngModel)]="timestampOptions.startTimestamp"
35 ></my-timestamp-input>
36 </div>
37
38 <div>
39 <my-peertube-checkbox
40 inputName="stopAt" [(ngModel)]="timestampOptions.stopTimestampEnabled"
41 i18n-labelText labelText="Stop at"
42 ></my-peertube-checkbox>
43
44 <my-timestamp-input
45 [timestamp]="timestampOptions.stopTimestamp"
46 [maxTimestamp]="video.duration"
47 [disabled]="!timestampOptions.stopTimestampEnabled"
48 [(ngModel)]="timestampOptions.stopTimestamp"
49 ></my-timestamp-input>
50 </div>
51
52 <input type="submit" i18n-value value="Save" (click)="updateTimestamps(video)">
53 </div>
54
55 <span class="dropdown-item" (click)="removeFromPlaylist(video)">
56 <my-global-icon iconName="delete"></my-global-icon> <ng-container i18n>Delete from {{playlist?.displayName}}</ng-container>
57 </span>
58 </div>
14 </div> 59 </div>
15 </div> 60 </div>
16</div> 61</div>
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss
index 5e6774739..3be10078e 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss
@@ -1,2 +1,96 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3@import '_miniature';
4
5.videos {
6 .video {
7 display: flex;
8 align-items: center;
9 padding: 10px;
10 border-bottom: 1px solid $separator-border-color;
11
12 &:hover {
13 background-color: rgba(0, 0, 0, 0.05);
14
15 .more {
16 display: block;
17 }
18 }
19
20 .position {
21 font-weight: $font-semibold;
22 margin-right: 10px;
23 color: $grey-foreground-color;
24 }
25
26 my-video-thumbnail {
27 display: flex; // Avoids an issue with line-height that adds space below the element
28 margin-right: 10px;
29
30 /deep/ .video-thumbnail {
31 @include miniature-thumbnail(130px, 72px);
32 }
33 }
34
35 .video-info {
36 display: flex;
37 flex-direction: column;
38
39 a {
40 @include disable-default-a-behaviour;
41
42 color: var(--mainForegroundColor);
43 }
44
45 .video-info-name {
46 font-size: 18px;
47 font-weight: $font-semibold;
48 }
49
50 .video-info-account, .video-info-timestamp {
51 color: $grey-foreground-color;
52 }
53 }
54
55 .more {
56 justify-self: flex-end;
57 margin-left: auto;
58 cursor: pointer;
59 display: none;
60
61 &.show {
62 display: block;
63 }
64
65 .icon-more {
66 @include apply-svg-color($grey-foreground-color);
67
68 &::after {
69 border: none;
70 }
71 }
72
73 .dropdown-item {
74 @include dropdown-with-icon-item;
75 }
76
77 .timestamp-options {
78 padding-top: 0;
79 padding-left: 35px;
80 margin-bottom: 15px;
81
82 > div {
83 display: flex;
84 align-items: center;
85 }
86
87 input {
88 @include peertube-button;
89 @include orange-button;
90
91 margin-top: 10px;
92 }
93 }
94 }
95 }
96}
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts
index 8b70a9b1a..76aff3d4f 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts
@@ -1,5 +1,5 @@
1import { Component, OnDestroy, OnInit } from '@angular/core' 1import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier, ServerService } from '@app/core'
3import { AuthService } from '../../core/auth' 3import { AuthService } from '../../core/auth'
4import { ConfirmService } from '../../core/confirm' 4import { ConfirmService } from '../../core/confirm'
5import { ComponentPagination } from '@app/shared/rest/component-pagination.model' 5import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
@@ -7,6 +7,12 @@ import { Video } from '@app/shared/video/video.model'
7import { Subscription } from 'rxjs' 7import { Subscription } from 'rxjs'
8import { ActivatedRoute } from '@angular/router' 8import { ActivatedRoute } from '@angular/router'
9import { VideoService } from '@app/shared/video/video.service' 9import { VideoService } from '@app/shared/video/video.service'
10import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
11import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
12import { I18n } from '@ngx-translate/i18n-polyfill'
13import { secondsToTime } from '../../../assets/player/utils'
14import { VideoPlaylistElementUpdate } from '@shared/models'
15import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
10 16
11@Component({ 17@Component({
12 selector: 'my-account-video-playlist-elements', 18 selector: 'my-account-video-playlist-elements',
@@ -14,7 +20,10 @@ import { VideoService } from '@app/shared/video/video.service'
14 styleUrls: [ './my-account-video-playlist-elements.component.scss' ] 20 styleUrls: [ './my-account-video-playlist-elements.component.scss' ]
15}) 21})
16export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestroy { 22export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestroy {
23 @ViewChild('moreDropdown') moreDropdown: NgbDropdown
24
17 videos: Video[] = [] 25 videos: Video[] = []
26 playlist: VideoPlaylist
18 27
19 pagination: ComponentPagination = { 28 pagination: ComponentPagination = {
20 currentPage: 1, 29 currentPage: 1,
@@ -22,21 +31,35 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
22 totalItems: null 31 totalItems: null
23 } 32 }
24 33
34 displayTimestampOptions = false
35
36 timestampOptions: {
37 startTimestampEnabled: boolean
38 startTimestamp: number
39 stopTimestampEnabled: boolean
40 stopTimestamp: number
41 } = {} as any
42
25 private videoPlaylistId: string | number 43 private videoPlaylistId: string | number
26 private paramsSub: Subscription 44 private paramsSub: Subscription
27 45
28 constructor ( 46 constructor (
29 private authService: AuthService, 47 private authService: AuthService,
48 private serverService: ServerService,
30 private notifier: Notifier, 49 private notifier: Notifier,
31 private confirmService: ConfirmService, 50 private confirmService: ConfirmService,
32 private route: ActivatedRoute, 51 private route: ActivatedRoute,
33 private videoService: VideoService 52 private i18n: I18n,
53 private videoService: VideoService,
54 private videoPlaylistService: VideoPlaylistService
34 ) {} 55 ) {}
35 56
36 ngOnInit () { 57 ngOnInit () {
37 this.paramsSub = this.route.params.subscribe(routeParams => { 58 this.paramsSub = this.route.params.subscribe(routeParams => {
38 this.videoPlaylistId = routeParams[ 'videoPlaylistId' ] 59 this.videoPlaylistId = routeParams[ 'videoPlaylistId' ]
39 this.loadElements() 60 this.loadElements()
61
62 this.loadPlaylistInfo()
40 }) 63 })
41 } 64 }
42 65
@@ -44,6 +67,46 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
44 if (this.paramsSub) this.paramsSub.unsubscribe() 67 if (this.paramsSub) this.paramsSub.unsubscribe()
45 } 68 }
46 69
70 isVideoBlur (video: Video) {
71 return video.isVideoNSFWForUser(this.authService.getUser(), this.serverService.getConfig())
72 }
73
74 removeFromPlaylist (video: Video) {
75 this.videoPlaylistService.removeVideoFromPlaylist(this.playlist.id, video.id)
76 .subscribe(
77 () => {
78 this.notifier.success(this.i18n('Video removed from {{name}}', { name: this.playlist.displayName }))
79
80 this.videos = this.videos.filter(v => v.id !== video.id)
81 },
82
83 err => this.notifier.error(err.message)
84 )
85
86 this.moreDropdown.close()
87 }
88
89 updateTimestamps (video: Video) {
90 const body: VideoPlaylistElementUpdate = {}
91
92 body.startTimestamp = this.timestampOptions.startTimestampEnabled ? this.timestampOptions.startTimestamp : null
93 body.stopTimestamp = this.timestampOptions.stopTimestampEnabled ? this.timestampOptions.stopTimestamp : null
94
95 this.videoPlaylistService.updateVideoOfPlaylist(this.playlist.id, video.id, body)
96 .subscribe(
97 () => {
98 this.notifier.success(this.i18n('Timestamps updated'))
99
100 video.playlistElement.startTimestamp = body.startTimestamp
101 video.playlistElement.stopTimestamp = body.stopTimestamp
102 },
103
104 err => this.notifier.error(err.message)
105 )
106
107 this.moreDropdown.close()
108 }
109
47 onNearOfBottom () { 110 onNearOfBottom () {
48 // Last page 111 // Last page
49 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return 112 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
@@ -52,6 +115,50 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
52 this.loadElements() 115 this.loadElements()
53 } 116 }
54 117
118 formatTimestamp (video: Video) {
119 const start = video.playlistElement.startTimestamp
120 const stop = video.playlistElement.stopTimestamp
121
122 const startFormatted = secondsToTime(start, true, ':')
123 const stopFormatted = secondsToTime(stop, true, ':')
124
125 if (start === null && stop === null) return ''
126
127 if (start !== null && stop === null) return this.i18n('Starts at ') + startFormatted
128 if (start === null && stop !== null) return this.i18n('Stops at ') + stopFormatted
129
130 return this.i18n('Starts at ') + startFormatted + this.i18n(' and stops at ') + stopFormatted
131 }
132
133 onDropdownOpenChange () {
134 this.displayTimestampOptions = false
135 }
136
137 toggleDisplayTimestampsOptions (event: Event, video: Video) {
138 event.preventDefault()
139
140 this.displayTimestampOptions = !this.displayTimestampOptions
141
142 if (this.displayTimestampOptions === true) {
143 this.timestampOptions = {
144 startTimestampEnabled: false,
145 stopTimestampEnabled: false,
146 startTimestamp: 0,
147 stopTimestamp: video.duration
148 }
149
150 if (video.playlistElement.startTimestamp) {
151 this.timestampOptions.startTimestampEnabled = true
152 this.timestampOptions.startTimestamp = video.playlistElement.startTimestamp
153 }
154
155 if (video.playlistElement.stopTimestamp) {
156 this.timestampOptions.stopTimestampEnabled = true
157 this.timestampOptions.stopTimestamp = video.playlistElement.stopTimestamp
158 }
159 }
160 }
161
55 private loadElements () { 162 private loadElements () {
56 this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination) 163 this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination)
57 .subscribe(({ totalVideos, videos }) => { 164 .subscribe(({ totalVideos, videos }) => {
@@ -59,4 +166,11 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
59 this.pagination.totalItems = totalVideos 166 this.pagination.totalItems = totalVideos
60 }) 167 })
61 } 168 }
169
170 private loadPlaylistInfo () {
171 this.videoPlaylistService.getVideoPlaylist(this.videoPlaylistId)
172 .subscribe(playlist => {
173 this.playlist = playlist
174 })
175 }
62} 176}
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html
index 1e532ec13..5622b3a31 100644
--- a/client/src/app/menu/menu.component.html
+++ b/client/src/app/menu/menu.component.html
@@ -10,7 +10,7 @@
10 </div> 10 </div>
11 11
12 <div class="logged-in-more" ngbDropdown placement="bottom-right"> 12 <div class="logged-in-more" ngbDropdown placement="bottom-right">
13 <span class="glyphicon glyphicon-option-vertical" ngbDropdownToggle role="button"></span> 13 <my-global-icon iconName="more-vertical" ngbDropdownToggle role="button"></my-global-icon>
14 14
15 <div ngbDropdownMenu> 15 <div ngbDropdownMenu>
16 <a *ngIf="user.account" i18n [routerLink]="[ '/accounts', user.account.nameWithHost ]" class="dropdown-item"> 16 <a *ngIf="user.account" i18n [routerLink]="[ '/accounts', user.account.nameWithHost ]" class="dropdown-item">
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss
index 137df6fc1..7b392b599 100644
--- a/client/src/app/menu/menu.component.scss
+++ b/client/src/app/menu/menu.component.scss
@@ -65,9 +65,10 @@ menu {
65 .logged-in-more { 65 .logged-in-more {
66 margin-right: 20px; 66 margin-right: 20px;
67 67
68 .glyphicon { 68 my-global-icon {
69 @include apply-svg-color(var(--mainBackgroundColor));
70
69 cursor: pointer; 71 cursor: pointer;
70 font-size: 18px;
71 72
72 &::after { 73 &::after {
73 border: none; 74 border: none;
diff --git a/client/src/app/shared/buttons/action-dropdown.component.html b/client/src/app/shared/buttons/action-dropdown.component.html
index 114b1d71f..6999474d6 100644
--- a/client/src/app/shared/buttons/action-dropdown.component.html
+++ b/client/src/app/shared/buttons/action-dropdown.component.html
@@ -3,7 +3,7 @@
3 class="action-button" [ngClass]="{ small: buttonSize === 'small', grey: theme === 'grey', orange: theme === 'orange' }" 3 class="action-button" [ngClass]="{ small: buttonSize === 'small', grey: theme === 'grey', orange: theme === 'orange' }"
4 ngbDropdownToggle role="button" 4 ngbDropdownToggle role="button"
5 > 5 >
6 <my-global-icon *ngIf="!label" class="more-icon" iconName="more"></my-global-icon> 6 <my-global-icon *ngIf="!label" class="more-icon" iconName="more-horizontal"></my-global-icon>
7 <span *ngIf="label" class="dropdown-toggle">{{ label }}</span> 7 <span *ngIf="label" class="dropdown-toggle">{{ label }}</span>
8 </div> 8 </div>
9 9
diff --git a/client/src/app/shared/images/global-icon.component.ts b/client/src/app/shared/images/global-icon.component.ts
index 3fda7ee4d..093e88033 100644
--- a/client/src/app/shared/images/global-icon.component.ts
+++ b/client/src/app/shared/images/global-icon.component.ts
@@ -23,7 +23,8 @@ const icons = {
23 'dislike': require('../../../assets/images/video/dislike.html'), 23 'dislike': require('../../../assets/images/video/dislike.html'),
24 'heart': require('../../../assets/images/video/heart.html'), 24 'heart': require('../../../assets/images/video/heart.html'),
25 'like': require('../../../assets/images/video/like.html'), 25 'like': require('../../../assets/images/video/like.html'),
26 'more': require('../../../assets/images/video/more.html'), 26 'more-horizontal': require('../../../assets/images/global/more-horizontal.html'),
27 'more-vertical': require('../../../assets/images/global/more-vertical.html'),
27 'share': require('../../../assets/images/video/share.html'), 28 'share': require('../../../assets/images/video/share.html'),
28 'upload': require('../../../assets/images/video/upload.html'), 29 'upload': require('../../../assets/images/video/upload.html'),
29 'playlist-add': require('../../../assets/images/video/playlist-add.html') 30 'playlist-add': require('../../../assets/images/video/playlist-add.html')
diff --git a/client/src/app/shared/video-playlist/video-add-to-playlist.component.html b/client/src/app/shared/video-playlist/video-add-to-playlist.component.html
index ed3cd8dc5..f85e50d6d 100644
--- a/client/src/app/shared/video-playlist/video-add-to-playlist.component.html
+++ b/client/src/app/shared/video-playlist/video-add-to-playlist.component.html
@@ -2,10 +2,10 @@
2 <div class="first-row"> 2 <div class="first-row">
3 <div i18n class="title">Save to</div> 3 <div i18n class="title">Save to</div>
4 4
5 <div i18n class="options" (click)="displayOptions = !displayOptions"> 5 <div class="options" (click)="displayOptions = !displayOptions">
6 <my-global-icon iconName="cog"></my-global-icon> 6 <my-global-icon iconName="cog"></my-global-icon>
7 7
8 Options 8 <span i18n>Options</span>
9 </div> 9 </div>
10 </div> 10 </div>
11 11
diff --git a/client/src/app/shared/video-playlist/video-add-to-playlist.component.scss b/client/src/app/shared/video-playlist/video-add-to-playlist.component.scss
index 68dcda1eb..bc0d55912 100644
--- a/client/src/app/shared/video-playlist/video-add-to-playlist.component.scss
+++ b/client/src/app/shared/video-playlist/video-add-to-playlist.component.scss
@@ -18,6 +18,8 @@
18 } 18 }
19 19
20 .options { 20 .options {
21 display: flex;
22 align-items: center;
21 font-size: 14px; 23 font-size: 14px;
22 cursor: pointer; 24 cursor: pointer;
23 25
@@ -25,7 +27,8 @@
25 @include apply-svg-color(#333); 27 @include apply-svg-color(#333);
26 28
27 width: 16px; 29 width: 16px;
28 height: 16px; 30 height: 23px;
31 margin-right: 3px;
29 } 32 }
30 } 33 }
31 } 34 }
diff --git a/client/src/app/shared/video-playlist/video-playlist-miniature.component.scss b/client/src/app/shared/video-playlist/video-playlist-miniature.component.scss
index f8cd47f73..72158eb10 100644
--- a/client/src/app/shared/video-playlist/video-playlist-miniature.component.scss
+++ b/client/src/app/shared/video-playlist/video-playlist-miniature.component.scss
@@ -29,7 +29,8 @@
29 padding: 0 10px; 29 padding: 0 10px;
30 display: flex; 30 display: flex;
31 align-items: center; 31 align-items: center;
32 font-size: 15px; 32 font-size: 14px;
33 font-weight: $font-semibold;
33 } 34 }
34 } 35 }
35 36
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts
index c936a8207..95b5e3671 100644
--- a/client/src/app/shared/video/video.model.ts
+++ b/client/src/app/shared/video/video.model.ts
@@ -1,5 +1,5 @@
1import { User } from '../' 1import { User } from '../'
2import { Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared' 2import { PlaylistElement, Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared'
3import { Avatar } from '../../../../../shared/models/avatars/avatar.model' 3import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
4import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' 4import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model'
5import { durationToString, getAbsoluteAPIUrl } from '../misc/utils' 5import { durationToString, getAbsoluteAPIUrl } from '../misc/utils'
@@ -47,6 +47,8 @@ export class Video implements VideoServerModel {
47 blacklisted?: boolean 47 blacklisted?: boolean
48 blacklistedReason?: string 48 blacklistedReason?: string
49 49
50 playlistElement?: PlaylistElement
51
50 account: { 52 account: {
51 id: number 53 id: number
52 uuid: string 54 uuid: string
@@ -125,6 +127,8 @@ export class Video implements VideoServerModel {
125 this.blacklistedReason = hash.blacklistedReason 127 this.blacklistedReason = hash.blacklistedReason
126 128
127 this.userHistory = hash.userHistory 129 this.userHistory = hash.userHistory
130
131 this.playlistElement = hash.playlistElement
128 } 132 }
129 133
130 isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { 134 isVideoNSFWForUser (user: User, serverConfig: ServerConfig) {
diff --git a/client/src/app/videos/+video-watch/video-watch.component.html b/client/src/app/videos/+video-watch/video-watch.component.html
index 615b88bd6..394c31f23 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -91,7 +91,7 @@
91 91
92 <div class="action-dropdown" ngbDropdown placement="top" role="button"> 92 <div class="action-dropdown" ngbDropdown placement="top" role="button">
93 <div class="action-button" ngbDropdownToggle role="button"> 93 <div class="action-button" ngbDropdownToggle role="button">
94 <my-global-icon class="more-icon" iconName="more"></my-global-icon> 94 <my-global-icon class="more-icon" iconName="more-horizontal"></my-global-icon>
95 </div> 95 </div>
96 96
97 <div ngbDropdownMenu> 97 <div ngbDropdownMenu>
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss
index ff321fdbc..44040e90d 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/videos/+video-watch/video-watch.component.scss
@@ -228,15 +228,7 @@ $other-videos-width: 260px;
228 display: inline-block; 228 display: inline-block;
229 229
230 .dropdown-menu .dropdown-item { 230 .dropdown-menu .dropdown-item {
231 padding: 6px 24px; 231 @include dropdown-with-icon-item;
232
233 my-global-icon {
234 width: 24px;
235
236 margin-right: 10px;
237 position: relative;
238 top: -2px;
239 }
240 } 232 }
241 } 233 }
242 } 234 }
diff --git a/client/src/assets/images/video/more.html b/client/src/assets/images/global/more-horizontal.html
index 39dcad10e..39dcad10e 100644
--- a/client/src/assets/images/video/more.html
+++ b/client/src/assets/images/global/more-horizontal.html
diff --git a/client/src/assets/images/global/more-vertical.html b/client/src/assets/images/global/more-vertical.html
new file mode 100644
index 000000000..9bff87a82
--- /dev/null
+++ b/client/src/assets/images/global/more-vertical.html
@@ -0,0 +1,11 @@
1<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
3 <g id="Artboard-4" transform="translate(-268.000000, -1046.000000)" fill="#000000">
4 <g id="Extras" transform="translate(48.000000, 1046.000000)">
5 <g id="more-vertical" transform="translate(220.000000, 0.000000)">
6 <path d="M10,12 C10,10.8954305 10.8877296,10 12,10 C13.1045695,10 14,10.8877296 14,12 C14,13.1045695 13.1122704,14 12,14 C10.8954305,14 10,13.1122704 10,12 Z M10,5 C10,3.8954305 10.8877296,3 12,3 C13.1045695,3 14,3.88772964 14,5 C14,6.1045695 13.1122704,7 12,7 C10.8954305,7 10,6.11227036 10,5 Z M10,19 C10,17.8954305 10.8877296,17 12,17 C13.1045695,17 14,17.8877296 14,19 C14,20.1045695 13.1122704,21 12,21 C10.8954305,21 10,20.1122704 10,19 Z" id="Combined-Shape"/>
7 </g>
8 </g>
9 </g>
10 </g>
11</svg>
diff --git a/client/src/sass/include/_miniature.scss b/client/src/sass/include/_miniature.scss
index 36d4e84d3..25a024aac 100644
--- a/client/src/sass/include/_miniature.scss
+++ b/client/src/sass/include/_miniature.scss
@@ -28,15 +28,15 @@ $play-overlay-transition: 0.2s ease;
28$play-overlay-height: 26px; 28$play-overlay-height: 26px;
29$play-overlay-width: 18px; 29$play-overlay-width: 18px;
30 30
31@mixin miniature-thumbnail { 31@mixin miniature-thumbnail($width: $video-thumbnail-width, $height: $video-thumbnail-height) {
32 @include disable-outline; 32 @include disable-outline;
33 33
34 display: inline-block; 34 display: inline-block;
35 position: relative; 35 position: relative;
36 border-radius: 3px; 36 border-radius: 3px;
37 overflow: hidden; 37 overflow: hidden;
38 width: $video-thumbnail-width; 38 width: $width;
39 height: $video-thumbnail-height; 39 height: $height;
40 background-color: #ececec; 40 background-color: #ececec;
41 transition: filter $play-overlay-transition; 41 transition: filter $play-overlay-transition;
42 42
@@ -45,8 +45,8 @@ $play-overlay-width: 18px;
45 right: 0; 45 right: 0;
46 bottom: 0; 46 bottom: 0;
47 47
48 width: $video-thumbnail-width; 48 width: inherit;
49 height: $video-thumbnail-height; 49 height: inherit;
50 opacity: 0; 50 opacity: 0;
51 background-color: rgba(0, 0, 0, 0.7); 51 background-color: rgba(0, 0, 0, 0.7);
52 52
@@ -87,8 +87,8 @@ $play-overlay-width: 18px;
87 } 87 }
88 88
89 img { 89 img {
90 width: $video-thumbnail-width; 90 width: inherit;
91 height: $video-thumbnail-height; 91 height: inherit;
92 92
93 &.blur-filter { 93 &.blur-filter {
94 filter: blur(5px); 94 filter: blur(5px);
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index 3eefdb6fb..7faeec6bd 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -515,3 +515,15 @@
515 align-items: center; 515 align-items: center;
516 } 516 }
517} 517}
518
519@mixin dropdown-with-icon-item {
520 padding: 6px 24px;
521
522 my-global-icon {
523 width: 24px;
524
525 margin-right: 10px;
526 position: relative;
527 top: -2px;
528 }
529}
diff --git a/client/src/sass/primeng-custom.scss b/client/src/sass/primeng-custom.scss
index 6e502b028..6de145379 100644
--- a/client/src/sass/primeng-custom.scss
+++ b/client/src/sass/primeng-custom.scss
@@ -62,11 +62,9 @@ p-table {
62 tr { 62 tr {
63 &:hover { 63 &:hover {
64 background-color: var(--submenuColor) !important; 64 background-color: var(--submenuColor) !important;
65 }
66 65
67 &:not(:hover) { 66 .action-cell .dropdown-root {
68 .action-cell * { 67 display: block !important;
69 display: none !important;
70 } 68 }
71 } 69 }
72 70
@@ -140,6 +138,14 @@ p-table {
140 padding: 0 !important; 138 padding: 0 !important;
141 text-align: center; 139 text-align: center;
142 140
141 .dropdown-root {
142 display: none !important;
143
144 &.show {
145 display: block !important;
146 }
147 }
148
143 my-edit-button + my-delete-button { 149 my-edit-button + my-delete-button {
144 margin-left: 5px; 150 margin-left: 5px;
145 } 151 }