aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+my-account/my-account-routing.module.ts12
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html31
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.scss11
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts6
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html3
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss3
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.html2
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.scss4
-rw-r--r--client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html11
-rw-r--r--client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss9
-rw-r--r--client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts67
-rw-r--r--client/src/app/+video-channels/video-channels-routing.module.ts10
-rw-r--r--client/src/app/+video-channels/video-channels.component.html1
-rw-r--r--client/src/app/+video-channels/video-channels.module.ts4
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html2
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss4
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts16
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-miniature.component.html18
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-miniature.component.scss36
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-miniature.component.ts3
-rw-r--r--client/src/app/shared/video/infinite-scroller.directive.ts23
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.html6
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.scss3
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts4
-rw-r--r--client/src/sass/application.scss2
-rw-r--r--client/src/sass/include/_variables.scss2
26 files changed, 248 insertions, 45 deletions
diff --git a/client/src/app/+my-account/my-account-routing.module.ts b/client/src/app/+my-account/my-account-routing.module.ts
index 3f921b13f..07557a029 100644
--- a/client/src/app/+my-account/my-account-routing.module.ts
+++ b/client/src/app/+my-account/my-account-routing.module.ts
@@ -85,20 +85,20 @@ const myAccountRoutes: Routes = [
85 } 85 }
86 }, 86 },
87 { 87 {
88 path: 'video-playlists/:videoPlaylistId', 88 path: 'video-playlists/create',
89 component: MyAccountVideoPlaylistElementsComponent, 89 component: MyAccountVideoPlaylistCreateComponent,
90 data: { 90 data: {
91 meta: { 91 meta: {
92 title: 'Playlist elements' 92 title: 'Create new playlist'
93 } 93 }
94 } 94 }
95 }, 95 },
96 { 96 {
97 path: 'video-playlists/create', 97 path: 'video-playlists/:videoPlaylistId',
98 component: MyAccountVideoPlaylistCreateComponent, 98 component: MyAccountVideoPlaylistElementsComponent,
99 data: { 99 data: {
100 meta: { 100 meta: {
101 title: 'Create new playlist' 101 title: 'Playlist elements'
102 } 102 }
103 } 103 }
104 }, 104 },
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 bc26e198e..95d4519fa 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,11 +1,26 @@
1<div i18n class="no-results" *ngIf="pagination.totalItems === 0">No videos in this playlist.</div> 1<div class="row">
2 2
3<div 3 <div class="playlist-info col-xs-12 col-md-5 col-xl-3">
4 class="videos" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" 4 <my-video-playlist-miniature
5 cdkDropList (cdkDropListDropped)="drop($event)" 5 *ngIf="playlist" [playlist]="playlist" [toManage]="false" [displayChannel]="true"
6> 6 [displayDescription]="true" [displayPrivacy]="true"
7 <div class="video" *ngFor="let video of videos" cdkDrag (cdkDragMoved)="onDragMove($event)"> 7 ></my-video-playlist-miniature>
8 <my-video-playlist-element-miniature [video]="video" [playlist]="playlist" [owned]="true" (elementRemoved)="onElementRemoved($event)"> 8 </div>
9 </my-video-playlist-element-miniature> 9
10 <div class="col-xs-12 col-md-7 col-xl-9">
11 <div i18n class="no-results" *ngIf="pagination.totalItems === 0">No videos in this playlist.</div>
12
13 <div
14 class="videos" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"
15 cdkDropList (cdkDropListDropped)="drop($event)"
16 >
17 <div class="video" *ngFor="let video of videos; trackBy: trackByFn" cdkDrag (cdkDragMoved)="onDragMove($event)">
18 <my-video-playlist-element-miniature
19 [video]="video" [playlist]="playlist" [owned]="true" (elementRemoved)="onElementRemoved($event)"
20 [position]="video.playlistElement.position"
21 >
22 </my-video-playlist-element-miniature>
23 </div>
24 </div>
10 </div> 25 </div>
11</div> 26</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 b05af0490..900669827 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
@@ -2,6 +2,17 @@
2@import '_mixins'; 2@import '_mixins';
3@import '_miniature'; 3@import '_miniature';
4 4
5.playlist-info {
6 background-color: var(--submenuColor);
7 margin-left: -15px;
8 margin-top: -$sub-menu-margin-bottom;
9
10 padding: $sub-menu-margin-bottom 0;
11
12 display: flex;
13 justify-content: center;
14}
15
5// Thanks Angular CDK <3 https://material.angular.io/cdk/drag-drop/examples 16// Thanks Angular CDK <3 https://material.angular.io/cdk/drag-drop/examples
6.cdk-drag-preview { 17.cdk-drag-preview {
7 box-sizing: border-box; 18 box-sizing: border-box;
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 dcf470be3..25d51d2cb 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
@@ -24,7 +24,7 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
24 24
25 pagination: ComponentPagination = { 25 pagination: ComponentPagination = {
26 currentPage: 1, 26 currentPage: 1,
27 itemsPerPage: 10, 27 itemsPerPage: 30,
28 totalItems: null 28 totalItems: null
29 } 29 }
30 30
@@ -123,6 +123,10 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
123 this.loadElements() 123 this.loadElements()
124 } 124 }
125 125
126 trackByFn (index: number, elem: Video) {
127 return elem.id
128 }
129
126 private loadElements () { 130 private loadElements () {
127 this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination) 131 this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination)
128 .subscribe(({ totalVideos, videos }) => { 132 .subscribe(({ totalVideos, videos }) => {
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html
index 7d1bed12a..322560673 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html
@@ -8,7 +8,8 @@
8<div class="video-playlists" myInfiniteScroller (nearOfBottom)="onNearOfBottom()"> 8<div class="video-playlists" myInfiniteScroller (nearOfBottom)="onNearOfBottom()">
9 <div *ngFor="let playlist of videoPlaylists" class="video-playlist"> 9 <div *ngFor="let playlist of videoPlaylists" class="video-playlist">
10 <div class="miniature-wrapper"> 10 <div class="miniature-wrapper">
11 <my-video-playlist-miniature [playlist]="playlist" [toManage]="true"></my-video-playlist-miniature> 11 <my-video-playlist-miniature [playlist]="playlist" [toManage]="true" [displayChannel]="true" [displayDescription]="true" [displayPrivacy]="true"
12 ></my-video-playlist-miniature>
12 </div> 13 </div>
13 14
14 <div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons"> 15 <div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons">
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss
index 88fba5b05..f648c33e4 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss
@@ -20,8 +20,9 @@
20 /deep/ .miniature { 20 /deep/ .miniature {
21 display: flex; 21 display: flex;
22 22
23 .miniature-bottom { 23 .miniature-info {
24 margin-left: 10px; 24 margin-left: 10px;
25 width: auto;
25 } 26 }
26 } 27 }
27 } 28 }
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
index 69748ef37..b09e845ac 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.html
@@ -17,7 +17,7 @@
17 <div class="video-info"> 17 <div class="video-info">
18 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a> 18 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
19 <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> 19 <span i18n class="video-info-date-views">{{ video.createdAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span>
20 <div class="video-info-private">{{ video.privacy.label }}{{ getStateLabel(video) }}</div> 20 <div class="video-info-privacy">{{ video.privacy.label }}{{ getStateLabel(video) }}</div>
21 <div *ngIf="video.blacklisted" class="video-info-blacklisted"> 21 <div *ngIf="video.blacklisted" class="video-info-blacklisted">
22 <span class="blacklisted-label" i18n>Blacklisted</span> 22 <span class="blacklisted-label" i18n>Blacklisted</span>
23 <span class="blacklisted-reason" *ngIf="video.blacklistedReason">{{ video.blacklistedReason }}</span> 23 <span class="blacklisted-reason" *ngIf="video.blacklistedReason">{{ video.blacklistedReason }}</span>
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
index 39d0cf2f7..f6b5faa45 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
@@ -64,11 +64,11 @@
64 } 64 }
65 65
66 .video-info-date-views, 66 .video-info-date-views,
67 .video-info-private, 67 .video-info-privacy,
68 .video-info-blacklisted { 68 .video-info-blacklisted {
69 font-size: 13px; 69 font-size: 13px;
70 70
71 &.video-info-private, 71 &.video-info-privacy,
72 &.video-info-blacklisted .blacklisted-label { 72 &.video-info-blacklisted .blacklisted-label {
73 font-weight: $font-semibold; 73 font-weight: $font-semibold;
74 } 74 }
diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html
new file mode 100644
index 000000000..0d9fba375
--- /dev/null
+++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.html
@@ -0,0 +1,11 @@
1<div i18n class="title-page title-page-single">
2 Created {{pagination.totalItems}} playlists
3</div>
4
5<div i18n class="no-results" *ngIf="pagination.totalItems === 0">This channel does not have playlists.</div>
6
7<div class="video-playlist" myInfiniteScroller (nearOfBottom)="onNearOfBottom()">
8 <div *ngFor="let playlist of videoPlaylists">
9 <my-video-playlist-miniature [playlist]="playlist" [toManage]="false"></my-video-playlist-miniature>
10 </div>
11</div>
diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss
new file mode 100644
index 000000000..fe9104794
--- /dev/null
+++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.scss
@@ -0,0 +1,9 @@
1.video-playlist {
2 display: flex;
3 justify-content: center;
4
5 my-video-playlist-miniature {
6 margin-right: 15px;
7 margin-bottom: 30px;
8 }
9}
diff --git a/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts
new file mode 100644
index 000000000..f878a5a24
--- /dev/null
+++ b/client/src/app/+video-channels/video-channel-playlists/video-channel-playlists.component.ts
@@ -0,0 +1,67 @@
1import { Component, OnDestroy, OnInit } from '@angular/core'
2import { AuthService } from '../../core/auth'
3import { ConfirmService } from '../../core/confirm'
4import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
5import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
6import { flatMap } from 'rxjs/operators'
7import { Subscription } from 'rxjs'
8import { Notifier } from '@app/core'
9import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
10import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
11import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
12
13@Component({
14 selector: 'my-video-channel-playlists',
15 templateUrl: './video-channel-playlists.component.html',
16 styleUrls: [ './video-channel-playlists.component.scss' ]
17})
18export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
19 videoPlaylists: VideoPlaylist[] = []
20
21 pagination: ComponentPagination = {
22 currentPage: 1,
23 itemsPerPage: 20,
24 totalItems: null
25 }
26
27 private videoChannelSub: Subscription
28 private videoChannel: VideoChannel
29
30 constructor (
31 private authService: AuthService,
32 private notifier: Notifier,
33 private confirmService: ConfirmService,
34 private videoPlaylistService: VideoPlaylistService,
35 private videoChannelService: VideoChannelService
36 ) {}
37
38 ngOnInit () {
39 // Parent get the video channel for us
40 this.videoChannelSub = this.videoChannelService.videoChannelLoaded
41 .subscribe(videoChannel => {
42 this.videoChannel = videoChannel
43 this.loadVideoPlaylists()
44 })
45 }
46
47 ngOnDestroy () {
48 if (this.videoChannelSub) this.videoChannelSub.unsubscribe()
49 }
50
51 onNearOfBottom () {
52 // Last page
53 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
54
55 this.pagination.currentPage += 1
56 this.loadVideoPlaylists()
57 }
58
59 private loadVideoPlaylists () {
60 this.authService.userInformationLoaded
61 .pipe(flatMap(() => this.videoPlaylistService.listChannelPlaylists(this.videoChannel)))
62 .subscribe(res => {
63 this.videoPlaylists = this.videoPlaylists.concat(res.data)
64 this.pagination.totalItems = res.total
65 })
66 }
67}
diff --git a/client/src/app/+video-channels/video-channels-routing.module.ts b/client/src/app/+video-channels/video-channels-routing.module.ts
index 3ac3533d9..cedd07d39 100644
--- a/client/src/app/+video-channels/video-channels-routing.module.ts
+++ b/client/src/app/+video-channels/video-channels-routing.module.ts
@@ -4,6 +4,7 @@ import { MetaGuard } from '@ngx-meta/core'
4import { VideoChannelsComponent } from './video-channels.component' 4import { VideoChannelsComponent } from './video-channels.component'
5import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' 5import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
6import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component' 6import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component'
7import { VideoChannelPlaylistsComponent } from '@app/+video-channels/video-channel-playlists/video-channel-playlists.component'
7 8
8const videoChannelsRoutes: Routes = [ 9const videoChannelsRoutes: Routes = [
9 { 10 {
@@ -26,6 +27,15 @@ const videoChannelsRoutes: Routes = [
26 } 27 }
27 }, 28 },
28 { 29 {
30 path: 'video-playlists',
31 component: VideoChannelPlaylistsComponent,
32 data: {
33 meta: {
34 title: 'Video channel playlists'
35 }
36 }
37 },
38 {
29 path: 'about', 39 path: 'about',
30 component: VideoChannelAboutComponent, 40 component: VideoChannelAboutComponent,
31 data: { 41 data: {
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html
index c65b5713d..600b7a365 100644
--- a/client/src/app/+video-channels/video-channels.component.html
+++ b/client/src/app/+video-channels/video-channels.component.html
@@ -22,6 +22,7 @@
22 22
23 <div class="links"> 23 <div class="links">
24 <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a> 24 <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a>
25 <a i18n routerLink="video-playlists" routerLinkActive="active" class="title-page">Video playlists</a>
25 <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a> 26 <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a>
26 </div> 27 </div>
27 </div> 28 </div>
diff --git a/client/src/app/+video-channels/video-channels.module.ts b/client/src/app/+video-channels/video-channels.module.ts
index a09ea6f11..6975d05b2 100644
--- a/client/src/app/+video-channels/video-channels.module.ts
+++ b/client/src/app/+video-channels/video-channels.module.ts
@@ -4,6 +4,7 @@ import { VideoChannelsRoutingModule } from './video-channels-routing.module'
4import { VideoChannelsComponent } from './video-channels.component' 4import { VideoChannelsComponent } from './video-channels.component'
5import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component' 5import { VideoChannelVideosComponent } from './video-channel-videos/video-channel-videos.component'
6import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component' 6import { VideoChannelAboutComponent } from './video-channel-about/video-channel-about.component'
7import { VideoChannelPlaylistsComponent } from '@app/+video-channels/video-channel-playlists/video-channel-playlists.component'
7 8
8@NgModule({ 9@NgModule({
9 imports: [ 10 imports: [
@@ -14,7 +15,8 @@ import { VideoChannelAboutComponent } from './video-channel-about/video-channel-
14 declarations: [ 15 declarations: [
15 VideoChannelsComponent, 16 VideoChannelsComponent,
16 VideoChannelVideosComponent, 17 VideoChannelVideosComponent,
17 VideoChannelAboutComponent 18 VideoChannelAboutComponent,
19 VideoChannelPlaylistsComponent
18 ], 20 ],
19 21
20 exports: [ 22 exports: [
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html
index 1f178675f..4764fc0e1 100644
--- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html
+++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html
@@ -2,7 +2,7 @@
2 <a [routerLink]="buildRouterLink()" [queryParams]="buildRouterQuery()"> 2 <a [routerLink]="buildRouterLink()" [queryParams]="buildRouterQuery()">
3 <div class="position"> 3 <div class="position">
4 <my-global-icon *ngIf="playing" iconName="play"></my-global-icon> 4 <my-global-icon *ngIf="playing" iconName="play"></my-global-icon>
5 <ng-container *ngIf="!playing">{{ video.playlistElement.position }}</ng-container> 5 <ng-container *ngIf="!playing">{{ position }}</ng-container>
6 </div> 6 </div>
7 7
8 <my-video-thumbnail 8 <my-video-thumbnail
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss
index eb869f69a..f57fd2e1c 100644
--- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss
+++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss
@@ -34,7 +34,7 @@
34 font-weight: $font-semibold; 34 font-weight: $font-semibold;
35 margin-right: 10px; 35 margin-right: 10px;
36 color: $grey-foreground-color; 36 color: $grey-foreground-color;
37 min-width: 20px; 37 min-width: 25px;
38 38
39 my-global-icon { 39 my-global-icon {
40 @include apply-svg-color($grey-foreground-color); 40 @include apply-svg-color($grey-foreground-color);
@@ -59,7 +59,7 @@
59 59
60 a { 60 a {
61 color: var(--mainForegroundColor); 61 color: var(--mainForegroundColor);
62 width: fit-content; 62 width: auto;
63 63
64 &:hover { 64 &:hover {
65 text-decoration: underline !important; 65 text-decoration: underline !important;
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts
index c0cfd855d..6cc5b87b4 100644
--- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts
+++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts
@@ -1,4 +1,4 @@
1import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core' 1import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'
2import { Video } from '@app/shared/video/video.model' 2import { Video } from '@app/shared/video/video.model'
3import { VideoPlaylistElementUpdate } from '@shared/models' 3import { VideoPlaylistElementUpdate } from '@shared/models'
4import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core' 4import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core'
@@ -13,7 +13,8 @@ import { secondsToTime } from '../../../assets/player/utils'
13@Component({ 13@Component({
14 selector: 'my-video-playlist-element-miniature', 14 selector: 'my-video-playlist-element-miniature',
15 styleUrls: [ './video-playlist-element-miniature.component.scss' ], 15 styleUrls: [ './video-playlist-element-miniature.component.scss' ],
16 templateUrl: './video-playlist-element-miniature.component.html' 16 templateUrl: './video-playlist-element-miniature.component.html',
17 changeDetection: ChangeDetectionStrategy.OnPush
17}) 18})
18export class VideoPlaylistElementMiniatureComponent { 19export class VideoPlaylistElementMiniatureComponent {
19 @ViewChild('moreDropdown') moreDropdown: NgbDropdown 20 @ViewChild('moreDropdown') moreDropdown: NgbDropdown
@@ -24,6 +25,7 @@ export class VideoPlaylistElementMiniatureComponent {
24 @Input() playing = false 25 @Input() playing = false
25 @Input() rowLink = false 26 @Input() rowLink = false
26 @Input() accountLink = true 27 @Input() accountLink = true
28 @Input() position: number
27 29
28 @Output() elementRemoved = new EventEmitter<Video>() 30 @Output() elementRemoved = new EventEmitter<Video>()
29 31
@@ -44,7 +46,8 @@ export class VideoPlaylistElementMiniatureComponent {
44 private route: ActivatedRoute, 46 private route: ActivatedRoute,
45 private i18n: I18n, 47 private i18n: I18n,
46 private videoService: VideoService, 48 private videoService: VideoService,
47 private videoPlaylistService: VideoPlaylistService 49 private videoPlaylistService: VideoPlaylistService,
50 private cdr: ChangeDetectorRef
48 ) {} 51 ) {}
49 52
50 buildRouterLink () { 53 buildRouterLink () {
@@ -95,6 +98,8 @@ export class VideoPlaylistElementMiniatureComponent {
95 98
96 video.playlistElement.startTimestamp = body.startTimestamp 99 video.playlistElement.startTimestamp = body.startTimestamp
97 video.playlistElement.stopTimestamp = body.stopTimestamp 100 video.playlistElement.stopTimestamp = body.stopTimestamp
101
102 this.cdr.detectChanges()
98 }, 103 },
99 104
100 err => this.notifier.error(err.message) 105 err => this.notifier.error(err.message)
@@ -145,5 +150,10 @@ export class VideoPlaylistElementMiniatureComponent {
145 this.timestampOptions.stopTimestamp = video.playlistElement.stopTimestamp 150 this.timestampOptions.stopTimestamp = video.playlistElement.stopTimestamp
146 } 151 }
147 } 152 }
153
154 // FIXME: why do we have to use setTimeout here?
155 setTimeout(() => {
156 this.cdr.detectChanges()
157 })
148 } 158 }
149} 159}
diff --git a/client/src/app/shared/video-playlist/video-playlist-miniature.component.html b/client/src/app/shared/video-playlist/video-playlist-miniature.component.html
index a136f9233..c01c73012 100644
--- a/client/src/app/shared/video-playlist/video-playlist-miniature.component.html
+++ b/client/src/app/shared/video-playlist/video-playlist-miniature.component.html
@@ -1,6 +1,6 @@
1<div class="miniature" [ngClass]="{ 'no-videos': playlist.videosLength === 0, 'to-manage': toManage }"> 1<div class="miniature" [ngClass]="{ 'no-videos': playlist.videosLength === 0, 'to-manage': toManage }">
2 <a 2 <a
3 [routerLink]="getPlaylistUrl()" [attr.title]="playlist.displayName" 3 [routerLink]="getPlaylistUrl()" [attr.title]="playlist.description"
4 class="miniature-thumbnail" 4 class="miniature-thumbnail"
5 > 5 >
6 <img alt="" [attr.aria-labelledby]="playlist.displayName" [attr.src]="playlist.thumbnailUrl" /> 6 <img alt="" [attr.aria-labelledby]="playlist.displayName" [attr.src]="playlist.thumbnailUrl" />
@@ -14,9 +14,21 @@
14 </div> 14 </div>
15 </a> 15 </a>
16 16
17 <div class="miniature-bottom"> 17 <div class="miniature-info">
18 <a tabindex="-1" class="miniature-name" [routerLink]="getPlaylistUrl()" [attr.title]="playlist.displayName"> 18 <a tabindex="-1" class="miniature-name" [routerLink]="getPlaylistUrl()" [attr.title]="playlist.description">
19 {{ playlist.displayName }} 19 {{ playlist.displayName }}
20 </a> 20 </a>
21
22 <div class="video-info-privacy" *ngIf="displayPrivacy">{{ playlist.privacy.label }}</div>
23
24 <div class="video-info-by-date">
25 <a i18n [routerLink]="[ '/video-channels', playlist.videoChannelBy ]" class="by" *ngIf="displayChannel && playlist.videoChannelBy">
26 {{ playlist.videoChannelBy }}
27 </a>
28
29 <div i18n class="updated-at">Updated {{ playlist.updatedAt | myFromNow }}</div>
30 </div>
31
32 <div *ngIf="displayDescription" class="video-info-description">{{ playlist.description }}</div>
21 </div> 33 </div>
22</div> 34</div>
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 72158eb10..94edd1177 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
@@ -11,9 +11,11 @@
11 } 11 }
12 } 12 }
13 13
14 &.to-manage .play-overlay, 14 &.to-manage,
15 &.no-videos { 15 &.no-videos {
16 display: none; 16 .play-overlay {
17 display: none;
18 }
17 } 19 }
18 20
19 .miniature-thumbnail { 21 .miniature-thumbnail {
@@ -34,7 +36,7 @@
34 } 36 }
35 } 37 }
36 38
37 .miniature-bottom { 39 .miniature-info {
38 width: 200px; 40 width: 200px;
39 margin-top: 2px; 41 margin-top: 2px;
40 line-height: normal; 42 line-height: normal;
@@ -42,5 +44,33 @@
42 .miniature-name { 44 .miniature-name {
43 @include miniature-name; 45 @include miniature-name;
44 } 46 }
47
48 .video-info-by-date {
49 display: flex;
50 font-size: 13px;
51 margin: 5px 0;
52
53 .by {
54 @include disable-default-a-behaviour;
55
56 display: block;
57 color: var(--mainForegroundColor);
58
59 &::after {
60 content: '-';
61 margin: 0 3px;
62 }
63 }
64 }
65
66 .video-info-privacy {
67 font-size: 13px;
68 font-weight: $font-semibold;
69 }
70
71 .video-info-description {
72 margin-top: 10px;
73 color: $grey-foreground-color;
74 }
45 } 75 }
46} 76}
diff --git a/client/src/app/shared/video-playlist/video-playlist-miniature.component.ts b/client/src/app/shared/video-playlist/video-playlist-miniature.component.ts
index cb5803400..523e96f2a 100644
--- a/client/src/app/shared/video-playlist/video-playlist-miniature.component.ts
+++ b/client/src/app/shared/video-playlist/video-playlist-miniature.component.ts
@@ -9,6 +9,9 @@ import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
9export class VideoPlaylistMiniatureComponent { 9export class VideoPlaylistMiniatureComponent {
10 @Input() playlist: VideoPlaylist 10 @Input() playlist: VideoPlaylist
11 @Input() toManage = false 11 @Input() toManage = false
12 @Input() displayChannel = false
13 @Input() displayDescription = false
14 @Input() displayPrivacy = false
12 15
13 getPlaylistUrl () { 16 getPlaylistUrl () {
14 if (this.toManage) return [ '/my-account/video-playlists', this.playlist.uuid ] 17 if (this.toManage) return [ '/my-account/video-playlists', this.playlist.uuid ]
diff --git a/client/src/app/shared/video/infinite-scroller.directive.ts b/client/src/app/shared/video/infinite-scroller.directive.ts
index 186597a3a..a9e75007c 100644
--- a/client/src/app/shared/video/infinite-scroller.directive.ts
+++ b/client/src/app/shared/video/infinite-scroller.directive.ts
@@ -1,5 +1,5 @@
1import { distinct, distinctUntilChanged, filter, map, share, startWith, throttleTime } from 'rxjs/operators' 1import { distinct, distinctUntilChanged, filter, map, share, startWith, throttleTime } from 'rxjs/operators'
2import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core' 2import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
3import { fromEvent, Subscription } from 'rxjs' 3import { fromEvent, Subscription } from 'rxjs'
4 4
5@Directive({ 5@Directive({
@@ -11,7 +11,7 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
11 @Input() firstLoadedPage = 1 11 @Input() firstLoadedPage = 1
12 @Input() percentLimit = 70 12 @Input() percentLimit = 70
13 @Input() autoInit = false 13 @Input() autoInit = false
14 @Input() container = document.body 14 @Input() onItself = false
15 15
16 @Output() nearOfBottom = new EventEmitter<void>() 16 @Output() nearOfBottom = new EventEmitter<void>()
17 @Output() nearOfTop = new EventEmitter<void>() 17 @Output() nearOfTop = new EventEmitter<void>()
@@ -24,8 +24,9 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
24 private scrollUpSub: Subscription 24 private scrollUpSub: Subscription
25 private pageChangeSub: Subscription 25 private pageChangeSub: Subscription
26 private middleScreen: number 26 private middleScreen: number
27 private container: HTMLElement
27 28
28 constructor () { 29 constructor (private el: ElementRef) {
29 this.decimalLimit = this.percentLimit / 100 30 this.decimalLimit = this.percentLimit / 100
30 } 31 }
31 32
@@ -40,16 +41,20 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
40 } 41 }
41 42
42 initialize () { 43 initialize () {
44 if (this.onItself) {
45 this.container = this.el.nativeElement
46 }
47
43 this.middleScreen = window.innerHeight / 2 48 this.middleScreen = window.innerHeight / 2
44 49
45 // Emit the last value 50 // Emit the last value
46 const throttleOptions = { leading: true, trailing: true } 51 const throttleOptions = { leading: true, trailing: true }
47 52
48 const scrollObservable = fromEvent(window, 'scroll') 53 const scrollObservable = fromEvent(this.container || window, 'scroll')
49 .pipe( 54 .pipe(
50 startWith(null), 55 startWith(null),
51 throttleTime(200, undefined, throttleOptions), 56 throttleTime(200, undefined, throttleOptions),
52 map(() => ({ current: window.scrollY, maximumScroll: this.container.clientHeight - window.innerHeight })), 57 map(() => this.getScrollInfo()),
53 distinctUntilChanged((o1, o2) => o1.current === o2.current), 58 distinctUntilChanged((o1, o2) => o1.current === o2.current),
54 share() 59 share()
55 ) 60 )
@@ -102,4 +107,12 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
102 // Offset page 107 // Offset page
103 return page + (this.firstLoadedPage - 1) 108 return page + (this.firstLoadedPage - 1)
104 } 109 }
110
111 private getScrollInfo () {
112 if (this.container) {
113 return { current: this.container.scrollTop, maximumScroll: this.container.scrollHeight }
114 }
115
116 return { current: window.scrollY, maximumScroll: document.body.clientHeight - window.innerHeight }
117 }
105} 118}
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 7f3d1cc2e..3df5b7b19 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.html
+++ b/client/src/app/videos/+video-watch/video-watch.component.html
@@ -9,7 +9,7 @@
9 9
10 <div id="videojs-wrapper"></div> 10 <div id="videojs-wrapper"></div>
11 11
12 <div *ngIf="playlist && video" class="playlist"> 12 <div *ngIf="playlist && video" class="playlist" myInfiniteScroller [autoInit]="true" [onItself]="true" (nearOfBottom)="onPlaylistVideosNearOfBottom()">
13 <div class="playlist-info"> 13 <div class="playlist-info">
14 <div class="playlist-display-name"> 14 <div class="playlist-display-name">
15 {{ playlist.displayName }} 15 {{ playlist.displayName }}
@@ -27,10 +27,10 @@
27 </div> 27 </div>
28 </div> 28 </div>
29 29
30 <div *ngFor="let playlistVideo of playlistVideos" myInfiniteScroller [autoInit]="true" #elem [container]="elem" (nearOfBottom)="onPlaylistVideosNearOfBottom()"> 30 <div *ngFor="let playlistVideo of playlistVideos">
31 <my-video-playlist-element-miniature 31 <my-video-playlist-element-miniature
32 [video]="playlistVideo" [playlist]="playlist" [owned]="isPlaylistOwned()" (elementRemoved)="onElementRemoved($event)" 32 [video]="playlistVideo" [playlist]="playlist" [owned]="isPlaylistOwned()" (elementRemoved)="onElementRemoved($event)"
33 [playing]="currentPlaylistPosition === playlistVideo.playlistElement.position" [accountLink]="false" 33 [playing]="currentPlaylistPosition === playlistVideo.playlistElement.position" [accountLink]="false" [position]="playlistVideo.playlistElement.position"
34 ></my-video-playlist-element-miniature> 34 ></my-video-playlist-element-miniature>
35 </div> 35 </div>
36 </div> 36 </div>
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 e1cb249ef..281b9240b 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.scss
+++ b/client/src/app/videos/+video-watch/video-watch.component.scss
@@ -43,11 +43,12 @@ $other-videos-width: 260px;
43 .playlist { 43 .playlist {
44 width: 400px; 44 width: 400px;
45 height: 66vh; 45 height: 66vh;
46 background-color: #e4e4e4; 46 background-color: var(--mainBackgroundColor);
47 overflow-y: auto; 47 overflow-y: auto;
48 48
49 .playlist-info { 49 .playlist-info {
50 padding: 5px 30px; 50 padding: 5px 30px;
51 background-color: #e4e4e4;
51 52
52 .playlist-display-name { 53 .playlist-display-name {
53 font-size: 18px; 54 font-size: 18px;
diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts
index ddd0f1766..adb728aba 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -58,7 +58,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
58 playlistVideos: Video[] = [] 58 playlistVideos: Video[] = []
59 playlistPagination: ComponentPagination = { 59 playlistPagination: ComponentPagination = {
60 currentPage: 1, 60 currentPage: 1,
61 itemsPerPage: 10, 61 itemsPerPage: 30,
62 totalItems: null 62 totalItems: null
63 } 63 }
64 noPlaylistVideos = false 64 noPlaylistVideos = false
@@ -401,7 +401,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
401 } 401 }
402 402
403 private loadPlaylistElements (redirectToFirst = false) { 403 private loadPlaylistElements (redirectToFirst = false) {
404 this.videoService.getPlaylistVideos(this.playlist.id, this.playlistPagination) 404 this.videoService.getPlaylistVideos(this.playlist.uuid, this.playlistPagination)
405 .subscribe(({ totalVideos, videos }) => { 405 .subscribe(({ totalVideos, videos }) => {
406 this.playlistVideos = this.playlistVideos.concat(videos) 406 this.playlistVideos = this.playlistVideos.concat(videos)
407 this.playlistPagination.totalItems = totalVideos 407 this.playlistPagination.totalItems = totalVideos
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss
index 478737a43..28b466c01 100644
--- a/client/src/sass/application.scss
+++ b/client/src/sass/application.scss
@@ -104,7 +104,7 @@ label {
104 background-color: var(--submenuColor); 104 background-color: var(--submenuColor);
105 width: 100%; 105 width: 100%;
106 height: 81px; 106 height: 81px;
107 margin-bottom: 30px; 107 margin-bottom: $sub-menu-margin-bottom;
108 display: flex; 108 display: flex;
109 align-items: center; 109 align-items: center;
110 padding-left: $not-expanded-horizontal-margins; 110 padding-left: $not-expanded-horizontal-margins;
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss
index deabbf6d4..b8eb06f2c 100644
--- a/client/src/sass/include/_variables.scss
+++ b/client/src/sass/include/_variables.scss
@@ -54,6 +54,8 @@ $theater-bottom-space: 115px;
54$input-background-color: $bg-color; 54$input-background-color: $bg-color;
55$input-placeholder-color: #898989; 55$input-placeholder-color: #898989;
56 56
57$sub-menu-margin-bottom: 30px;
58
57/*** map theme ***/ 59/*** map theme ***/
58 60
59// pass variables into a sass map, 61// pass variables into a sass map,