aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+my-account/+my-account-video-channels
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-06-23 14:10:17 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-06-23 16:00:49 +0200
commit67ed6552b831df66713bac9e672738796128d33f (patch)
tree59c97d41e0b49d75a90aa3de987968ab9b1ff447 /client/src/app/+my-account/+my-account-video-channels
parent0c4bacbff53bc732f5a2677d62a6ead7752e2405 (diff)
downloadPeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.gz
PeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.zst
PeerTube-67ed6552b831df66713bac9e672738796128d33f.zip
Reorganize client shared modules
Diffstat (limited to 'client/src/app/+my-account/+my-account-video-channels')
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-create.component.ts82
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-edit.component.html105
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-edit.component.scss67
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-edit.ts19
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-update.component.ts135
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channels-routing.module.ts41
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.html35
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.scss115
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.ts154
-rw-r--r--client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.module.ts31
10 files changed, 784 insertions, 0 deletions
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..039c389e4
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-create.component.ts
@@ -0,0 +1,82 @@
1import { Component, OnInit } from '@angular/core'
2import { Router } from '@angular/router'
3import { AuthService, Notifier } from '@app/core'
4import { FormValidatorService, VideoChannelValidatorsService } from '@app/shared/shared-forms'
5import { VideoChannelService } from '@app/shared/shared-main'
6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { VideoChannelCreate } from '@shared/models'
8import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
9
10@Component({
11 selector: 'my-account-video-channel-create',
12 templateUrl: './my-account-video-channel-edit.component.html',
13 styleUrls: [ './my-account-video-channel-edit.component.scss' ]
14})
15export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelEdit implements OnInit {
16 error: string
17
18 constructor (
19 protected formValidatorService: FormValidatorService,
20 private authService: AuthService,
21 private videoChannelValidatorsService: VideoChannelValidatorsService,
22 private notifier: Notifier,
23 private router: Router,
24 private videoChannelService: VideoChannelService,
25 private i18n: I18n
26 ) {
27 super()
28 }
29
30 get instanceHost () {
31 return window.location.host
32 }
33
34 ngOnInit () {
35 this.buildForm({
36 name: this.videoChannelValidatorsService.VIDEO_CHANNEL_NAME,
37 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME,
38 description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION,
39 support: this.videoChannelValidatorsService.VIDEO_CHANNEL_SUPPORT
40 })
41 }
42
43 formValidated () {
44 this.error = undefined
45
46 const body = this.form.value
47 const videoChannelCreate: VideoChannelCreate = {
48 name: body.name,
49 displayName: body['display-name'],
50 description: body.description || null,
51 support: body.support || null
52 }
53
54 this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe(
55 () => {
56 this.authService.refreshUserInformation()
57
58 this.notifier.success(
59 this.i18n('Video channel {{videoChannelName}} created.', { videoChannelName: videoChannelCreate.displayName })
60 )
61 this.router.navigate([ '/my-account', 'video-channels' ])
62 },
63
64 err => {
65 if (err.status === 409) {
66 this.error = this.i18n('This name already exists on this instance.')
67 return
68 }
69
70 this.error = err.message
71 }
72 )
73 }
74
75 isCreation () {
76 return true
77 }
78
79 getFormButtonTitle () {
80 return this.i18n('Create')
81 }
82}
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..048d143cd
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-edit.component.html
@@ -0,0 +1,105 @@
1<nav aria-label="breadcrumb">
2 <ol class="breadcrumb">
3 <li class="breadcrumb-item">
4 <a routerLink="/my-account/video-channels" i18n>My Channels</a>
5 </li>
6
7 <ng-container *ngIf="isCreation()">
8 <li class="breadcrumb-item active" i18n>Create</li>
9 </ng-container>
10 <ng-container *ngIf="!isCreation()">
11 <li class="breadcrumb-item active" i18n>Edit</li>
12 <li class="breadcrumb-item active" aria-current="page">
13 <a *ngIf="videoChannelToUpdate" [routerLink]="[ '/my-account/video-channels/update', videoChannelToUpdate?.nameWithHost ]">{{ videoChannelToUpdate?.displayName }}</a>
14 </li>
15 </ng-container>
16 </ol>
17</nav>
18
19<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
20
21<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
22
23 <div class="form-row"> <!-- channel grid -->
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>
26 <div *ngIf="!isCreation() && videoChannelToUpdate" class="video-channel-title" i18n>CHANNEL</div>
27 </div>
28
29 <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
30
31 <div class="form-group" *ngIf="isCreation()">
32 <label i18n for="name">Name</label>
33 <div class="input-group">
34 <input
35 type="text" id="name" i18n-placeholder placeholder="Example: my_channel"
36 formControlName="name" [ngClass]="{ 'input-error': formErrors['name'] }" class="form-control"
37 >
38 <div class="input-group-append">
39 <span class="input-group-text">@{{ instanceHost }}</span>
40 </div>
41 </div>
42 <div *ngIf="formErrors['name']" class="form-error">
43 {{ formErrors['name'] }}
44 </div>
45 </div>
46
47 <my-actor-avatar-info
48 *ngIf="!isCreation() && videoChannelToUpdate"
49 [actor]="videoChannelToUpdate" (avatarChange)="onAvatarChange($event)"
50 ></my-actor-avatar-info>
51
52 <div class="form-group">
53 <label i18n for="display-name">Display name</label>
54 <input
55 type="text" id="display-name" class="form-control"
56 formControlName="display-name" [ngClass]="{ 'input-error': formErrors['display-name'] }"
57 >
58 <div *ngIf="formErrors['display-name']" class="form-error">
59 {{ formErrors['display-name'] }}
60 </div>
61 </div>
62
63 <div class="form-group">
64 <label i18n for="description">Description</label>
65 <textarea
66 id="description" formControlName="description" class="form-control"
67 [ngClass]="{ 'input-error': formErrors['description'] }"
68 ></textarea>
69 <div *ngIf="formErrors.description" class="form-error">
70 {{ formErrors.description }}
71 </div>
72 </div>
73
74 <div class="form-group">
75 <label for="support">Support</label>
76 <my-help
77 helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support your channel (membership platform...).<br /><br />
78 When you will upload a video in this channel, the video support field will be automatically filled by this text."
79 ></my-help>
80 <my-markdown-textarea
81 id="support" formControlName="support" textareaMaxWidth="500px" markdownType="enhanced"
82 [classes]="{ 'input-error': formErrors['support'] }"
83 ></my-markdown-textarea>
84 <div *ngIf="formErrors.support" class="form-error">
85 {{ formErrors.support }}
86 </div>
87 </div>
88
89 <div class="form-group" *ngIf="isBulkUpdateVideosDisplayed()">
90 <my-peertube-checkbox
91 inputName="bulkVideosSupportUpdate" formControlName="bulkVideosSupportUpdate"
92 i18n-labelText labelText="Overwrite support field of all videos of this channel"
93 ></my-peertube-checkbox>
94 </div>
95
96 </div>
97 </div>
98
99 <div class="form-row"> <!-- submit placement block -->
100 <div class="col-md-7 col-xl-5"></div>
101 <div class="col-md-5 col-xl-5 d-inline-flex">
102 <input type="submit" value="{{ getFormButtonTitle() }}" [disabled]="!form.valid">
103 </div>
104 </div>
105</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..8f8af655c
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-edit.component.scss
@@ -0,0 +1,67 @@
1@import '_variables';
2@import '_mixins';
3
4label {
5 font-weight: $font-regular;
6 font-size: 100%;
7}
8
9.video-channel-title {
10 @include settings-big-title;
11}
12
13my-actor-avatar-info {
14 display: block;
15 margin-bottom: 20px;
16}
17
18.input-group {
19 @include peertube-input-group(fit-content);
20}
21
22.input-group-append {
23 height: 30px;
24}
25
26input {
27 &[type=text] {
28 @include peertube-input-text(340px);
29
30 display: block;
31
32 &#name {
33 width: auto;
34 flex-grow: 1;
35 }
36 }
37
38 &[type=submit] {
39 @include peertube-button;
40 @include orange-button;
41 margin-left: auto;
42 }
43}
44
45textarea {
46 @include peertube-textarea(500px, 150px);
47
48 display: block;
49}
50
51.peertube-select-container {
52 @include peertube-select-container(340px);
53}
54
55.breadcrumb {
56 @include breadcrumb;
57}
58
59@media screen and (max-width: $small-view) {
60 input[type=text]#name {
61 width: auto !important;
62 }
63
64 label[for=name] + div, textarea {
65 width: 100%;
66 }
67}
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..710c51d8e
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-edit.ts
@@ -0,0 +1,19 @@
1import { FormReactive } from '@app/shared/shared-forms'
2import { VideoChannel } from '@app/shared/shared-main'
3
4export abstract class MyAccountVideoChannelEdit extends FormReactive {
5 // We need it even in the create component because it's used in the edit template
6 videoChannelToUpdate: VideoChannel
7 instanceHost: string
8
9 abstract isCreation (): boolean
10 abstract getFormButtonTitle (): string
11
12 // We need this method so angular does not complain in child template that doesn't need this
13 onAvatarChange (formData: FormData) { /* empty */ }
14
15 // Should be implemented by the child
16 isBulkUpdateVideosDisplayed () {
17 return false
18 }
19}
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..489c437ea
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channel-update.component.ts
@@ -0,0 +1,135 @@
1import { Subscription } from 'rxjs'
2import { Component, OnDestroy, OnInit } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router'
4import { AuthService, Notifier, ServerService } from '@app/core'
5import { FormValidatorService, VideoChannelValidatorsService } from '@app/shared/shared-forms'
6import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { ServerConfig, VideoChannelUpdate } from '@shared/models'
9import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
10
11@Component({
12 selector: 'my-account-video-channel-update',
13 templateUrl: './my-account-video-channel-edit.component.html',
14 styleUrls: [ './my-account-video-channel-edit.component.scss' ]
15})
16export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelEdit implements OnInit, OnDestroy {
17 error: string
18 videoChannelToUpdate: VideoChannel
19
20 private paramsSub: Subscription
21 private oldSupportField: string
22 private serverConfig: ServerConfig
23
24 constructor (
25 protected formValidatorService: FormValidatorService,
26 private authService: AuthService,
27 private videoChannelValidatorsService: VideoChannelValidatorsService,
28 private notifier: Notifier,
29 private router: Router,
30 private route: ActivatedRoute,
31 private videoChannelService: VideoChannelService,
32 private i18n: I18n,
33 private serverService: ServerService
34 ) {
35 super()
36 }
37
38 ngOnInit () {
39 this.serverConfig = this.serverService.getTmpConfig()
40 this.serverService.getConfig()
41 .subscribe(config => this.serverConfig = config)
42
43 this.buildForm({
44 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME,
45 description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION,
46 support: this.videoChannelValidatorsService.VIDEO_CHANNEL_SUPPORT,
47 bulkVideosSupportUpdate: null
48 })
49
50 this.paramsSub = this.route.params.subscribe(routeParams => {
51 const videoChannelId = routeParams['videoChannelId']
52
53 this.videoChannelService.getVideoChannel(videoChannelId).subscribe(
54 videoChannelToUpdate => {
55 this.videoChannelToUpdate = videoChannelToUpdate
56
57 this.oldSupportField = videoChannelToUpdate.support
58
59 this.form.patchValue({
60 'display-name': videoChannelToUpdate.displayName,
61 description: videoChannelToUpdate.description,
62 support: videoChannelToUpdate.support
63 })
64 },
65
66 err => this.error = err.message
67 )
68 })
69 }
70
71 ngOnDestroy () {
72 if (this.paramsSub) this.paramsSub.unsubscribe()
73 }
74
75 formValidated () {
76 this.error = undefined
77
78 const body = this.form.value
79 const videoChannelUpdate: VideoChannelUpdate = {
80 displayName: body['display-name'],
81 description: body.description || null,
82 support: body.support || null,
83 bulkVideosSupportUpdate: body.bulkVideosSupportUpdate || false
84 }
85
86 this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.name, videoChannelUpdate).subscribe(
87 () => {
88 this.authService.refreshUserInformation()
89
90 this.notifier.success(
91 this.i18n('Video channel {{videoChannelName}} updated.', { videoChannelName: videoChannelUpdate.displayName })
92 )
93
94 this.router.navigate([ '/my-account', 'video-channels' ])
95 },
96
97 err => this.error = err.message
98 )
99 }
100
101 onAvatarChange (formData: FormData) {
102 this.videoChannelService.changeVideoChannelAvatar(this.videoChannelToUpdate.name, formData)
103 .subscribe(
104 data => {
105 this.notifier.success(this.i18n('Avatar changed.'))
106
107 this.videoChannelToUpdate.updateAvatar(data.avatar)
108 },
109
110 err => this.notifier.error(err.message)
111 )
112 }
113
114 get maxAvatarSize () {
115 return this.serverConfig.avatar.file.size.max
116 }
117
118 get avatarExtensions () {
119 return this.serverConfig.avatar.file.extensions.join(',')
120 }
121
122 isCreation () {
123 return false
124 }
125
126 getFormButtonTitle () {
127 return this.i18n('Update')
128 }
129
130 isBulkUpdateVideosDisplayed () {
131 if (this.oldSupportField === undefined) return false
132
133 return this.oldSupportField !== this.form.value['support']
134 }
135}
diff --git a/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels-routing.module.ts b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels-routing.module.ts
new file mode 100644
index 000000000..94037e18f
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels-routing.module.ts
@@ -0,0 +1,41 @@
1import { NgModule } from '@angular/core'
2import { RouterModule, Routes } from '@angular/router'
3import { MyAccountVideoChannelUpdateComponent } from './my-account-video-channel-update.component'
4import { MyAccountVideoChannelCreateComponent } from './my-account-video-channel-create.component'
5import { MyAccountVideoChannelsComponent } from './my-account-video-channels.component'
6
7const myAccountVideoChannelsRoutes: Routes = [
8 {
9 path: '',
10 component: MyAccountVideoChannelsComponent,
11 data: {
12 meta: {
13 title: 'Account video channels'
14 }
15 }
16 },
17 {
18 path: 'create',
19 component: MyAccountVideoChannelCreateComponent,
20 data: {
21 meta: {
22 title: 'Create new video channel'
23 }
24 }
25 },
26 {
27 path: 'update/:videoChannelId',
28 component: MyAccountVideoChannelUpdateComponent,
29 data: {
30 meta: {
31 title: 'Update video channel'
32 }
33 }
34 }
35]
36
37@NgModule({
38 imports: [ RouterModule.forChild(myAccountVideoChannelsRoutes) ],
39 exports: [ RouterModule ]
40})
41export class MyAccountVideoChannelsRoutingModule {}
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..b2e8210d3
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.html
@@ -0,0 +1,35 @@
1<h1 class="sr-only" i18n>My channels</h1>
2<div class="video-channels-header">
3 <a class="create-button" routerLink="create">
4 <my-global-icon iconName="add" aria-hidden="true"></my-global-icon>
5 <ng-container i18n>Create video channel</ng-container>
6 </a>
7</div>
8
9<div class="video-channels">
10 <div *ngFor="let videoChannel of videoChannels; let i = index" class="video-channel">
11 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]">
12 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
13 </a>
14
15 <div class="video-channel-info">
16 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]" class="video-channel-names" i18n-title title="Channel page">
17 <div class="video-channel-display-name">{{ videoChannel.displayName }}</div>
18 <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div>
19 </a>
20
21 <div i18n class="video-channel-followers">{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div>
22
23 <div i18n class="video-channel-videos">{videoChannel.videosCount, plural, =0 {No videos} =1 {1 video} other {{{ videoChannel.videosCount }} videos}}</div>
24
25 <div class="video-channel-buttons">
26 <my-edit-button [routerLink]="[ 'update', videoChannel.nameWithHost ]"></my-edit-button>
27 <my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button>
28 </div>
29
30 <div *ngIf="!isInSmallView" class="w-100 d-flex justify-content-end">
31 <p-chart *ngIf="videoChannelsChartData && videoChannelsChartData[i]" type="line" [data]="videoChannelsChartData[i]" [options]="chartOptions" width="40vw" height="100px"></p-chart>
32 </div>
33 </div>
34 </div>
35</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..76fb2cde0
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.scss
@@ -0,0 +1,115 @@
1@import '_variables';
2@import '_mixins';
3
4.create-button {
5 @include create-button;
6}
7
8::ng-deep .action-button {
9 &.action-button-edit {
10 margin-right: 10px;
11 }
12}
13
14.video-channel {
15 @include row-blocks;
16 padding-bottom: 0;
17
18 img {
19 @include avatar(80px);
20
21 margin-right: 10px;
22 }
23
24 .video-channel-info {
25 flex-grow: 1;
26
27 a.video-channel-names {
28 @include disable-default-a-behaviour;
29
30 width: fit-content;
31 display: flex;
32 align-items: baseline;
33 color: pvar(--mainForegroundColor);
34
35 .video-channel-display-name {
36 font-weight: $font-semibold;
37 font-size: 18px;
38 }
39
40 .video-channel-name {
41 font-size: 14px;
42 color: $grey-actor-name;
43 margin-left: 5px;
44 }
45
46 .video-channel-followers {
47
48 }
49 }
50 }
51
52 .video-channel-buttons {
53 margin-top: 10px;
54 min-width: 190px;
55 }
56}
57
58.video-channels-header {
59 text-align: right;
60 margin: 20px 0 50px;
61}
62
63::ng-deep .chartjs-render-monitor {
64 position: relative;
65 top: 1px;
66}
67
68@media screen and (max-width: $small-view) {
69 .video-channels-header {
70 text-align: center;
71 }
72
73 .video-channel {
74 padding-bottom: 10px;
75
76 .video-channel-info {
77 padding-bottom: 10px;
78 text-align: center;
79
80 .video-channel-names {
81 flex-direction: column;
82 align-items: center !important;
83 margin: auto;
84
85 .video-channel-name {
86 margin-left: 0px !important;
87 }
88 }
89 }
90
91 img {
92 margin-right: 0;
93 }
94
95 .video-channel-buttons {
96 align-self: center;
97 }
98 }
99}
100
101@media screen and (min-width: breakpoint(lg)) {
102 :host-context(.main-col:not(.expanded)) {
103 .video-channel-buttons {
104 float: right;
105 }
106 }
107}
108
109@media screen and (min-width: $small-view) {
110 :host-context(.expanded) {
111 .video-channel-buttons {
112 float: right;
113 }
114 }
115}
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..70510d7c9
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.component.ts
@@ -0,0 +1,154 @@
1import { ChartData } from 'chart.js'
2import { max, maxBy, min, minBy } from 'lodash-es'
3import { flatMap } from 'rxjs/operators'
4import { Component, OnInit } from '@angular/core'
5import { AuthService, ConfirmService, Notifier, ScreenService, User } from '@app/core'
6import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8
9@Component({
10 selector: 'my-account-video-channels',
11 templateUrl: './my-account-video-channels.component.html',
12 styleUrls: [ './my-account-video-channels.component.scss' ]
13})
14export class MyAccountVideoChannelsComponent implements OnInit {
15 videoChannels: VideoChannel[] = []
16 videoChannelsChartData: ChartData[]
17 videoChannelsMinimumDailyViews = 0
18 videoChannelsMaximumDailyViews: number
19
20 private user: User
21
22 constructor (
23 private authService: AuthService,
24 private notifier: Notifier,
25 private confirmService: ConfirmService,
26 private videoChannelService: VideoChannelService,
27 private screenService: ScreenService,
28 private i18n: I18n
29 ) {}
30
31 ngOnInit () {
32 this.user = this.authService.getUser()
33
34 this.loadVideoChannels()
35 }
36
37 get isInSmallView () {
38 return this.screenService.isInSmallView()
39 }
40
41 get chartOptions () {
42 return {
43 legend: {
44 display: false
45 },
46 scales: {
47 xAxes: [{
48 display: false
49 }],
50 yAxes: [{
51 display: false,
52 ticks: {
53 min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)),
54 max: Math.max(1, this.videoChannelsMaximumDailyViews)
55 }
56 }]
57 },
58 layout: {
59 padding: {
60 left: 15,
61 right: 15,
62 top: 10,
63 bottom: 0
64 }
65 },
66 elements: {
67 point: {
68 radius: 0
69 }
70 },
71 tooltips: {
72 mode: 'index',
73 intersect: false,
74 custom: function (tooltip: any) {
75 if (!tooltip) return
76 // disable displaying the color box
77 tooltip.displayColors = false
78 },
79 callbacks: {
80 label: (tooltip: any, data: any) => `${tooltip.value} views`
81 }
82 },
83 hover: {
84 mode: 'index',
85 intersect: false
86 }
87 }
88 }
89
90 async deleteVideoChannel (videoChannel: VideoChannel) {
91 const res = await this.confirmService.confirmWithInput(
92 this.i18n(
93 // tslint:disable
94 'Do you really want to delete {{channelDisplayName}}? It will delete {{videosCount}} videos uploaded in this channel, and you will not be able to create another channel with the same name ({{channelName}})!',
95 { channelDisplayName: videoChannel.displayName, videosCount: videoChannel.videosCount, channelName: videoChannel.name }
96 ),
97 this.i18n(
98 'Please type the display name of the video channel ({{displayName}}) to confirm',
99 { displayName: videoChannel.displayName }
100 ),
101 videoChannel.displayName,
102 this.i18n('Delete')
103 )
104 if (res === false) return
105
106 this.videoChannelService.removeVideoChannel(videoChannel)
107 .subscribe(
108 () => {
109 this.loadVideoChannels()
110 this.notifier.success(
111 this.i18n('Video channel {{videoChannelName}} deleted.', { videoChannelName: videoChannel.displayName })
112 )
113 },
114
115 error => this.notifier.error(error.message)
116 )
117 }
118
119 private loadVideoChannels () {
120 this.authService.userInformationLoaded
121 .pipe(flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account, null, true)))
122 .subscribe(res => {
123 this.videoChannels = res.data
124
125 // chart data
126 this.videoChannelsChartData = this.videoChannels.map(v => ({
127 labels: v.viewsPerDay.map(day => day.date.toLocaleDateString()),
128 datasets: [
129 {
130 label: this.i18n('Views for the day'),
131 data: v.viewsPerDay.map(day => day.views),
132 fill: false,
133 borderColor: "#c6c6c6"
134 }
135 ]
136 } as ChartData))
137
138 // chart options that depend on chart data:
139 // we don't want to skew values and have min at 0, so we define what the floor/ceiling is here
140 this.videoChannelsMinimumDailyViews = min(
141 this.videoChannels.map(v => minBy( // compute local minimum daily views for each channel, by their "views" attribute
142 v.viewsPerDay,
143 day => day.views
144 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
145 )
146 this.videoChannelsMaximumDailyViews = max(
147 this.videoChannels.map(v => maxBy( // compute local maximum daily views for each channel, by their "views" attribute
148 v.viewsPerDay,
149 day => day.views
150 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
151 )
152 })
153 }
154}
diff --git a/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.module.ts b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.module.ts
new file mode 100644
index 000000000..f8c6ad56b
--- /dev/null
+++ b/client/src/app/+my-account/+my-account-video-channels/my-account-video-channels.module.ts
@@ -0,0 +1,31 @@
1import { ChartModule } from 'primeng/chart'
2import { NgModule } from '@angular/core'
3import { SharedFormModule } from '@app/shared/shared-forms'
4import { SharedGlobalIconModule } from '@app/shared/shared-icons'
5import { SharedMainModule } from '@app/shared/shared-main'
6import { MyAccountVideoChannelCreateComponent } from './my-account-video-channel-create.component'
7import { MyAccountVideoChannelUpdateComponent } from './my-account-video-channel-update.component'
8import { MyAccountVideoChannelsRoutingModule } from './my-account-video-channels-routing.module'
9import { MyAccountVideoChannelsComponent } from './my-account-video-channels.component'
10
11@NgModule({
12 imports: [
13 MyAccountVideoChannelsRoutingModule,
14
15 ChartModule,
16
17 SharedMainModule,
18 SharedFormModule,
19 SharedGlobalIconModule
20 ],
21
22 declarations: [
23 MyAccountVideoChannelsComponent,
24 MyAccountVideoChannelCreateComponent,
25 MyAccountVideoChannelUpdateComponent
26 ],
27
28 exports: [],
29 providers: []
30})
31export class MyAccountVideoChannelsModule { }