aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+my-account
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-03-06 15:36:44 +0100
committerChocobozzz <chocobozzz@cpy.re>2019-03-18 11:17:59 +0100
commit830b4faff15fb9c81d88e8e69fcdf94aad32bef8 (patch)
tree53de6c9e30ce88734b4bdda62016e0498fe78491 /client/src/app/+my-account
parentd4c9f45b31eda0b7a391ddc83eb290ca5cba311f (diff)
downloadPeerTube-830b4faff15fb9c81d88e8e69fcdf94aad32bef8.tar.gz
PeerTube-830b4faff15fb9c81d88e8e69fcdf94aad32bef8.tar.zst
PeerTube-830b4faff15fb9c81d88e8e69fcdf94aad32bef8.zip
Add/update/delete/list my playlists
Diffstat (limited to 'client/src/app/+my-account')
-rw-r--r--client/src/app/+my-account/my-account-routing.module.ts37
-rw-r--r--client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts4
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html2
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts89
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html64
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.scss27
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts13
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts132
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html20
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss50
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts85
-rw-r--r--client/src/app/+my-account/my-account.component.ts4
-rw-r--r--client/src/app/+my-account/my-account.module.ts13
13 files changed, 535 insertions, 5 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 9996218ca..0193afff7 100644
--- a/client/src/app/+my-account/my-account-routing.module.ts
+++ b/client/src/app/+my-account/my-account-routing.module.ts
@@ -15,6 +15,13 @@ import { MyAccountBlocklistComponent } from '@app/+my-account/my-account-blockli
15import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-server-blocklist.component' 15import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-server-blocklist.component'
16import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/my-account-history.component' 16import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/my-account-history.component'
17import { MyAccountNotificationsComponent } from '@app/+my-account/my-account-notifications/my-account-notifications.component' 17import { MyAccountNotificationsComponent } from '@app/+my-account/my-account-notifications/my-account-notifications.component'
18import { MyAccountVideoPlaylistsComponent } from '@app/+my-account/my-account-video-playlists/my-account-video-playlists.component'
19import {
20 MyAccountVideoPlaylistCreateComponent
21} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component'
22import {
23 MyAccountVideoPlaylistUpdateComponent
24} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component'
18 25
19const myAccountRoutes: Routes = [ 26const myAccountRoutes: Routes = [
20 { 27 {
@@ -36,6 +43,7 @@ const myAccountRoutes: Routes = [
36 } 43 }
37 } 44 }
38 }, 45 },
46
39 { 47 {
40 path: 'video-channels', 48 path: 'video-channels',
41 component: MyAccountVideoChannelsComponent, 49 component: MyAccountVideoChannelsComponent,
@@ -63,6 +71,35 @@ const myAccountRoutes: Routes = [
63 } 71 }
64 } 72 }
65 }, 73 },
74
75 {
76 path: 'video-playlists',
77 component: MyAccountVideoPlaylistsComponent,
78 data: {
79 meta: {
80 title: 'Account playlists'
81 }
82 }
83 },
84 {
85 path: 'video-playlists/create',
86 component: MyAccountVideoPlaylistCreateComponent,
87 data: {
88 meta: {
89 title: 'Create new playlist'
90 }
91 }
92 },
93 {
94 path: 'video-playlists/update/:videoPlaylistId',
95 component: MyAccountVideoPlaylistUpdateComponent,
96 data: {
97 meta: {
98 title: 'Update playlist'
99 }
100 }
101 },
102
66 { 103 {
67 path: 'videos', 104 path: 'videos',
68 component: MyAccountVideosComponent, 105 component: MyAccountVideosComponent,
diff --git a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts
index 9d2dccdf0..6ce22989b 100644
--- a/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts
+++ b/client/src/app/+my-account/my-account-subscriptions/my-account-subscriptions.component.ts
@@ -1,7 +1,6 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { Notifier } from '@app/core' 2import { Notifier } from '@app/core'
3import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 3import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { UserSubscriptionService } from '@app/shared/user-subscription' 4import { UserSubscriptionService } from '@app/shared/user-subscription'
6import { ComponentPagination } from '@app/shared/rest/component-pagination.model' 5import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
7 6
@@ -21,8 +20,7 @@ export class MyAccountSubscriptionsComponent implements OnInit {
21 20
22 constructor ( 21 constructor (
23 private userSubscriptionService: UserSubscriptionService, 22 private userSubscriptionService: UserSubscriptionService,
24 private notifier: Notifier, 23 private notifier: Notifier
25 private i18n: I18n
26 ) {} 24 ) {}
27 25
28 ngOnInit () { 26 ngOnInit () {
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
index 51db2e75d..11e87ba79 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html
@@ -1,7 +1,7 @@
1<div class="video-channels-header"> 1<div class="video-channels-header">
2 <a class="create-button" routerLink="create"> 2 <a class="create-button" routerLink="create">
3 <my-global-icon iconName="add"></my-global-icon> 3 <my-global-icon iconName="add"></my-global-icon>
4 <ng-container i18n>Create another video channel</ng-container> 4 <ng-container i18n>Create a new video channel</ng-container>
5 </a> 5 </a>
6</div> 6</div>
7 7
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
new file mode 100644
index 000000000..61b61e221
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
@@ -0,0 +1,89 @@
1import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router'
3import { AuthService, Notifier, ServerService } from '@app/core'
4import { MyAccountVideoPlaylistEdit } from './my-account-video-playlist-edit'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
7import { VideoPlaylistValidatorsService } from '@app/shared'
8import { VideoPlaylistCreate } from '@shared/models/videos/playlist/video-playlist-create.model'
9import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
10import { VideoConstant } from '@shared/models'
11import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model'
12import { populateAsyncUserVideoChannels } from '@app/shared/misc/utils'
13
14@Component({
15 selector: 'my-account-video-playlist-create',
16 templateUrl: './my-account-video-playlist-edit.component.html',
17 styleUrls: [ './my-account-video-playlist-edit.component.scss' ]
18})
19export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylistEdit implements OnInit {
20 error: string
21 videoPlaylistPrivacies: VideoConstant<VideoPlaylistPrivacy>[] = []
22
23 constructor (
24 protected formValidatorService: FormValidatorService,
25 private authService: AuthService,
26 private videoPlaylistValidatorsService: VideoPlaylistValidatorsService,
27 private notifier: Notifier,
28 private router: Router,
29 private videoPlaylistService: VideoPlaylistService,
30 private serverService: ServerService,
31 private i18n: I18n
32 ) {
33 super()
34 }
35
36 ngOnInit () {
37 this.buildForm({
38 'display-name': this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_DISPLAY_NAME,
39 privacy: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_PRIVACY,
40 description: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_DESCRIPTION,
41 videoChannelId: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_CHANNEL_ID,
42 thumbnailfile: null
43 })
44
45 populateAsyncUserVideoChannels(this.authService, this.userVideoChannels)
46
47 this.serverService.videoPlaylistPrivaciesLoaded.subscribe(
48 () => {
49 this.videoPlaylistPrivacies = this.serverService.getVideoPlaylistPrivacies()
50
51 this.form.patchValue({
52 privacy: VideoPlaylistPrivacy.PRIVATE
53 })
54 }
55 )
56 }
57
58 formValidated () {
59 this.error = undefined
60
61 const body = this.form.value
62 const videoPlaylistCreate: VideoPlaylistCreate = {
63 displayName: body['display-name'],
64 privacy: body.privacy,
65 description: body.description || null,
66 videoChannelId: body.videoChannelId || null,
67 thumbnailfile: body.thumbnailfile || null
68 }
69
70 this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe(
71 () => {
72 this.notifier.success(
73 this.i18n('Playlist {{playlistName}} created.', { playlistName: videoPlaylistCreate.displayName })
74 )
75 this.router.navigate([ '/my-account', 'video-playlists' ])
76 },
77
78 err => this.error = err.message
79 )
80 }
81
82 isCreation () {
83 return true
84 }
85
86 getFormButtonTitle () {
87 return this.i18n('Create')
88 }
89}
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html
new file mode 100644
index 000000000..b76488c78
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.html
@@ -0,0 +1,64 @@
1<div i18n class="form-sub-title" *ngIf="isCreation() === true">Create a new playlist</div>
2
3<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
4
5<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
6 <div class="row">
7 <div class="col-md-12 col-xl-6">
8 <div class="form-group">
9 <label i18n for="display-name">Display name</label>
10 <input
11 type="text" id="display-name"
12 formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
13 >
14 <div *ngIf="formErrors['display-name']" class="form-error">
15 {{ formErrors['display-name'] }}
16 </div>
17 </div>
18
19 <div class="form-group">
20 <label i18n for="description">Description</label>
21 <textarea
22 id="description" formControlName="description"
23 [ngClass]="{ 'input-error': formErrors['description'] }"
24 ></textarea>
25 <div *ngIf="formErrors.description" class="form-error">
26 {{ formErrors.description }}
27 </div>
28 </div>
29 </div>
30
31 <div class="col-md-12 col-xl-6">
32 <div class="form-group">
33 <label i18n for="privacy">Privacy</label>
34 <div class="peertube-select-container">
35 <select id="privacy" formControlName="privacy">
36 <option *ngFor="let privacy of videoPlaylistPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
37 </select>
38 </div>
39
40 <div *ngIf="formErrors.privacy" class="form-error">
41 {{ formErrors.privacy }}
42 </div>
43 </div>
44
45 <div class="form-group">
46 <label i18n>Channel</label>
47 <div class="peertube-select-container">
48 <select formControlName="videoChannelId">
49 <option></option>
50 <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
51 </select>
52 </div>
53 </div>
54
55 <div class="form-group">
56 <my-image-upload
57 i18n-inputLabel inputLabel="Upload thumbnail" inputName="thumbnailfile" formControlName="thumbnailfile"
58 previewWidth="200px" previewHeight="110px"
59 ></my-image-upload>
60 </div>
61 </div>
62 </div>
63 <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid">
64</form>
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.scss b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.scss
new file mode 100644
index 000000000..5af846d8e
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.component.scss
@@ -0,0 +1,27 @@
1@import '_variables';
2@import '_mixins';
3
4.form-sub-title {
5 margin-bottom: 20px;
6}
7
8input[type=text] {
9 @include peertube-input-text(340px);
10
11 display: block;
12}
13
14textarea {
15 @include peertube-textarea(500px, 150px);
16
17 display: block;
18}
19
20.peertube-select-container {
21 @include peertube-select-container(340px);
22}
23
24input[type=submit] {
25 @include peertube-button;
26 @include orange-button;
27}
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts
new file mode 100644
index 000000000..fbfb4c8f7
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-edit.ts
@@ -0,0 +1,13 @@
1import { FormReactive } from '@app/shared'
2import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
3import { ServerService } from '@app/core'
4import { VideoPlaylist } from '@shared/models/videos/playlist/video-playlist.model'
5
6export abstract class MyAccountVideoPlaylistEdit extends FormReactive {
7 // Declare it here to avoid errors in create template
8 videoPlaylistToUpdate: VideoPlaylist
9 userVideoChannels: { id: number, label: string }[] = []
10
11 abstract isCreation (): boolean
12 abstract getFormButtonTitle (): string
13}
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
new file mode 100644
index 000000000..167d7dd09
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
@@ -0,0 +1,132 @@
1import { Component, OnDestroy, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router'
3import { AuthService, Notifier, ServerService } from '@app/core'
4import { Subscription } from 'rxjs'
5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
7import { MyAccountVideoPlaylistEdit } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-edit'
8import { populateAsyncUserVideoChannels } from '@app/shared/misc/utils'
9import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
10import { VideoPlaylistValidatorsService } from '@app/shared'
11import { VideoPlaylistUpdate } from '@shared/models/videos/playlist/video-playlist-update.model'
12import { VideoConstant } from '@shared/models'
13import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model'
14import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
15
16@Component({
17 selector: 'my-account-video-playlist-update',
18 templateUrl: './my-account-video-playlist-edit.component.html',
19 styleUrls: [ './my-account-video-playlist-edit.component.scss' ]
20})
21export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylistEdit implements OnInit, OnDestroy {
22 error: string
23 videoPlaylistToUpdate: VideoPlaylist
24 videoPlaylistPrivacies: VideoConstant<VideoPlaylistPrivacy>[] = []
25
26 private paramsSub: Subscription
27
28 constructor (
29 protected formValidatorService: FormValidatorService,
30 private authService: AuthService,
31 private videoPlaylistValidatorsService: VideoPlaylistValidatorsService,
32 private notifier: Notifier,
33 private router: Router,
34 private route: ActivatedRoute,
35 private videoPlaylistService: VideoPlaylistService,
36 private i18n: I18n,
37 private serverService: ServerService
38 ) {
39 super()
40 }
41
42 ngOnInit () {
43 this.buildForm({
44 'display-name': this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_DISPLAY_NAME,
45 privacy: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_PRIVACY,
46 description: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_DESCRIPTION,
47 videoChannelId: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_CHANNEL_ID,
48 thumbnailfile: null
49 })
50
51 populateAsyncUserVideoChannels(this.authService, this.userVideoChannels)
52
53 this.paramsSub = this.route.params.subscribe(routeParams => {
54 const videoPlaylistId = routeParams['videoPlaylistId']
55
56 this.videoPlaylistService.getVideoPlaylist(videoPlaylistId).subscribe(
57 videoPlaylistToUpdate => {
58 this.videoPlaylistToUpdate = videoPlaylistToUpdate
59
60 this.hydrateFormFromPlaylist()
61
62 this.serverService.videoPlaylistPrivaciesLoaded.subscribe(
63 () => {
64 this.videoPlaylistPrivacies = this.serverService.getVideoPlaylistPrivacies()
65 .filter(p => {
66 // If the playlist is not private, we cannot put it in private anymore
67 return this.videoPlaylistToUpdate.privacy.id === VideoPlaylistPrivacy.PRIVATE ||
68 p.id !== VideoPlaylistPrivacy.PRIVATE
69 })
70 }
71 )
72 },
73
74 err => this.error = err.message
75 )
76 })
77 }
78
79 ngOnDestroy () {
80 if (this.paramsSub) this.paramsSub.unsubscribe()
81 }
82
83 formValidated () {
84 this.error = undefined
85
86 const body = this.form.value
87 const videoPlaylistUpdate: VideoPlaylistUpdate = {
88 displayName: body['display-name'],
89 privacy: body['privacy'],
90 description: body.description || null,
91 videoChannelId: body.videoChannelId || null,
92 thumbnailfile: body.thumbnailfile || undefined
93 }
94
95 this.videoPlaylistService.updateVideoPlaylist(this.videoPlaylistToUpdate, videoPlaylistUpdate).subscribe(
96 () => {
97 this.notifier.success(
98 this.i18n('Playlist {{videoPlaylistName}} updated.', { videoPlaylistName: videoPlaylistUpdate.displayName })
99 )
100
101 this.router.navigate([ '/my-account', 'video-playlists' ])
102 },
103
104 err => this.error = err.message
105 )
106 }
107
108 isCreation () {
109 return false
110 }
111
112 getFormButtonTitle () {
113 return this.i18n('Update')
114 }
115
116 private hydrateFormFromPlaylist () {
117 this.form.patchValue({
118 'display-name': this.videoPlaylistToUpdate.displayName,
119 privacy: this.videoPlaylistToUpdate.privacy.id,
120 description: this.videoPlaylistToUpdate.description,
121 videoChannelId: this.videoPlaylistToUpdate.videoChannel ? this.videoPlaylistToUpdate.videoChannel.id : null
122 })
123
124 fetch(this.videoPlaylistToUpdate.thumbnailUrl)
125 .then(response => response.blob())
126 .then(data => {
127 this.form.patchValue({
128 thumbnailfile: data
129 })
130 })
131 }
132}
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
new file mode 100644
index 000000000..ab5d9cc5a
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.html
@@ -0,0 +1,20 @@
1<div class="video-playlists-header">
2 <a class="create-button" routerLink="create">
3 <my-global-icon iconName="add"></my-global-icon>
4 <ng-container i18n>Create a new playlist</ng-container>
5 </a>
6</div>
7
8<div class="video-playlists">
9 <div *ngFor="let playlist of videoPlaylists" class="video-playlist">
10 <div class="miniature-wrapper">
11 <my-video-playlist-miniature [playlist]="playlist"></my-video-playlist-miniature>
12 </div>
13
14 <div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons">
15 <my-delete-button (click)="deleteVideoPlaylist(playlist)"></my-delete-button>
16
17 <my-edit-button [routerLink]="[ 'update', playlist.uuid ]"></my-edit-button>
18 </div>
19 </div>
20</div>
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
new file mode 100644
index 000000000..88fba5b05
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.scss
@@ -0,0 +1,50 @@
1@import '_variables';
2@import '_mixins';
3
4.create-button {
5 @include create-button;
6}
7
8/deep/ .action-button {
9 &.action-button-delete {
10 margin-right: 10px;
11 }
12}
13
14.video-playlist {
15 @include row-blocks;
16
17 .miniature-wrapper {
18 flex-grow: 1;
19
20 /deep/ .miniature {
21 display: flex;
22
23 .miniature-bottom {
24 margin-left: 10px;
25 }
26 }
27 }
28
29 .video-playlist-buttons {
30 min-width: 190px;
31 }
32}
33
34.video-playlists-header {
35 text-align: right;
36 margin: 20px 0 50px;
37}
38
39@media screen and (max-width: 800px) {
40 .video-playlists-header {
41 text-align: center;
42 }
43
44 .video-playlist {
45
46 .video-playlist-buttons {
47 margin-top: 10px;
48 }
49 }
50}
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
new file mode 100644
index 000000000..761ce90e8
--- /dev/null
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlists.component.ts
@@ -0,0 +1,85 @@
1import { Component, OnInit } from '@angular/core'
2import { Notifier } from '@app/core'
3import { AuthService } from '../../core/auth'
4import { ConfirmService } from '../../core/confirm'
5import { User } from '@app/shared'
6import { flatMap } from 'rxjs/operators'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
9import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
10import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
11import { VideoPlaylistType } from '@shared/models'
12
13@Component({
14 selector: 'my-account-video-playlists',
15 templateUrl: './my-account-video-playlists.component.html',
16 styleUrls: [ './my-account-video-playlists.component.scss' ]
17})
18export class MyAccountVideoPlaylistsComponent implements OnInit {
19 videoPlaylists: VideoPlaylist[] = []
20
21 pagination: ComponentPagination = {
22 currentPage: 1,
23 itemsPerPage: 10,
24 totalItems: null
25 }
26
27 private user: User
28
29 constructor (
30 private authService: AuthService,
31 private notifier: Notifier,
32 private confirmService: ConfirmService,
33 private videoPlaylistService: VideoPlaylistService,
34 private i18n: I18n
35 ) {}
36
37 ngOnInit () {
38 this.user = this.authService.getUser()
39
40 this.loadVideoPlaylists()
41 }
42
43 async deleteVideoPlaylist (videoPlaylist: VideoPlaylist) {
44 const res = await this.confirmService.confirm(
45 this.i18n(
46 'Do you really want to delete {{playlistDisplayName}}?',
47 { playlistDisplayName: videoPlaylist.displayName }
48 ),
49 this.i18n('Delete')
50 )
51 if (res === false) return
52
53 this.videoPlaylistService.removeVideoPlaylist(videoPlaylist)
54 .subscribe(
55 () => {
56 this.videoPlaylists = this.videoPlaylists
57 .filter(p => p.id !== videoPlaylist.id)
58
59 this.notifier.success(
60 this.i18n('Playlist {{playlistDisplayName}} deleted.', { playlistDisplayName: videoPlaylist.displayName })
61 )
62 },
63
64 error => this.notifier.error(error.message)
65 )
66 }
67
68 isRegularPlaylist (playlist: VideoPlaylist) {
69 return playlist.type.id === VideoPlaylistType.REGULAR
70 }
71
72 private loadVideoPlaylists () {
73 this.authService.userInformationLoaded
74 .pipe(flatMap(() => this.videoPlaylistService.listAccountPlaylists(this.user.account)))
75 .subscribe(res => this.videoPlaylists = res.data)
76 }
77
78 private ofNearOfBottom () {
79 // Last page
80 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
81
82 this.pagination.currentPage += 1
83 this.loadVideoPlaylists()
84 }
85}
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index 8a4102d80..f624ff505 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -28,6 +28,10 @@ export class MyAccountComponent {
28 routerLink: '/my-account/videos' 28 routerLink: '/my-account/videos'
29 }, 29 },
30 { 30 {
31 label: this.i18n('My playlists'),
32 routerLink: '/my-account/video-playlists'
33 },
34 {
31 label: this.i18n('My subscriptions'), 35 label: this.i18n('My subscriptions'),
32 routerLink: '/my-account/subscriptions' 36 routerLink: '/my-account/subscriptions'
33 }, 37 },
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts
index 18f51f171..3dbce2b92 100644
--- a/client/src/app/+my-account/my-account.module.ts
+++ b/client/src/app/+my-account/my-account.module.ts
@@ -25,6 +25,13 @@ import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-b
25import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/my-account-history.component' 25import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/my-account-history.component'
26import { MyAccountNotificationsComponent } from '@app/+my-account/my-account-notifications/my-account-notifications.component' 26import { MyAccountNotificationsComponent } from '@app/+my-account/my-account-notifications/my-account-notifications.component'
27import { MyAccountNotificationPreferencesComponent } from '@app/+my-account/my-account-settings/my-account-notification-preferences' 27import { MyAccountNotificationPreferencesComponent } from '@app/+my-account/my-account-settings/my-account-notification-preferences'
28import {
29 MyAccountVideoPlaylistCreateComponent
30} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component'
31import {
32 MyAccountVideoPlaylistUpdateComponent
33} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component'
34import { MyAccountVideoPlaylistsComponent } from '@app/+my-account/my-account-video-playlists/my-account-video-playlists.component'
28 35
29@NgModule({ 36@NgModule({
30 imports: [ 37 imports: [
@@ -57,7 +64,11 @@ import { MyAccountNotificationPreferencesComponent } from '@app/+my-account/my-a
57 MyAccountServerBlocklistComponent, 64 MyAccountServerBlocklistComponent,
58 MyAccountHistoryComponent, 65 MyAccountHistoryComponent,
59 MyAccountNotificationsComponent, 66 MyAccountNotificationsComponent,
60 MyAccountNotificationPreferencesComponent 67 MyAccountNotificationPreferencesComponent,
68
69 MyAccountVideoPlaylistCreateComponent,
70 MyAccountVideoPlaylistUpdateComponent,
71 MyAccountVideoPlaylistsComponent
61 ], 72 ],
62 73
63 exports: [ 74 exports: [