aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+my-library
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/+my-library')
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts70
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html21
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss7
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts7
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts48
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss3
-rw-r--r--client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts4
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.html4
-rw-r--r--client/src/app/+my-library/my-history/my-history.component.scss45
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html2
-rw-r--r--client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss88
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html6
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss46
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html14
-rw-r--r--client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss69
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.html12
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.scss94
-rw-r--r--client/src/app/+my-library/my-videos/my-videos.component.ts14
18 files changed, 303 insertions, 251 deletions
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
index a625493de..b3265210f 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
@@ -8,10 +8,12 @@ import {
8 VIDEO_CHANNEL_SUPPORT_VALIDATOR 8 VIDEO_CHANNEL_SUPPORT_VALIDATOR
9} from '@app/shared/form-validators/video-channel-validators' 9} from '@app/shared/form-validators/video-channel-validators'
10import { FormValidatorService } from '@app/shared/shared-forms' 10import { FormValidatorService } from '@app/shared/shared-forms'
11import { VideoChannelService } from '@app/shared/shared-main' 11import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
12import { VideoChannelCreate } from '@shared/models' 12import { VideoChannelCreate } from '@shared/models'
13import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' 13import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
14import { MyVideoChannelEdit } from './my-video-channel-edit' 14import { MyVideoChannelEdit } from './my-video-channel-edit'
15import { switchMap } from 'rxjs/operators'
16import { of } from 'rxjs'
15 17
16@Component({ 18@Component({
17 templateUrl: './my-video-channel-edit.component.html', 19 templateUrl: './my-video-channel-edit.component.html',
@@ -19,6 +21,10 @@ import { MyVideoChannelEdit } from './my-video-channel-edit'
19}) 21})
20export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements OnInit { 22export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements OnInit {
21 error: string 23 error: string
24 videoChannel = new VideoChannel({})
25
26 private avatar: FormData
27 private banner: FormData
22 28
23 constructor ( 29 constructor (
24 protected formValidatorService: FormValidatorService, 30 protected formValidatorService: FormValidatorService,
@@ -50,23 +56,43 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements
50 support: body.support || null 56 support: body.support || null
51 } 57 }
52 58
53 this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe( 59 this.videoChannelService.createVideoChannel(videoChannelCreate)
54 () => { 60 .pipe(
55 this.authService.refreshUserInformation() 61 switchMap(() => this.uploadAvatar()),
62 switchMap(() => this.uploadBanner())
63 ).subscribe(
64 () => {
65 this.authService.refreshUserInformation()
66
67 this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`)
68 this.router.navigate(['/my-library', 'video-channels'])
69 },
56 70
57 this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`) 71 err => {
58 this.router.navigate([ '/my-library', 'video-channels' ]) 72 if (err.status === HttpStatusCode.CONFLICT_409) {
59 }, 73 this.error = $localize`This name already exists on this instance.`
74 return
75 }
60 76
61 err => { 77 this.error = err.message
62 if (err.status === HttpStatusCode.CONFLICT_409) {
63 this.error = $localize`This name already exists on this instance.`
64 return
65 } 78 }
79 )
80 }
81
82 onAvatarChange (formData: FormData) {
83 this.avatar = formData
84 }
85
86 onAvatarDelete () {
87 this.avatar = null
88 }
89
90 onBannerChange (formData: FormData) {
91 this.banner = formData
92 }
66 93
67 this.error = err.message 94 onBannerDelete () {
68 } 95 this.banner = null
69 )
70 } 96 }
71 97
72 isCreation () { 98 isCreation () {
@@ -76,4 +102,20 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements
76 getFormButtonTitle () { 102 getFormButtonTitle () {
77 return $localize`Create` 103 return $localize`Create`
78 } 104 }
105
106 getUsername () {
107 return this.form.value.name
108 }
109
110 private uploadAvatar () {
111 if (!this.avatar) return of(undefined)
112
113 return this.videoChannelService.changeVideoChannelImage(this.getUsername(), this.avatar, 'avatar')
114 }
115
116 private uploadBanner () {
117 if (!this.banner) return of(undefined)
118
119 return this.videoChannelService.changeVideoChannelImage(this.getUsername(), this.banner, 'banner')
120 }
79} 121}
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html
index 735f9e3ba..2910dffad 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.html
@@ -10,7 +10,7 @@
10 <ng-container *ngIf="!isCreation()"> 10 <ng-container *ngIf="!isCreation()">
11 <li class="breadcrumb-item active" i18n>Edit</li> 11 <li class="breadcrumb-item active" i18n>Edit</li>
12 <li class="breadcrumb-item active" aria-current="page"> 12 <li class="breadcrumb-item active" aria-current="page">
13 <a *ngIf="videoChannelToUpdate" [routerLink]="[ '/my-library/video-channels/update', videoChannelToUpdate?.nameWithHost ]">{{ videoChannelToUpdate?.displayName }}</a> 13 <a *ngIf="videoChannel" [routerLink]="[ '/my-library/video-channels/update', videoChannel?.nameWithHost ]">{{ videoChannel?.displayName }}</a>
14 </li> 14 </li>
15 </ng-container> 15 </ng-container>
16 </ol> 16 </ol>
@@ -23,10 +23,22 @@
23 <div class="form-row"> <!-- channel grid --> 23 <div class="form-row"> <!-- channel grid -->
24 <div class="form-group col-12 col-lg-4 col-xl-3"> 24 <div class="form-group col-12 col-lg-4 col-xl-3">
25 <div *ngIf="isCreation()" class="video-channel-title" i18n>NEW CHANNEL</div> 25 <div *ngIf="isCreation()" class="video-channel-title" i18n>NEW CHANNEL</div>
26 <div *ngIf="!isCreation() && videoChannelToUpdate" class="video-channel-title" i18n>CHANNEL</div> 26 <div *ngIf="!isCreation() && videoChannel" class="video-channel-title" i18n>CHANNEL</div>
27 </div> 27 </div>
28 28
29 <div class="form-group col-12 col-lg-8 col-xl-9"> 29 <div class="form-group col-12 col-lg-8 col-xl-9">
30 <h6 i18n>Banner image of your channel</h6>
31
32 <my-actor-banner-edit
33 *ngIf="videoChannel" [previewImage]="isCreation()"
34 [actor]="videoChannel" (bannerChange)="onBannerChange($event)" (bannerDelete)="onBannerDelete()"
35 ></my-actor-banner-edit>
36
37 <my-actor-avatar-edit
38 *ngIf="videoChannel" [previewImage]="isCreation()"
39 [actor]="videoChannel" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"
40 [displayUsername]="!isCreation()" [displaySubscribers]="!isCreation()"
41 ></my-actor-avatar-edit>
30 42
31 <div class="form-group" *ngIf="isCreation()"> 43 <div class="form-group" *ngIf="isCreation()">
32 <label i18n for="name">Name</label> 44 <label i18n for="name">Name</label>
@@ -44,11 +56,6 @@
44 </div> 56 </div>
45 </div> 57 </div>
46 58
47 <my-actor-avatar-info
48 *ngIf="!isCreation() && videoChannelToUpdate"
49 [actor]="videoChannelToUpdate" (avatarChange)="onAvatarChange($event)" (avatarDelete)="onAvatarDelete()"
50 ></my-actor-avatar-info>
51
52 <div class="form-group"> 59 <div class="form-group">
53 <label i18n for="display-name">Display name</label> 60 <label i18n for="display-name">Display name</label>
54 <input 61 <input
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss
index 8f8af655c..22de103d1 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.component.scss
@@ -10,11 +10,16 @@ label {
10 @include settings-big-title; 10 @include settings-big-title;
11} 11}
12 12
13my-actor-avatar-info { 13my-actor-avatar-edit,
14my-actor-banner-edit {
14 display: block; 15 display: block;
15 margin-bottom: 20px; 16 margin-bottom: 20px;
16} 17}
17 18
19my-actor-banner-edit {
20 max-width: 500px;
21}
22
18.input-group { 23.input-group {
19 @include peertube-input-group(fit-content); 24 @include peertube-input-group(fit-content);
20} 25}
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts
index 3e20a27ee..33bb90f14 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-edit.ts
@@ -2,8 +2,7 @@ import { FormReactive } from '@app/shared/shared-forms'
2import { VideoChannel } from '@app/shared/shared-main' 2import { VideoChannel } from '@app/shared/shared-main'
3 3
4export abstract class MyVideoChannelEdit extends FormReactive { 4export abstract class MyVideoChannelEdit extends FormReactive {
5 // We need it even in the create component because it's used in the edit template 5 videoChannel: VideoChannel
6 videoChannelToUpdate: VideoChannel
7 6
8 abstract isCreation (): boolean 7 abstract isCreation (): boolean
9 abstract getFormButtonTitle (): string 8 abstract getFormButtonTitle (): string
@@ -12,10 +11,6 @@ export abstract class MyVideoChannelEdit extends FormReactive {
12 return window.location.host 11 return window.location.host
13 } 12 }
14 13
15 // We need this method so angular does not complain in child template that doesn't need this
16 onAvatarChange (formData: FormData) { /* empty */ }
17 onAvatarDelete () { /* empty */ }
18
19 // Should be implemented by the child 14 // Should be implemented by the child
20 isBulkUpdateVideosDisplayed () { 15 isBulkUpdateVideosDisplayed () {
21 return false 16 return false
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts b/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts
index 6cd1ff503..a29af176c 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts
@@ -1,7 +1,9 @@
1import { Subscription } from 'rxjs' 1import { Subscription } from 'rxjs'
2import { HttpErrorResponse } from '@angular/common/http'
2import { Component, OnDestroy, OnInit } from '@angular/core' 3import { Component, OnDestroy, OnInit } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, Notifier, ServerService } from '@app/core' 5import { AuthService, Notifier, ServerService } from '@app/core'
6import { uploadErrorHandler } from '@app/helpers'
5import { 7import {
6 VIDEO_CHANNEL_DESCRIPTION_VALIDATOR, 8 VIDEO_CHANNEL_DESCRIPTION_VALIDATOR,
7 VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, 9 VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR,
@@ -11,8 +13,6 @@ import { FormValidatorService } from '@app/shared/shared-forms'
11import { VideoChannel, VideoChannelService } from '@app/shared/shared-main' 13import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
12import { ServerConfig, VideoChannelUpdate } from '@shared/models' 14import { ServerConfig, VideoChannelUpdate } from '@shared/models'
13import { MyVideoChannelEdit } from './my-video-channel-edit' 15import { MyVideoChannelEdit } from './my-video-channel-edit'
14import { HttpErrorResponse } from '@angular/common/http'
15import { uploadErrorHandler } from '@app/helpers'
16 16
17@Component({ 17@Component({
18 selector: 'my-video-channel-update', 18 selector: 'my-video-channel-update',
@@ -21,7 +21,7 @@ import { uploadErrorHandler } from '@app/helpers'
21}) 21})
22export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements OnInit, OnDestroy { 22export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements OnInit, OnDestroy {
23 error: string 23 error: string
24 videoChannelToUpdate: VideoChannel 24 videoChannel: VideoChannel
25 25
26 private paramsSub: Subscription 26 private paramsSub: Subscription
27 private oldSupportField: string 27 private oldSupportField: string
@@ -56,7 +56,7 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
56 56
57 this.videoChannelService.getVideoChannel(videoChannelId).subscribe( 57 this.videoChannelService.getVideoChannel(videoChannelId).subscribe(
58 videoChannelToUpdate => { 58 videoChannelToUpdate => {
59 this.videoChannelToUpdate = videoChannelToUpdate 59 this.videoChannel = videoChannelToUpdate
60 60
61 this.oldSupportField = videoChannelToUpdate.support 61 this.oldSupportField = videoChannelToUpdate.support
62 62
@@ -87,7 +87,7 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
87 bulkVideosSupportUpdate: body.bulkVideosSupportUpdate || false 87 bulkVideosSupportUpdate: body.bulkVideosSupportUpdate || false
88 } 88 }
89 89
90 this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.name, videoChannelUpdate).subscribe( 90 this.videoChannelService.updateVideoChannel(this.videoChannel.name, videoChannelUpdate).subscribe(
91 () => { 91 () => {
92 this.authService.refreshUserInformation() 92 this.authService.refreshUserInformation()
93 93
@@ -101,12 +101,12 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
101 } 101 }
102 102
103 onAvatarChange (formData: FormData) { 103 onAvatarChange (formData: FormData) {
104 this.videoChannelService.changeVideoChannelAvatar(this.videoChannelToUpdate.name, formData) 104 this.videoChannelService.changeVideoChannelImage(this.videoChannel.name, formData, 'avatar')
105 .subscribe( 105 .subscribe(
106 data => { 106 data => {
107 this.notifier.success($localize`Avatar changed.`) 107 this.notifier.success($localize`Avatar changed.`)
108 108
109 this.videoChannelToUpdate.updateAvatar(data.avatar) 109 this.videoChannel.updateAvatar(data.avatar)
110 }, 110 },
111 111
112 (err: HttpErrorResponse) => uploadErrorHandler({ 112 (err: HttpErrorResponse) => uploadErrorHandler({
@@ -118,12 +118,42 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
118 } 118 }
119 119
120 onAvatarDelete () { 120 onAvatarDelete () {
121 this.videoChannelService.deleteVideoChannelAvatar(this.videoChannelToUpdate.name) 121 this.videoChannelService.deleteVideoChannelImage(this.videoChannel.name, 'avatar')
122 .subscribe( 122 .subscribe(
123 data => { 123 data => {
124 this.notifier.success($localize`Avatar deleted.`) 124 this.notifier.success($localize`Avatar deleted.`)
125 125
126 this.videoChannelToUpdate.resetAvatar() 126 this.videoChannel.resetAvatar()
127 },
128
129 err => this.notifier.error(err.message)
130 )
131 }
132
133 onBannerChange (formData: FormData) {
134 this.videoChannelService.changeVideoChannelImage(this.videoChannel.name, formData, 'banner')
135 .subscribe(
136 data => {
137 this.notifier.success($localize`Banner changed.`)
138
139 this.videoChannel.updateBanner(data.banner)
140 },
141
142 (err: HttpErrorResponse) => uploadErrorHandler({
143 err,
144 name: $localize`banner`,
145 notifier: this.notifier
146 })
147 )
148 }
149
150 onBannerDelete () {
151 this.videoChannelService.deleteVideoChannelImage(this.videoChannel.name, 'banner')
152 .subscribe(
153 data => {
154 this.notifier.success($localize`Banner deleted.`)
155
156 this.videoChannel.resetBanner()
127 }, 157 },
128 158
129 err => this.notifier.error(err.message) 159 err => this.notifier.error(err.message)
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
index f2f42459f..8804fa95c 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.component.scss
@@ -17,10 +17,11 @@ input[type=text] {
17 17
18.video-channel { 18.video-channel {
19 @include row-blocks; 19 @include row-blocks;
20
20 padding-bottom: 0; 21 padding-bottom: 0;
21 22
22 img { 23 img {
23 @include avatar(80px); 24 @include channel-avatar(80px);
24 25
25 margin-right: 10px; 26 margin-right: 10px;
26 } 27 }
diff --git a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts
index 92b56db49..53557ca02 100644
--- a/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts
+++ b/client/src/app/+my-library/+my-video-channels/my-video-channels.module.ts
@@ -1,5 +1,6 @@
1import { ChartModule } from 'primeng/chart' 1import { ChartModule } from 'primeng/chart'
2import { NgModule } from '@angular/core' 2import { NgModule } from '@angular/core'
3import { SharedActorImageModule } from '@app/shared/shared-actor-image'
3import { SharedFormModule } from '@app/shared/shared-forms' 4import { SharedFormModule } from '@app/shared/shared-forms'
4import { SharedGlobalIconModule } from '@app/shared/shared-icons' 5import { SharedGlobalIconModule } from '@app/shared/shared-icons'
5import { SharedMainModule } from '@app/shared/shared-main' 6import { SharedMainModule } from '@app/shared/shared-main'
@@ -16,7 +17,8 @@ import { MyVideoChannelsComponent } from './my-video-channels.component'
16 17
17 SharedMainModule, 18 SharedMainModule,
18 SharedFormModule, 19 SharedFormModule,
19 SharedGlobalIconModule 20 SharedGlobalIconModule,
21 SharedActorImageModule
20 ], 22 ],
21 23
22 declarations: [ 24 declarations: [
diff --git a/client/src/app/+my-library/my-history/my-history.component.html b/client/src/app/+my-library/my-history/my-history.component.html
index c180161e7..9dec64645 100644
--- a/client/src/app/+my-library/my-history/my-history.component.html
+++ b/client/src/app/+my-library/my-history/my-history.component.html
@@ -4,7 +4,7 @@
4</h1> 4</h1>
5 5
6<div class="top-buttons"> 6<div class="top-buttons">
7 <div> 7 <div class="search-wrapper">
8 <div class="input-group has-feedback has-clear"> 8 <div class="input-group has-feedback has-clear">
9 <input 9 <input
10 type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history" 10 type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history"
@@ -15,7 +15,7 @@
15 </div> 15 </div>
16 </div> 16 </div>
17 17
18 <div class="history-switch ml-auto mr-3"> 18 <div class="history-switch">
19 <my-input-switch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch> 19 <my-input-switch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch>
20 <label i18n>Track watch history</label> 20 <label i18n>Track watch history</label>
21 </div> 21 </div>
diff --git a/client/src/app/+my-library/my-history/my-history.component.scss b/client/src/app/+my-library/my-history/my-history.component.scss
index 928a8a3da..af4a34b4b 100644
--- a/client/src/app/+my-library/my-history/my-history.component.scss
+++ b/client/src/app/+my-library/my-history/my-history.component.scss
@@ -11,16 +11,24 @@
11 11
12.top-buttons { 12.top-buttons {
13 margin-bottom: 30px; 13 margin-bottom: 30px;
14 display: flex; 14 display: grid;
15 grid-template-columns: 250px 1fr auto auto;
15 align-items: center; 16 align-items: center;
16 flex-wrap: wrap;
17 17
18 #history-search { 18 .search-wrapper {
19 @include peertube-input-text(250px); 19 grid-column: 1;
20
21 input {
22 @include peertube-input-text(250px);
23 }
20 } 24 }
21 25
22 .history-switch { 26 .history-switch {
27 grid-column: 3;
28
23 display: flex; 29 display: flex;
30 margin-left: auto;
31 margin-right: 15px;
24 32
25 label { 33 label {
26 margin: 0 0 0 5px; 34 margin: 0 0 0 5px;
@@ -31,6 +39,8 @@
31 } 39 }
32 40
33 .delete-history { 41 .delete-history {
42 grid-column: 4;
43
34 @include peertube-button; 44 @include peertube-button;
35 @include grey-button; 45 @include grey-button;
36 @include button-with-icon; 46 @include button-with-icon;
@@ -40,26 +50,27 @@
40} 50}
41 51
42.video { 52.video {
43 @include row-blocks; 53 @include row-blocks($column-responsive: false);
44
45 .my-video-miniature {
46 flex-grow: 1;
47 }
48} 54}
49 55
50@media screen and (max-width: $mobile-view) { 56@media screen and (max-width: $small-view) {
51 .top-buttons { 57 .top-buttons {
52 .history-switch label, .delete-history { 58 grid-template-columns: auto 1fr auto;
53 @include ellipsis; 59 row-gap: 20px;
54 }
55 60
56 .history-switch label { 61 .history-switch {
57 width: 60%; 62 grid-row: 1;
63 grid-column: 1;
64 margin: 0;
58 } 65 }
59 66
60 .delete-history { 67 .delete-history {
61 margin-left: auto; 68 grid-row: 1;
62 max-width: 32%; 69 grid-column: 3;
70 }
71
72 .search-wrapper {
73 grid-column: 1 / 4;
63 } 74 }
64 } 75 }
65} 76}
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
index 510b400c0..ff448ad87 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
+++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.html
@@ -6,7 +6,7 @@
6 </span> 6 </span>
7</h1> 7</h1>
8 8
9<div class="video-subscriptions-header d-flex justify-content-between"> 9<div class="video-subscriptions-header">
10 <div class="has-feedback has-clear"> 10 <div class="has-feedback has-clear">
11 <input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch" 11 <input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch"
12 (ngModelChange)="onSubscriptionsSearchChanged()" /> 12 (ngModelChange)="onSubscriptionsSearchChanged()" />
diff --git a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
index 5ead45dd8..3c1a4d2ad 100644
--- a/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
+++ b/client/src/app/+my-library/my-subscriptions/my-subscriptions.component.scss
@@ -9,40 +9,40 @@ input[type=text] {
9 @include row-blocks; 9 @include row-blocks;
10 10
11 img { 11 img {
12 @include avatar(80px); 12 @include channel-avatar(80px);
13 13
14 margin-right: 10px; 14 margin-right: 10px;
15 } 15 }
16}
16 17
17 .video-channel-info { 18.video-channel-info {
18 flex-grow: 1; 19 flex-grow: 1;
19 20
20 a.video-channel-names { 21 a.video-channel-names {
21 @include disable-default-a-behaviour; 22 @include disable-default-a-behaviour;
22 23
23 width: fit-content; 24 width: fit-content;
24 display: flex; 25 display: flex;
25 align-items: baseline; 26 align-items: baseline;
26 color: pvar(--mainForegroundColor); 27 color: pvar(--mainForegroundColor);
27 28
28 .video-channel-display-name { 29 .video-channel-display-name {
29 font-weight: $font-semibold; 30 font-weight: $font-semibold;
30 font-size: 18px; 31 font-size: 18px;
31 } 32 }
32 33
33 .video-channel-name { 34 .video-channel-name {
34 font-size: 14px; 35 font-size: 14px;
35 color: $grey-actor-name; 36 color: $grey-actor-name;
36 margin-left: 5px; 37 margin-left: 5px;
37 }
38 } 38 }
39 } 39 }
40}
40 41
41 .actor-owner { 42.actor-owner {
42 @include actor-owner; 43 @include actor-owner;
43 44
44 margin-top: 0; 45 margin-top: 0;
45 }
46} 46}
47 47
48.video-subscriptions-header { 48.video-subscriptions-header {
@@ -50,32 +50,22 @@ input[type=text] {
50} 50}
51 51
52@media screen and (max-width: $small-view) { 52@media screen and (max-width: $small-view) {
53 .video-channel { 53 .video-subscriptions-header input[type=text] {
54 .video-channel-info { 54 width: 100% !important;
55 padding-bottom: 10px;
56 text-align: center;
57
58 .video-channel-names {
59 flex-direction: column;
60 align-items: center !important;
61 margin: auto;
62 }
63 }
64
65 img {
66 margin-right: 0;
67 }
68 } 55 }
69}
70 56
71@media screen and (max-width: $mobile-view) { 57 .video-channel-info {
72 .video-subscriptions-header { 58 padding-bottom: 10px;
73 flex-direction: column; 59 text-align: center;
74 60
75 input[type=text] { 61 .video-channel-names {
76 width: 100% !important; 62 flex-direction: column;
63 align-items: center !important;
64 margin: auto;
77 } 65 }
78 } 66 }
79}
80
81 67
68 img {
69 margin-right: 0;
70 }
71}
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html
index a97b2b4fb..e7e3c17b3 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.html
@@ -1,6 +1,6 @@
1<div class="row"> 1<div class="root">
2 2
3 <div class="playlist-info col-xs-12 col-md-5 col-xl-3"> 3 <div class="playlist-info">
4 <my-video-playlist-miniature 4 <my-video-playlist-miniature
5 *ngIf="playlist" [playlist]="playlist" [toManage]="false" [displayChannel]="true" 5 *ngIf="playlist" [playlist]="playlist" [toManage]="false" [displayChannel]="true"
6 [displayDescription]="true" [displayPrivacy]="true" 6 [displayDescription]="true" [displayPrivacy]="true"
@@ -20,7 +20,7 @@
20 20
21 </div> 21 </div>
22 22
23 <div class="playlist-elements col-xs-12 col-md-7 col-xl-9"> 23 <div class="playlist-elements">
24 <div class="no-results" *ngIf="pagination.totalItems === 0"> 24 <div class="no-results" *ngIf="pagination.totalItems === 0">
25 <div i18n>No videos in this playlist.</div> 25 <div i18n>No videos in this playlist.</div>
26 26
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss
index de7e1993f..0c68dedf6 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.scss
@@ -2,21 +2,25 @@
2@import '_mixins'; 2@import '_mixins';
3@import '_miniature'; 3@import '_miniature';
4 4
5.root {
6 display: grid;
7 grid-template-columns: auto 1fr;
8}
9
5.playlist-info { 10.playlist-info {
6 background-color: pvar(--submenuColor); 11 grid-column: 1;
7 margin-left: -$not-expanded-horizontal-margins; 12 background-color: pvar(--submenuBackgroundColor);
13 margin-left: calc(#{pvar(--horizontalMarginContent)} * -1);
8 margin-top: -$sub-menu-margin-bottom; 14 margin-top: -$sub-menu-margin-bottom;
9 15
10 padding: 10px; 16 padding: 15px;
11 17
12 display: flex; 18 display: flex;
13 flex-direction: column; 19 flex-direction: column;
14 justify-content: flex-start;
15 align-items: center;
16 20
17 /* fix ellipsis dots background color */ 21 /* fix ellipsis dots background color */
18 ::ng-deep .miniature-name::after { 22 ::ng-deep .miniature-name::after {
19 background-color: pvar(--submenuColor) !important; 23 background-color: pvar(--submenuBackgroundColor) !important;
20 } 24 }
21} 25}
22 26
@@ -59,15 +63,35 @@
59 transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 63 transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
60} 64}
61 65
62@media screen and (max-width: $small-view) { 66.playlist-elements {
67 grid-column: 2;
68}
69
70my-video-playlist-miniature {
71 width: $video-thumbnail-width;
72}
73
74@include on-small-main-col {
75 my-video-playlist-miniature {
76 width: $video-thumbnail-medium-width;
77 }
78}
79
80@include on-mobile-main-col {
81 .root {
82 display: block;
83 }
84
63 .playlist-info { 85 .playlist-info {
64 width: 100vw; 86 width: calc(100% + (2 * var(--horizontalMarginContent)));
65 padding-top: 20px; 87 padding-top: 20px;
66 margin-left: calc(#{var(--expanded-horizontal-margin-content)} * -1); 88 margin-bottom: 10px;
67 } 89 }
68 90
69 .playlist-elements { 91 my-video-playlist-miniature,
70 padding: 0 !important; 92 .playlist-buttons {
93 margin-left: auto;
94 margin-right: auto;
71 } 95 }
72 96
73 ::ng-deep my-video-playlist-element-miniature { 97 ::ng-deep my-video-playlist-element-miniature {
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
index afcf6a084..b88ea3db7 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.html
@@ -1,8 +1,6 @@
1<h1> 1<h1>
2 <span> 2 <my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon>
3 <my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon> 3 <ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span>
4 <ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span>
5 </span>
6</h1> 4</h1>
7 5
8<div class="video-playlists-header d-flex justify-content-between"> 6<div class="video-playlists-header d-flex justify-content-between">
@@ -21,10 +19,10 @@
21 19
22<div class="video-playlists" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()"> 20<div class="video-playlists" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
23 <div *ngFor="let playlist of videoPlaylists" class="video-playlist"> 21 <div *ngFor="let playlist of videoPlaylists" class="video-playlist">
24 <div class="miniature-wrapper"> 22 <my-video-playlist-miniature
25 <my-video-playlist-miniature [playlist]="playlist" [toManage]="true" [displayChannel]="true" [displayDescription]="true" [displayPrivacy]="true" 23 [playlist]="playlist" [toManage]="true" [displayChannel]="true"
26 ></my-video-playlist-miniature> 24 [displayDescription]="true" [displayPrivacy]="true" [displayAsRow]="true"
27 </div> 25 ></my-video-playlist-miniature>
28 26
29 <div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons"> 27 <div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons">
30 <my-delete-button label (click)="deleteVideoPlaylist(playlist)"></my-delete-button> 28 <my-delete-button label (click)="deleteVideoPlaylist(playlist)"></my-delete-button>
diff --git a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss
index 2b7c88246..94187efd4 100644
--- a/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss
+++ b/client/src/app/+my-library/my-video-playlists/my-video-playlists.component.scss
@@ -1,6 +1,10 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4h1 {
5 display: flex;
6}
7
4.create-button { 8.create-button {
5 @include create-button; 9 @include create-button;
6} 10}
@@ -9,64 +13,45 @@ input[type=text] {
9 @include peertube-input-text(300px); 13 @include peertube-input-text(300px);
10} 14}
11 15
12::ng-deep .action-button {
13 &.action-button-delete {
14 margin-right: 10px;
15 }
16}
17
18.video-playlist { 16.video-playlist {
19 @include row-blocks; 17 @include row-blocks($column-responsive: false);
20 18}
21 .miniature-wrapper {
22 flex-grow: 1;
23
24 ::ng-deep .miniature {
25 display: flex;
26
27 .miniature-info {
28 margin-left: 10px;
29 width: auto;
30 }
31 }
32 }
33 19
34 .video-playlist-buttons { 20.video-playlist-buttons {
35 min-width: 190px; 21 display: flex;
36 height: max-content; 22 margin-left: 10px;
37 } 23 align-self: flex-end;
38} 24}
39 25
40.video-playlists-header { 26.video-playlists-header {
41 margin-bottom: 30px; 27 margin-bottom: 30px;
42} 28}
43 29
44@media screen and (max-width: $small-view) { 30my-video-playlist-miniature {
31 display: block;
32 flex-grow: 1;
33}
34
35my-delete-button {
36 margin-right: 10px;
37}
38
39@include on-small-main-col {
45 .video-playlists-header { 40 .video-playlists-header {
46 text-align: center; 41 text-align: center;
47 } 42 }
48 43
49 .video-playlist { 44 .video-playlist {
50 45 flex-wrap: wrap;
51 .video-playlist-buttons {
52 margin-top: 10px;
53 }
54 } 46 }
55 47
56 my-video-playlist-miniature ::ng-deep .miniature { 48 .video-playlist-buttons {
57 flex-direction: column; 49 margin-top: 10px;
58 50 margin-left: auto;
59 .miniature-info {
60 margin-left: 0 !important;
61 }
62
63 .miniature-name {
64 max-width: $video-thumbnail-width;
65 }
66 } 51 }
67} 52}
68 53
69@media screen and (max-width: $mobile-view) { 54@include on-mobile-main-col {
70 .video-playlists-header { 55 .video-playlists-header {
71 flex-direction: column; 56 flex-direction: column;
72 57
@@ -75,4 +60,8 @@ input[type=text] {
75 margin-bottom: 12px; 60 margin-bottom: 12px;
76 } 61 }
77 } 62 }
63
64 .action-button {
65 margin-left: 0;
66 }
78} 67}
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html
index 5fa4c02ec..e9f436378 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.html
+++ b/client/src/app/+my-library/my-videos/my-videos.component.html
@@ -25,6 +25,17 @@
25 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a> 25 <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="resetSearch()"></a>
26 <span class="sr-only" i18n>Clear filters</span> 26 <span class="sr-only" i18n>Clear filters</span>
27 </div> 27 </div>
28
29 <div class="peertube-select-container peertube-select-button">
30 <select [(ngModel)]="sort" (ngModelChange)="onChangeSortColumn()" class="form-control">
31 <option value="undefined" disabled>Sort by</option>
32 <option value="-publishedAt" i18n>Last published first</option>
33 <option value="-createdAt" i18n>Last created first</option>
34 <option value="-views" i18n>Most viewed first</option>
35 <option value="-likes" i18n>Most liked first</option>
36 <option value="-duration" i18n>Longest first</option>
37 </select>
38 </div>
28</div> 39</div>
29 40
30<my-videos-selection 41<my-videos-selection
@@ -34,7 +45,6 @@
34 [miniatureDisplayOptions]="miniatureDisplayOptions" 45 [miniatureDisplayOptions]="miniatureDisplayOptions"
35 [titlePage]="titlePage" 46 [titlePage]="titlePage"
36 [getVideosObservableFunction]="getVideosObservableFunction" 47 [getVideosObservableFunction]="getVideosObservableFunction"
37 [ownerDisplayType]="ownerDisplayType"
38 [user]="user" 48 [user]="user"
39 #videosSelection 49 #videosSelection
40> 50>
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.scss b/client/src/app/+my-library/my-videos/my-videos.component.scss
index 59fc5fe80..aaf21126b 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.scss
+++ b/client/src/app/+my-library/my-videos/my-videos.component.scss
@@ -5,6 +5,11 @@ input[type=text] {
5 @include peertube-input-text(300px); 5 @include peertube-input-text(300px);
6} 6}
7 7
8.peertube-select-container {
9 @include peertube-select-container(auto);
10 margin-left: 0.5rem;
11}
12
8h1 { 13h1 {
9 display: flex; 14 display: flex;
10 justify-content: space-between; 15 justify-content: space-between;
@@ -32,36 +37,9 @@ h1 {
32 } 37 }
33} 38}
34 39
35::ng-deep {
36 .video {
37 flex-wrap: wrap;
38 }
39
40 .action-button span {
41 white-space: nowrap;
42 }
43
44 .video-miniature {
45 &.display-as-row {
46 // width: min-content !important;
47 width: 100% !important;
48
49 .video-bottom .video-miniature-information {
50 width: max-content !important;
51 min-width: unset !important;
52 }
53 }
54
55 .video-bottom {
56 max-width: 350px;
57 }
58 }
59}
60
61.action-button { 40.action-button {
62 display: flex; 41 display: flex;
63 margin-left: 55px; 42 margin-left: 10px;
64 margin-top: 10px;
65 align-self: flex-end; 43 align-self: flex-end;
66} 44}
67 45
@@ -69,7 +47,7 @@ my-edit-button {
69 margin-right: 10px; 47 margin-right: 10px;
70} 48}
71 49
72@media screen and (max-width: $small-view) { 50@include on-small-main-col {
73 h1 { 51 h1 {
74 flex-direction: column; 52 flex-direction: column;
75 53
@@ -80,59 +58,25 @@ my-edit-button {
80 } 58 }
81 59
82 .action-button { 60 .action-button {
83 flex-direction: column; 61 margin-top: 10px;
84 align-self: center; 62 margin-left: auto;
85 align-items: center;
86 margin-left: 0px;
87 }
88
89 my-edit-button {
90 margin: 15px 0 5px 0;
91 width: 100%;
92 text-align: center;
93
94 ::ng-deep {
95 .action-button {
96 /* same width than a.video-thumbnail */
97 width: $video-thumbnail-width;
98 }
99 }
100 }
101
102 ::ng-deep {
103 .video-miniature {
104 align-items: center;
105
106 .video-bottom,
107 .video-bottom .video-miniature-information {
108 /* same width than a.video-thumbnail */
109 max-width: $video-thumbnail-width !important;
110 }
111 }
112 } 63 }
113} 64}
114 65
115// Adapt my-video-miniature on small screens with menu 66@include on-mobile-main-col {
116@media screen and (min-width: $small-view) and (max-width: #{breakpoint(lg) + ($not-expanded-horizontal-margins / 3) * 2}) {
117 :host-context(.main-col:not(.expanded)) {
118 ::ng-deep {
119 .video-miniature {
120 flex-direction: column;
121
122 .video-miniature-name {
123 max-width: $video-thumbnail-width;
124 }
125 }
126 }
127 }
128}
129
130@media screen and (max-width: $mobile-view) {
131 .videos-header { 67 .videos-header {
132 flex-direction: column; 68 flex-direction: column;
133 69
134 input[type=text] { 70 input[type=text] {
135 width: 100% !important; 71 width: 100%;
72 margin-bottom: 12px;
73 }
74 .peertube-select-container {
75 margin-left: 0;
136 } 76 }
137 } 77 }
78
79 .action-button {
80 margin-left: 0;
81 }
138} 82}
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts
index 6a2a62608..356e158d6 100644
--- a/client/src/app/+my-library/my-videos/my-videos.component.ts
+++ b/client/src/app/+my-library/my-videos/my-videos.component.ts
@@ -2,12 +2,12 @@ import { concat, Observable, Subject } from 'rxjs'
2import { debounceTime, tap, toArray } from 'rxjs/operators' 2import { debounceTime, tap, toArray } from 'rxjs/operators'
3import { Component, OnInit, ViewChild } from '@angular/core' 3import { Component, OnInit, ViewChild } from '@angular/core'
4import { ActivatedRoute, Router } from '@angular/router' 4import { ActivatedRoute, Router } from '@angular/router'
5import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User, UserService } from '@app/core' 5import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core'
6import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' 6import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
7import { immutableAssign } from '@app/helpers' 7import { immutableAssign } from '@app/helpers'
8import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' 8import { DropdownAction, Video, VideoService } from '@app/shared/shared-main'
9import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' 9import { LiveStreamInformationComponent } from '@app/shared/shared-video-live'
10import { MiniatureDisplayOptions, OwnerDisplayType, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature' 10import { MiniatureDisplayOptions, SelectionType, VideosSelectionComponent } from '@app/shared/shared-video-miniature'
11import { VideoSortField } from '@shared/models' 11import { VideoSortField } from '@shared/models'
12import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component' 12import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component'
13 13
@@ -36,7 +36,6 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
36 state: true, 36 state: true,
37 blacklistInfo: true 37 blacklistInfo: true
38 } 38 }
39 ownerDisplayType: OwnerDisplayType = 'videoChannel'
40 39
41 videoActions: DropdownAction<{ video: Video }>[] = [] 40 videoActions: DropdownAction<{ video: Video }>[] = []
42 41
@@ -44,6 +43,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
44 videosSearch: string 43 videosSearch: string
45 videosSearchChanged = new Subject<string>() 44 videosSearchChanged = new Subject<string>()
46 getVideosObservableFunction = this.getVideosObservable.bind(this) 45 getVideosObservableFunction = this.getVideosObservable.bind(this)
46 sort: VideoSortField = '-publishedAt'
47 47
48 user: User 48 user: User
49 49
@@ -81,6 +81,10 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
81 this.videosSearchChanged.next() 81 this.videosSearchChanged.next()
82 } 82 }
83 83
84 onChangeSortColumn () {
85 this.videosSelection.reloadVideos()
86 }
87
84 disableForReuse () { 88 disableForReuse () {
85 this.videosSelection.disableForReuse() 89 this.videosSelection.disableForReuse()
86 } 90 }
@@ -89,10 +93,10 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
89 this.videosSelection.enabledForReuse() 93 this.videosSelection.enabledForReuse()
90 } 94 }
91 95
92 getVideosObservable (page: number, sort: VideoSortField) { 96 getVideosObservable (page: number) {
93 const newPagination = immutableAssign(this.pagination, { currentPage: page }) 97 const newPagination = immutableAssign(this.pagination, { currentPage: page })
94 98
95 return this.videoService.getMyVideos(newPagination, sort, this.videosSearch) 99 return this.videoService.getMyVideos(newPagination, this.sort, this.videosSearch)
96 .pipe( 100 .pipe(
97 tap(res => this.pagination.totalItems = res.total) 101 tap(res => this.pagination.totalItems = res.total)
98 ) 102 )