aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html2
-rw-r--r--client/src/app/+admin/follows/follows.component.html2
-rw-r--r--client/src/app/+admin/follows/follows.component.scss2
-rw-r--r--client/src/app/+admin/jobs/jobs-list/jobs-list.component.html2
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.html4
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.component.scss2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.html2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.scss10
-rw-r--r--client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html2
-rw-r--r--client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html2
-rw-r--r--client/src/app/my-account/my-account-routing.module.ts30
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channel-create.component.ts86
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.component.html42
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.component.scss27
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.ts6
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channel-update.component.ts116
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.html30
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.scss72
-rw-r--r--client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.ts59
-rw-r--r--client/src/app/my-account/my-account-videos/my-account-videos.component.ts2
-rw-r--r--client/src/app/my-account/my-account.component.html2
-rw-r--r--client/src/app/my-account/my-account.module.ts8
-rw-r--r--client/src/app/shared/forms/form-validators/video-channel.ts34
-rw-r--r--client/src/app/shared/video-channel/video-channel.service.ts22
24 files changed, 543 insertions, 23 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
index df40bba9f..021252456 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html
@@ -1,4 +1,4 @@
1<div class="admin-sub-title">Update PeerTube configuration</div> 1<div class="form-sub-title">Update PeerTube configuration</div>
2 2
3<form role="form" [formGroup]="form"> 3<form role="form" [formGroup]="form">
4 4
diff --git a/client/src/app/+admin/follows/follows.component.html b/client/src/app/+admin/follows/follows.component.html
index d3d748622..71e82059c 100644
--- a/client/src/app/+admin/follows/follows.component.html
+++ b/client/src/app/+admin/follows/follows.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="admin-sub-title">Manage follows</div> 2 <div class="form-sub-title">Manage follows</div>
3 3
4 <tabset #followsMenuTabs> 4 <tabset #followsMenuTabs>
5 <tab *ngFor="let link of links"> 5 <tab *ngFor="let link of links">
diff --git a/client/src/app/+admin/follows/follows.component.scss b/client/src/app/+admin/follows/follows.component.scss
index 385dfbb7d..08b3737f8 100644
--- a/client/src/app/+admin/follows/follows.component.scss
+++ b/client/src/app/+admin/follows/follows.component.scss
@@ -1,4 +1,4 @@
1.admin-sub-title { 1.form-sub-title {
2 flex-grow: 0; 2 flex-grow: 0;
3 margin-right: 30px; 3 margin-right: 30px;
4} 4}
diff --git a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html
index 2bc8ab3dd..0cc4dc3e0 100644
--- a/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html
+++ b/client/src/app/+admin/jobs/jobs-list/jobs-list.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="admin-sub-title">Jobs list</div> 2 <div class="form-sub-title">Jobs list</div>
3 3
4 <div class="peertube-select-container"> 4 <div class="peertube-select-container">
5 <select [(ngModel)]="jobState" (ngModelChange)="onJobStateChanged()"> 5 <select [(ngModel)]="jobState" (ngModelChange)="onJobStateChanged()">
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html
index a68dd231b..6dee0b7aa 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.html
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-title" *ngIf="isCreation() === true">Add user</div> 1<div class="form-sub-title" *ngIf="isCreation() === true">Add user</div>
2<div class="admin-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div> 2<div class="form-sub-title" *ngIf="isCreation() === false">Edit user {{ username }}</div>
3 3
4<div *ngIf="error" class="alert alert-danger">{{ error }}</div> 4<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
5 5
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss
index 59bb8e3e4..b74604e3b 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.component.scss
+++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss
@@ -1,7 +1,7 @@
1@import '_variables'; 1@import '_variables';
2@import '_mixins'; 2@import '_mixins';
3 3
4.admin-sub-title { 4.form-sub-title {
5 margin-bottom: 30px; 5 margin-bottom: 30px;
6} 6}
7 7
diff --git a/client/src/app/+admin/users/user-list/user-list.component.html b/client/src/app/+admin/users/user-list/user-list.component.html
index 8dbe9ddc4..83db034e3 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.html
+++ b/client/src/app/+admin/users/user-list/user-list.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="admin-sub-title">Users list</div> 2 <div class="form-sub-title">Users list</div>
3 3
4 <a class="add-button" routerLink="/admin/users/add"> 4 <a class="add-button" routerLink="/admin/users/add">
5 <span class="icon icon-add"></span> 5 <span class="icon icon-add"></span>
diff --git a/client/src/app/+admin/users/user-list/user-list.component.scss b/client/src/app/+admin/users/user-list/user-list.component.scss
index 72d31a0cc..4a66b5d8d 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.scss
+++ b/client/src/app/+admin/users/user-list/user-list.component.scss
@@ -2,13 +2,5 @@
2@import '_mixins'; 2@import '_mixins';
3 3
4.add-button { 4.add-button {
5 @include peertube-button-link; 5 @include create-button;
6 @include orange-button;
7
8 .icon.icon-add {
9 @include icon(22px);
10
11 margin-right: 3px;
12 background-image: url('../../../../assets/images/admin/add.svg');
13 }
14} 6}
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
index 13a5b1117..5f70db991 100644
--- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
+++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="admin-sub-title">Video abuses list</div> 2 <div class="form-sub-title">Video abuses list</div>
3</div> 3</div>
4 4
5<p-table 5<p-table
diff --git a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
index ac30cec39..0a0fcb762 100644
--- a/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
+++ b/client/src/app/+admin/video-blacklist/video-blacklist-list/video-blacklist-list.component.html
@@ -1,5 +1,5 @@
1<div class="admin-sub-header"> 1<div class="admin-sub-header">
2 <div class="admin-sub-title">Blacklisted videos</div> 2 <div class="form-sub-title">Blacklisted videos</div>
3</div> 3</div>
4 4
5<p-table 5<p-table
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 5a61db41f..96f52c1da 100644
--- a/client/src/app/my-account/my-account-routing.module.ts
+++ b/client/src/app/my-account/my-account-routing.module.ts
@@ -5,6 +5,9 @@ import { LoginGuard } from '../core'
5import { MyAccountComponent } from './my-account.component' 5import { MyAccountComponent } from './my-account.component'
6import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component' 6import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
7import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component' 7import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component'
8import { MyAccountVideoChannelsComponent } from '@app/my-account/my-account-video-channels/my-account-video-channels.component'
9import { MyAccountVideoChannelCreateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-create.component'
10import { MyAccountVideoChannelUpdateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-update.component'
8 11
9const myAccountRoutes: Routes = [ 12const myAccountRoutes: Routes = [
10 { 13 {
@@ -22,6 +25,33 @@ const myAccountRoutes: Routes = [
22 } 25 }
23 }, 26 },
24 { 27 {
28 path: 'video-channels',
29 component: MyAccountVideoChannelsComponent,
30 data: {
31 meta: {
32 title: 'Account video channels'
33 }
34 }
35 },
36 {
37 path: 'video-channels/create',
38 component: MyAccountVideoChannelCreateComponent,
39 data: {
40 meta: {
41 title: 'Create new video channel'
42 }
43 }
44 },
45 {
46 path: 'video-channels/update/:videoChannelId',
47 component: MyAccountVideoChannelUpdateComponent,
48 data: {
49 meta: {
50 title: 'Update video channel'
51 }
52 }
53 },
54 {
25 path: 'videos', 55 path: 'videos',
26 component: MyAccountVideosComponent, 56 component: MyAccountVideosComponent,
27 data: { 57 data: {
diff --git a/client/src/app/my-account/my-account-video-channels/my-account-video-channel-create.component.ts b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-create.component.ts
new file mode 100644
index 000000000..3cfe74752
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-create.component.ts
@@ -0,0 +1,86 @@
1import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router'
3import { NotificationsService } from 'angular2-notifications'
4import 'rxjs/add/observable/from'
5import 'rxjs/add/operator/concatAll'
6import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
7import { FormBuilder, FormGroup } from '@angular/forms'
8import { VideoChannelCreate } from '../../../../../shared/models/videos'
9import {
10 VIDEO_CHANNEL_DESCRIPTION,
11 VIDEO_CHANNEL_DISPLAY_NAME,
12 VIDEO_CHANNEL_SUPPORT
13} from '@app/shared/forms/form-validators/video-channel'
14import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
15
16@Component({
17 selector: 'my-account-video-channel-create',
18 templateUrl: './my-account-video-channel-edit.component.html',
19 styleUrls: [ './my-account-video-channel-edit.component.scss' ]
20})
21export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelEdit implements OnInit {
22 error: string
23
24 form: FormGroup
25 formErrors = {
26 'display-name': '',
27 'description': '',
28 'support': ''
29 }
30 validationMessages = {
31 'display-name': VIDEO_CHANNEL_DISPLAY_NAME.MESSAGES,
32 'description': VIDEO_CHANNEL_DESCRIPTION.MESSAGES,
33 'support': VIDEO_CHANNEL_SUPPORT.MESSAGES
34 }
35
36 constructor (
37 private notificationsService: NotificationsService,
38 private router: Router,
39 private formBuilder: FormBuilder,
40 private videoChannelService: VideoChannelService
41 ) {
42 super()
43 }
44
45 buildForm () {
46 this.form = this.formBuilder.group({
47 'display-name': [ '', VIDEO_CHANNEL_DISPLAY_NAME.VALIDATORS ],
48 description: [ '', VIDEO_CHANNEL_DESCRIPTION.VALIDATORS ],
49 support: [ '', VIDEO_CHANNEL_SUPPORT.VALIDATORS ]
50 })
51
52 this.form.valueChanges.subscribe(data => this.onValueChanged(data))
53 }
54
55 ngOnInit () {
56 this.buildForm()
57 }
58
59 formValidated () {
60 this.error = undefined
61
62 const body = this.form.value
63 const videoChannelCreate: VideoChannelCreate = {
64 displayName: body['display-name'],
65 description: body.description,
66 support: body.support
67 }
68
69 this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe(
70 () => {
71 this.notificationsService.success('Success', `Video channel ${videoChannelCreate.displayName} created.`)
72 this.router.navigate([ '/my-account', 'video-channels' ])
73 },
74
75 err => this.error = err.message
76 )
77 }
78
79 isCreation () {
80 return true
81 }
82
83 getFormButtonTitle () {
84 return 'Create this video channel'
85 }
86}
diff --git a/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.component.html b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.component.html
new file mode 100644
index 000000000..adc969bdb
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.component.html
@@ -0,0 +1,42 @@
1<div class="form-sub-title" *ngIf="isCreation() === true">Create a video channel</div>
2<div class="form-sub-title" *ngIf="isCreation() === false">Update {{ videoChannel?.displayName }}</div>
3
4<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
5
6<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
7 <div class="form-group">
8 <label for="display-name">Display name</label>
9 <input
10 type="text" id="display-name"
11 formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
12 >
13 <div *ngIf="formErrors.display-name" class="form-error">
14 {{ formErrors.display-name }}
15 </div>
16 </div>
17
18 <div class="form-group">
19 <label for="description">Description</label>
20 <textarea
21 id="description" formControlName="description"
22 [ngClass]="{ 'input-error': formErrors['description'] }"
23 ></textarea>
24 <div *ngIf="formErrors.description" class="form-error">
25 {{ formErrors.description }}
26 </div>
27 </div>
28
29 <div class="form-group">
30 <label for="support">Support</label>
31 <my-help helpType="markdownEnhanced" preHtml="Short text to tell people how they can support your channel (membership platform...)."></my-help>
32 <my-markdown-textarea
33 id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
34 [classes]="{ 'input-error': formErrors['support'] }"
35 ></my-markdown-textarea>
36 <div *ngIf="formErrors.support" class="form-error">
37 {{ formErrors.support }}
38 </div>
39 </div>
40
41 <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid">
42</form>
diff --git a/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.component.scss b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.component.scss
new file mode 100644
index 000000000..6fbb8ae8b
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-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} \ No newline at end of file
diff --git a/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.ts b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.ts
new file mode 100644
index 000000000..e56f46262
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-edit.ts
@@ -0,0 +1,6 @@
1import { FormReactive } from '@app/shared'
2
3export abstract class MyAccountVideoChannelEdit extends FormReactive {
4 abstract isCreation (): boolean
5 abstract getFormButtonTitle (): string
6}
diff --git a/client/src/app/my-account/my-account-video-channels/my-account-video-channel-update.component.ts b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-update.component.ts
new file mode 100644
index 000000000..2b8415938
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channel-update.component.ts
@@ -0,0 +1,116 @@
1import { Component, OnInit, OnDestroy } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router'
3import { NotificationsService } from 'angular2-notifications'
4import 'rxjs/add/observable/from'
5import 'rxjs/add/operator/concatAll'
6import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
7import { FormBuilder, FormGroup } from '@angular/forms'
8import { VideoChannelUpdate } from '../../../../../shared/models/videos'
9import {
10 VIDEO_CHANNEL_DESCRIPTION,
11 VIDEO_CHANNEL_DISPLAY_NAME,
12 VIDEO_CHANNEL_SUPPORT
13} from '@app/shared/forms/form-validators/video-channel'
14import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
15import { Subscription } from 'rxjs/Subscription'
16import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
17
18@Component({
19 selector: 'my-account-video-channel-update',
20 templateUrl: './my-account-video-channel-edit.component.html',
21 styleUrls: [ './my-account-video-channel-edit.component.scss' ]
22})
23export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelEdit implements OnInit, OnDestroy {
24 error: string
25
26 form: FormGroup
27 formErrors = {
28 'display-name': '',
29 'description': '',
30 'support': ''
31 }
32 validationMessages = {
33 'display-name': VIDEO_CHANNEL_DISPLAY_NAME.MESSAGES,
34 'description': VIDEO_CHANNEL_DESCRIPTION.MESSAGES,
35 'support': VIDEO_CHANNEL_SUPPORT.MESSAGES
36 }
37
38 private videoChannelToUpdate: VideoChannel
39 private paramsSub: Subscription
40
41 constructor (
42 private notificationsService: NotificationsService,
43 private router: Router,
44 private route: ActivatedRoute,
45 private formBuilder: FormBuilder,
46 private videoChannelService: VideoChannelService
47 ) {
48 super()
49 }
50
51 buildForm () {
52 this.form = this.formBuilder.group({
53 'display-name': [ '', VIDEO_CHANNEL_DISPLAY_NAME.VALIDATORS ],
54 description: [ '', VIDEO_CHANNEL_DESCRIPTION.VALIDATORS ],
55 support: [ '', VIDEO_CHANNEL_SUPPORT.VALIDATORS ]
56 })
57
58 this.form.valueChanges.subscribe(data => this.onValueChanged(data))
59 }
60
61 ngOnInit () {
62 this.buildForm()
63
64 this.paramsSub = this.route.params.subscribe(routeParams => {
65 const videoChannelId = routeParams['videoChannelId']
66
67 this.videoChannelService.getVideoChannel(videoChannelId).subscribe(
68 videoChannelToUpdate => {
69 this.videoChannelToUpdate = videoChannelToUpdate
70
71 this.form.patchValue({
72 'display-name': videoChannelToUpdate.displayName,
73 description: videoChannelToUpdate.description,
74 support: videoChannelToUpdate.support
75 })
76 },
77
78 err => this.error = err.message
79 )
80 })
81 }
82
83 ngOnDestroy () {
84 if (this.paramsSub) this.paramsSub.unsubscribe()
85 }
86
87 formValidated () {
88 this.error = undefined
89
90 const body = this.form.value
91 const videoChannelUpdate: VideoChannelUpdate = {
92 displayName: body['display-name'],
93 description: body.description,
94 support: body.support
95 }
96
97 this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.uuid, videoChannelUpdate).subscribe(
98 () => {
99 this.notificationsService.success('Success', `Video channel ${videoChannelUpdate.displayName} updated.`)
100 this.router.navigate([ '/my-account', 'video-channels' ])
101 },
102
103 err => this.error = err.message
104 )
105 }
106
107 isCreation () {
108 return false
109 }
110
111 getFormButtonTitle () {
112 return this.videoChannelToUpdate
113 ? 'Update ' + this.videoChannelToUpdate.displayName + ' video channel'
114 : 'Update'
115 }
116}
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
new file mode 100644
index 000000000..90c401bc5
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.html
@@ -0,0 +1,30 @@
1<div class="video-channels-header">
2 <a class="create-button" routerLink="create">
3 <span class="icon icon-add"></span>
4 Create another video channel
5 </a>
6</div>
7
8<div class="video-channels">
9 <div *ngFor="let videoChannel of videoChannels" class="video-channel">
10 <a [routerLink]="[ '/video-channels', videoChannel.uuid ]">
11 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
12 </a>
13
14 <div class="video-channel-info">
15 <a [routerLink]="[ '/video-channels', videoChannel.uuid ]" class="video-channel-names" title="Go to the channel">
16 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
17 <!-- Hide the name for now, because it's an UUID not very friendly -->
18 <!--<div class="video-channel-name">{{ videoChannel.name }}</div>-->
19 </a>
20
21 <div class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div>
22 </div>
23
24 <div class="video-channel-buttons">
25 <my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button>
26
27 <my-edit-button [routerLink]="[ 'update', videoChannel.uuid ]"></my-edit-button>
28 </div>
29 </div>
30</div>
diff --git a/client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.scss b/client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.scss
new file mode 100644
index 000000000..bcb58eac2
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.scss
@@ -0,0 +1,72 @@
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-channel {
15 display: flex;
16 min-height: 130px;
17 padding-bottom: 20px;
18 margin-bottom: 20px;
19 border-bottom: 1px solid #C6C6C6;
20
21 img {
22 @include avatar(80px);
23
24 margin-right: 10px;
25 }
26
27 .video-channel-info {
28 flex-grow: 1;
29
30 a.video-channel-names {
31 @include disable-default-a-behaviour;
32
33 display: flex;
34 color: #000;
35
36 .video-channel-display-name {
37 font-weight: $font-semibold;
38 font-size: 18px;
39 }
40
41 .video-channel-name {
42 font-size: 14px;
43 color: #777272;
44 }
45 }
46 }
47
48 .video-channel-buttons {
49 min-width: 190px;
50 }
51}
52
53.video-channels-header {
54 text-align: right;
55 margin: 20px 0 50px;
56}
57
58@media screen and (max-width: 800px) {
59 .video-channel {
60 flex-direction: column;
61 height: auto;
62 text-align: center;
63
64 img {
65 margin-right: 0;
66 }
67
68 .video-channel-buttons {
69 margin-top: 10px;
70 }
71 }
72}
diff --git a/client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.ts b/client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.ts
new file mode 100644
index 000000000..eb04094e1
--- /dev/null
+++ b/client/src/app/my-account/my-account-video-channels/my-account-video-channels.component.ts
@@ -0,0 +1,59 @@
1import { Component, OnInit } from '@angular/core'
2import { NotificationsService } from 'angular2-notifications'
3import 'rxjs/add/observable/from'
4import 'rxjs/add/operator/concatAll'
5import { AuthService } from '../../core/auth'
6import { ConfirmService } from '../../core/confirm'
7import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
8import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
9import { User } from '@app/shared'
10
11@Component({
12 selector: 'my-account-video-channels',
13 templateUrl: './my-account-video-channels.component.html',
14 styleUrls: [ './my-account-video-channels.component.scss' ]
15})
16export class MyAccountVideoChannelsComponent implements OnInit{
17 videoChannels: VideoChannel[] = []
18
19 private user: User
20
21 constructor (
22 private authService: AuthService,
23 private notificationsService: NotificationsService,
24 private confirmService: ConfirmService,
25 private videoChannelService: VideoChannelService
26 ) {}
27
28 ngOnInit () {
29 this.user = this.authService.getUser()
30
31 this.loadVideoChannels()
32 }
33
34 async deleteVideoChannel (videoChannel: VideoChannel) {
35 const res = await this.confirmService.confirmWithInput(
36 `Do you really want to delete ${videoChannel.displayName}? It will delete all videos uploaded in this channel too.`,
37 'Please type the name of the video channel to confirm',
38 videoChannel.displayName,
39 'Delete'
40 )
41 if (res === false) return
42
43 this.videoChannelService.removeVideoChannel(videoChannel)
44 .subscribe(
45 status => {
46 this.loadVideoChannels()
47 this.notificationsService.success('Success', `Video channel ${videoChannel.name} deleted.`)
48 },
49
50 error => this.notificationsService.error('Error', error.message)
51 )
52 }
53
54 private loadVideoChannels () {
55 this.authService.userInformationLoaded
56 .flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account.id))
57 .subscribe(res => this.videoChannels = res.data)
58 }
59}
diff --git a/client/src/app/my-account/my-account-videos/my-account-videos.component.ts b/client/src/app/my-account/my-account-videos/my-account-videos.component.ts
index a6cef361e..c1b53bcd5 100644
--- a/client/src/app/my-account/my-account-videos/my-account-videos.component.ts
+++ b/client/src/app/my-account/my-account-videos/my-account-videos.component.ts
@@ -43,8 +43,6 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
43 43
44 ngOnInit () { 44 ngOnInit () {
45 super.ngOnInit() 45 super.ngOnInit()
46
47 // this.generateSyndicationList()
48 } 46 }
49 47
50 ngOnDestroy () { 48 ngOnDestroy () {
diff --git a/client/src/app/my-account/my-account.component.html b/client/src/app/my-account/my-account.component.html
index 41afc1e5d..591d58cf9 100644
--- a/client/src/app/my-account/my-account.component.html
+++ b/client/src/app/my-account/my-account.component.html
@@ -2,6 +2,8 @@
2 <div class="sub-menu"> 2 <div class="sub-menu">
3 <a routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a> 3 <a routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a>
4 4
5 <a routerLink="/my-account/video-channels" routerLinkActive="active" class="title-page">My video channels</a>
6
5 <a routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a> 7 <a routerLink="/my-account/videos" routerLinkActive="active" class="title-page">My videos</a>
6 </div> 8 </div>
7 9
diff --git a/client/src/app/my-account/my-account.module.ts b/client/src/app/my-account/my-account.module.ts
index 981d3c697..ba9dea71e 100644
--- a/client/src/app/my-account/my-account.module.ts
+++ b/client/src/app/my-account/my-account.module.ts
@@ -7,6 +7,9 @@ import { MyAccountSettingsComponent } from './my-account-settings/my-account-set
7import { MyAccountComponent } from './my-account.component' 7import { MyAccountComponent } from './my-account.component'
8import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component' 8import { MyAccountVideosComponent } from './my-account-videos/my-account-videos.component'
9import { MyAccountProfileComponent } from '@app/my-account/my-account-settings/my-account-profile/my-account-profile.component' 9import { MyAccountProfileComponent } from '@app/my-account/my-account-settings/my-account-profile/my-account-profile.component'
10import { MyAccountVideoChannelsComponent } from '@app/my-account/my-account-video-channels/my-account-video-channels.component'
11import { MyAccountVideoChannelCreateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-create.component'
12import { MyAccountVideoChannelUpdateComponent } from '@app/my-account/my-account-video-channels/my-account-video-channel-update.component'
10 13
11@NgModule({ 14@NgModule({
12 imports: [ 15 imports: [
@@ -20,7 +23,10 @@ import { MyAccountProfileComponent } from '@app/my-account/my-account-settings/m
20 MyAccountChangePasswordComponent, 23 MyAccountChangePasswordComponent,
21 MyAccountVideoSettingsComponent, 24 MyAccountVideoSettingsComponent,
22 MyAccountProfileComponent, 25 MyAccountProfileComponent,
23 MyAccountVideosComponent 26 MyAccountVideosComponent,
27 MyAccountVideoChannelsComponent,
28 MyAccountVideoChannelCreateComponent,
29 MyAccountVideoChannelUpdateComponent
24 ], 30 ],
25 31
26 exports: [ 32 exports: [
diff --git a/client/src/app/shared/forms/form-validators/video-channel.ts b/client/src/app/shared/forms/form-validators/video-channel.ts
new file mode 100644
index 000000000..6233d17f7
--- /dev/null
+++ b/client/src/app/shared/forms/form-validators/video-channel.ts
@@ -0,0 +1,34 @@
1import { Validators } from '@angular/forms'
2
3export const VIDEO_CHANNEL_DISPLAY_NAME = {
4 VALIDATORS: [
5 Validators.required,
6 Validators.minLength(3),
7 Validators.maxLength(120)
8 ],
9 MESSAGES: {
10 'required': 'Display name is required.',
11 'minlength': 'Display name must be at least 3 characters long.',
12 'maxlength': 'Display name cannot be more than 120 characters long.'
13 }
14}
15export const VIDEO_CHANNEL_DESCRIPTION = {
16 VALIDATORS: [
17 Validators.minLength(3),
18 Validators.maxLength(250)
19 ],
20 MESSAGES: {
21 'minlength': 'Description must be at least 3 characters long.',
22 'maxlength': 'Description cannot be more than 250 characters long.'
23 }
24}
25export const VIDEO_CHANNEL_SUPPORT = {
26 VALIDATORS: [
27 Validators.minLength(3),
28 Validators.maxLength(300)
29 ],
30 MESSAGES: {
31 'minlength': 'Support text must be at least 3 characters long.',
32 'maxlength': 'Support text cannot be more than 300 characters long.'
33 }
34}
diff --git a/client/src/app/shared/video-channel/video-channel.service.ts b/client/src/app/shared/video-channel/video-channel.service.ts
index d8efcc171..3533a0e9c 100644
--- a/client/src/app/shared/video-channel/video-channel.service.ts
+++ b/client/src/app/shared/video-channel/video-channel.service.ts
@@ -5,12 +5,14 @@ import { Observable } from 'rxjs/Observable'
5import { RestExtractor } from '../rest/rest-extractor.service' 5import { RestExtractor } from '../rest/rest-extractor.service'
6import { RestService } from '../rest/rest.service' 6import { RestService } from '../rest/rest.service'
7import { HttpClient } from '@angular/common/http' 7import { HttpClient } from '@angular/common/http'
8import { VideoChannel as VideoChannelServer } from '../../../../../shared/models/videos' 8import { VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '../../../../../shared/models/videos'
9import { AccountService } from '../account/account.service' 9import { AccountService } from '../account/account.service'
10import { ResultList } from '../../../../../shared' 10import { ResultList } from '../../../../../shared'
11import { VideoChannel } from './video-channel.model' 11import { VideoChannel } from './video-channel.model'
12import { ReplaySubject } from 'rxjs/ReplaySubject' 12import { ReplaySubject } from 'rxjs/ReplaySubject'
13import { environment } from '../../../environments/environment' 13import { environment } from '../../../environments/environment'
14import { UserService } from '@app/+admin/users/shared/user.service'
15import { User } from '@app/shared'
14 16
15@Injectable() 17@Injectable()
16export class VideoChannelService { 18export class VideoChannelService {
@@ -37,6 +39,24 @@ export class VideoChannelService {
37 .catch((res) => this.restExtractor.handleError(res)) 39 .catch((res) => this.restExtractor.handleError(res))
38 } 40 }
39 41
42 createVideoChannel (videoChannel: VideoChannelCreate) {
43 return this.authHttp.post(VideoChannelService.BASE_VIDEO_CHANNEL_URL, videoChannel)
44 .map(this.restExtractor.extractDataBool)
45 .catch(err => this.restExtractor.handleError(err))
46 }
47
48 updateVideoChannel (videoChannelUUID: string, videoChannel: VideoChannelUpdate) {
49 return this.authHttp.put(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelUUID, videoChannel)
50 .map(this.restExtractor.extractDataBool)
51 .catch(err => this.restExtractor.handleError(err))
52 }
53
54 removeVideoChannel (videoChannel: VideoChannel) {
55 return this.authHttp.delete(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.uuid)
56 .map(this.restExtractor.extractDataBool)
57 .catch(err => this.restExtractor.handleError(err))
58 }
59
40 private extractVideoChannels (result: ResultList<VideoChannelServer>) { 60 private extractVideoChannels (result: ResultList<VideoChannelServer>) {
41 const videoChannels: VideoChannel[] = [] 61 const videoChannels: VideoChannel[] = []
42 62