aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
authorRigel Kent <sendmemail@rigelk.eu>2020-03-23 10:14:05 +0100
committerChocobozzz <chocobozzz@cpy.re>2020-03-31 10:29:24 +0200
commit8165d00ac6263cf3c0d61d450960ef36635084ff (patch)
treec0587121cd8dbdfc246a5bc74c08805830140a77 /client/src
parent628c155338cf106365a06ca021b9f244b784c003 (diff)
downloadPeerTube-8165d00ac6263cf3c0d61d450960ef36635084ff.tar.gz
PeerTube-8165d00ac6263cf3c0d61d450960ef36635084ff.tar.zst
PeerTube-8165d00ac6263cf3c0d61d450960ef36635084ff.zip
View stats for channels
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.html11
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.scss8
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channels.component.ts78
-rw-r--r--client/src/app/+my-account/my-account.module.ts6
-rw-r--r--client/src/app/shared/video-channel/video-channel.model.ts7
5 files changed, 101 insertions, 9 deletions
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 2461aa3f5..94e74938b 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
@@ -6,7 +6,7 @@
6</div> 6</div>
7 7
8<div class="video-channels"> 8<div class="video-channels">
9 <div *ngFor="let videoChannel of videoChannels" class="video-channel"> 9 <div *ngFor="let videoChannel of videoChannels; let i = index" class="video-channel">
10 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"> 10 <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]">
11 <img [src]="videoChannel.avatarUrl" alt="Avatar" /> 11 <img [src]="videoChannel.avatarUrl" alt="Avatar" />
12 </a> 12 </a>
@@ -17,13 +17,16 @@
17 <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div> 17 <div class="video-channel-name">{{ videoChannel.nameWithHost }}</div>
18 </a> 18 </a>
19 19
20 <div i18n class="video-channel-followers">{{ videoChannel.followersCount }} subscribers</div> 20 <div i18n class="video-channel-followers">{videoChannel.followersCount, plural, =1 {1 subscriber} other {{{ videoChannel.followersCount }} subscribers}}</div>
21
22 <div *ngIf="!isInSmallView" class="w-100 d-flex justify-content-end">
23 <p-chart *ngIf="videoChannelsData && videoChannelsData[i]" type="line" [data]="videoChannelsData[i]" [options]="chartOptions" width="40vw" height="100px"></p-chart>
24 </div>
21 </div> 25 </div>
22 26
23 <div class="video-channel-buttons"> 27 <div class="video-channel-buttons">
24 <my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button>
25
26 <my-edit-button [routerLink]="[ 'update', videoChannel.nameWithHost ]"></my-edit-button> 28 <my-edit-button [routerLink]="[ 'update', videoChannel.nameWithHost ]"></my-edit-button>
29 <my-delete-button (click)="deleteVideoChannel(videoChannel)"></my-delete-button>
27 </div> 30 </div>
28 </div> 31 </div>
29</div> 32</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
index db0c7f94f..c0dc41f12 100644
--- 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
@@ -6,13 +6,14 @@
6} 6}
7 7
8::ng-deep .action-button { 8::ng-deep .action-button {
9 &.action-button-delete { 9 &.action-button-edit {
10 margin-right: 10px; 10 margin-right: 10px;
11 } 11 }
12} 12}
13 13
14.video-channel { 14.video-channel {
15 @include row-blocks; 15 @include row-blocks;
16 padding-bottom: 0;
16 17
17 img { 18 img {
18 @include avatar(80px); 19 @include avatar(80px);
@@ -58,6 +59,11 @@
58 margin: 20px 0 50px; 59 margin: 20px 0 50px;
59} 60}
60 61
62::ng-deep .chartjs-render-monitor {
63 position: relative;
64 top: 1px;
65}
66
61@media screen and (max-width: $small-view) { 67@media screen and (max-width: $small-view) {
62 .video-channels-header { 68 .video-channels-header {
63 text-align: center; 69 text-align: center;
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
index 3b01b6c9f..eeab3a8dd 100644
--- 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
@@ -4,9 +4,11 @@ import { AuthService } from '../../core/auth'
4import { ConfirmService } from '../../core/confirm' 4import { ConfirmService } from '../../core/confirm'
5import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 5import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
6import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' 6import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
7import { ScreenService } from '@app/shared/misc/screen.service'
7import { User } from '@app/shared' 8import { User } from '@app/shared'
8import { flatMap } from 'rxjs/operators' 9import { flatMap } from 'rxjs/operators'
9import { I18n } from '@ngx-translate/i18n-polyfill' 10import { I18n } from '@ngx-translate/i18n-polyfill'
11import { minBy, maxBy } from 'lodash-es'
10 12
11@Component({ 13@Component({
12 selector: 'my-account-video-channels', 14 selector: 'my-account-video-channels',
@@ -15,6 +17,9 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
15}) 17})
16export class MyAccountVideoChannelsComponent implements OnInit { 18export class MyAccountVideoChannelsComponent implements OnInit {
17 videoChannels: VideoChannel[] = [] 19 videoChannels: VideoChannel[] = []
20 videoChannelsData: any[]
21 videoChannelsMinimumDailyViews = 0
22 videoChannelsMaximumDailyViews: number
18 23
19 private user: User 24 private user: User
20 25
@@ -23,6 +28,7 @@ export class MyAccountVideoChannelsComponent implements OnInit {
23 private notifier: Notifier, 28 private notifier: Notifier,
24 private confirmService: ConfirmService, 29 private confirmService: ConfirmService,
25 private videoChannelService: VideoChannelService, 30 private videoChannelService: VideoChannelService,
31 private screenService: ScreenService,
26 private i18n: I18n 32 private i18n: I18n
27 ) {} 33 ) {}
28 34
@@ -32,6 +38,61 @@ export class MyAccountVideoChannelsComponent implements OnInit {
32 this.loadVideoChannels() 38 this.loadVideoChannels()
33 } 39 }
34 40
41 get isInSmallView () {
42 return this.screenService.isInSmallView()
43 }
44
45 get chartOptions () {
46 return {
47 legend: {
48 display: false
49 },
50 scales: {
51 xAxes: [{
52 display: false
53 }],
54 yAxes: [{
55 display: false,
56 ticks: {
57 min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)),
58 max: this.videoChannelsMaximumDailyViews
59 }
60 }],
61 },
62 layout: {
63 padding: {
64 left: 15,
65 right: 15,
66 top: 10,
67 bottom: 0
68 }
69 },
70 elements: {
71 point:{
72 radius: 0
73 }
74 },
75 tooltips: {
76 mode: 'index',
77 intersect: false,
78 custom: function (tooltip: any) {
79 if (!tooltip) return;
80 // disable displaying the color box;
81 tooltip.displayColors = false;
82 },
83 callbacks: {
84 label: function (tooltip: any, data: any) {
85 return `${tooltip.value} views`;
86 }
87 }
88 },
89 hover: {
90 mode: 'index',
91 intersect: false
92 }
93 }
94 }
95
35 async deleteVideoChannel (videoChannel: VideoChannel) { 96 async deleteVideoChannel (videoChannel: VideoChannel) {
36 const res = await this.confirmService.confirmWithInput( 97 const res = await this.confirmService.confirmWithInput(
37 this.i18n( 98 this.i18n(
@@ -64,6 +125,21 @@ export class MyAccountVideoChannelsComponent implements OnInit {
64 private loadVideoChannels () { 125 private loadVideoChannels () {
65 this.authService.userInformationLoaded 126 this.authService.userInformationLoaded
66 .pipe(flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account))) 127 .pipe(flatMap(() => this.videoChannelService.listAccountVideoChannels(this.user.account)))
67 .subscribe(res => this.videoChannels = res.data) 128 .subscribe(res => {
129 this.videoChannels = res.data
130 this.videoChannelsData = this.videoChannels.map(v => ({
131 labels: v.viewsPerDay.map(day => day.date.toLocaleDateString()),
132 datasets: [
133 {
134 label: this.i18n('Views for the day'),
135 data: v.viewsPerDay.map(day => day.views),
136 fill: false,
137 borderColor: "#c6c6c6"
138 }
139 ]
140 }))
141 this.videoChannelsMinimumDailyViews = minBy(this.videoChannels.map(v => minBy(v.viewsPerDay, day => day.views)), day => day.views).views
142 this.videoChannelsMaximumDailyViews = maxBy(this.videoChannels.map(v => maxBy(v.viewsPerDay, day => day.views)), day => day.views).views
143 })
68 } 144 }
69} 145}
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts
index f8c04cb4d..42b61bba6 100644
--- a/client/src/app/+my-account/my-account.module.ts
+++ b/client/src/app/+my-account/my-account.module.ts
@@ -1,7 +1,8 @@
1import { TableModule } from 'primeng/table'
2import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { TableModule } from 'primeng/table'
3import { AutoCompleteModule } from 'primeng/autocomplete' 3import { AutoCompleteModule } from 'primeng/autocomplete'
4import { InputSwitchModule } from 'primeng/inputswitch' 4import { InputSwitchModule } from 'primeng/inputswitch'
5import { ChartModule } from 'primeng/chart'
5import { SharedModule } from '../shared' 6import { SharedModule } from '../shared'
6import { MyAccountRoutingModule } from './my-account-routing.module' 7import { MyAccountRoutingModule } from './my-account-routing.module'
7import { MyAccountChangePasswordComponent } from './my-account-settings/my-account-change-password/my-account-change-password.component' 8import { MyAccountChangePasswordComponent } from './my-account-settings/my-account-change-password/my-account-change-password.component'
@@ -44,7 +45,8 @@ import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-setti
44 SharedModule, 45 SharedModule,
45 TableModule, 46 TableModule,
46 InputSwitchModule, 47 InputSwitchModule,
47 DragDropModule 48 DragDropModule,
49 ChartModule
48 ], 50 ],
49 51
50 declarations: [ 52 declarations: [
diff --git a/client/src/app/shared/video-channel/video-channel.model.ts b/client/src/app/shared/video-channel/video-channel.model.ts
index fec050cde..ee3288d7a 100644
--- a/client/src/app/shared/video-channel/video-channel.model.ts
+++ b/client/src/app/shared/video-channel/video-channel.model.ts
@@ -1,4 +1,4 @@
1import { VideoChannel as ServerVideoChannel } from '../../../../../shared/models/videos' 1import { VideoChannel as ServerVideoChannel, viewsPerTime } from '../../../../../shared/models/videos'
2import { Actor } from '../actor/actor.model' 2import { Actor } from '../actor/actor.model'
3import { Account } from '../../../../../shared/models/actors' 3import { Account } from '../../../../../shared/models/actors'
4 4
@@ -12,6 +12,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
12 ownerAccount?: Account 12 ownerAccount?: Account
13 ownerBy?: string 13 ownerBy?: string
14 ownerAvatarUrl?: string 14 ownerAvatarUrl?: string
15 viewsPerDay?: viewsPerTime[]
15 16
16 constructor (hash: ServerVideoChannel) { 17 constructor (hash: ServerVideoChannel) {
17 super(hash) 18 super(hash)
@@ -23,6 +24,10 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
23 this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host) 24 this.nameWithHost = Actor.CREATE_BY_STRING(this.name, this.host)
24 this.nameWithHostForced = Actor.CREATE_BY_STRING(this.name, this.host, true) 25 this.nameWithHostForced = Actor.CREATE_BY_STRING(this.name, this.host, true)
25 26
27 if (hash.viewsPerDay) {
28 this.viewsPerDay = hash.viewsPerDay.map(v => ({ ...v, date: new Date(v.date)}))
29 }
30
26 if (hash.ownerAccount) { 31 if (hash.ownerAccount) {
27 this.ownerAccount = hash.ownerAccount 32 this.ownerAccount = hash.ownerAccount
28 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host) 33 this.ownerBy = Actor.CREATE_BY_STRING(hash.ownerAccount.name, hash.ownerAccount.host)