]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
Don't send undefined with HTML input
[github/Chocobozzz/PeerTube.git] / client / src / app / +my-library / +my-video-channels / my-video-channels.component.ts
CommitLineData
818045ef 1import { ChartData, ChartOptions, TooltipItem, TooltipModel } from 'chart.js'
67ed6552 2import { max, maxBy, min, minBy } from 'lodash-es'
439f5dfc 3import { Subject } from 'rxjs'
2e46eb97 4import { Component } from '@angular/core'
439f5dfc 5import { AuthService, ComponentPagination, ConfirmService, hasMoreItems, Notifier, ScreenService } from '@app/core'
67ed6552 6import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
08c1efbe
C
7
8@Component({
17119e4a
C
9 templateUrl: './my-video-channels.component.html',
10 styleUrls: [ './my-video-channels.component.scss' ]
08c1efbe 11})
2e46eb97 12export class MyVideoChannelsComponent {
4f5d0459
RK
13 totalItems: number
14
08c1efbe 15 videoChannels: VideoChannel[] = []
2e46eb97 16
3d527ba1 17 videoChannelsChartData: ChartData[]
08c1efbe 18
818045ef 19 chartOptions: ChartOptions
4f926722 20
2e46eb97 21 search: string
08c1efbe 22
439f5dfc
C
23 onChannelDataSubject = new Subject<any>()
24
25 pagination: ComponentPagination = {
26 currentPage: 1,
27 itemsPerPage: 10,
28 totalItems: null
29 }
30
08c1efbe
C
31 constructor (
32 private authService: AuthService,
f8b2c1b4 33 private notifier: Notifier,
08c1efbe 34 private confirmService: ConfirmService,
b1d40cff 35 private videoChannelService: VideoChannelService,
66357162 36 private screenService: ScreenService
2e46eb97 37 ) {}
08c1efbe 38
8165d00a
RK
39 get isInSmallView () {
40 return this.screenService.isInSmallView()
41 }
42
2e46eb97
C
43 onSearch (search: string) {
44 this.search = search
439f5dfc
C
45
46 this.pagination.currentPage = 1
47 this.videoChannels = []
48
49 this.authService.userInformationLoaded
50 .subscribe(() => this.loadMoreVideoChannels())
4f5d0459
RK
51 }
52
08c1efbe 53 async deleteVideoChannel (videoChannel: VideoChannel) {
d12b40fb 54 const res = await this.confirmService.confirmWithExpectedInput(
66357162
C
55 $localize`Do you really want to delete ${videoChannel.displayName}?
56It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another
57channel with the same name (${videoChannel.name})!`,
58
01af6462 59 $localize`Please type the name of the video channel (${videoChannel.name}) to confirm`,
66357162 60
01af6462 61 videoChannel.name,
d88c490d 62
66357162 63 $localize`Delete`
08c1efbe
C
64 )
65 if (res === false) return
66
67 this.videoChannelService.removeVideoChannel(videoChannel)
1378c0d3
C
68 .subscribe({
69 next: () => {
439f5dfc 70 this.videoChannels = this.videoChannels.filter(c => c.id !== videoChannel.id)
66357162 71 this.notifier.success($localize`Video channel ${videoChannel.displayName} deleted.`)
08c1efbe
C
72 },
73
1378c0d3
C
74 error: err => this.notifier.error(err.message)
75 })
08c1efbe
C
76 }
77
439f5dfc
C
78 onNearOfBottom () {
79 if (!hasMoreItems(this.pagination)) return
80
81 this.pagination.currentPage += 1
dc2b2938 82
439f5dfc
C
83 this.loadMoreVideoChannels()
84 }
85
86 private loadMoreVideoChannels () {
87 const user = this.authService.getUser()
88 const options = {
89 account: user.account,
90 withStats: true,
91 search: this.search,
92 componentPagination: this.pagination,
93 sort: '-updatedAt'
94 }
95
96 return this.videoChannelService.listAccountVideoChannels(options)
97 .subscribe(res => {
98 this.videoChannels = this.videoChannels.concat(res.data)
99 this.totalItems = res.total
100
101 // chart data
102 this.videoChannelsChartData = this.videoChannels.map(v => ({
103 labels: v.viewsPerDay.map(day => day.date.toLocaleDateString()),
104 datasets: [
105 {
106 label: $localize`Views for the day`,
107 data: v.viewsPerDay.map(day => day.views),
108 fill: false,
109 borderColor: '#c6c6c6'
110 }
111 ]
112 } as ChartData))
113
114 this.buildChartOptions()
115
116 this.onChannelDataSubject.next(res.data)
117 })
08c1efbe 118 }
4f926722
C
119
120 private buildChartOptions () {
439f5dfc
C
121 // chart options that depend on chart data:
122 // we don't want to skew values and have min at 0, so we define what the floor/ceiling is here
123 const videoChannelsMinimumDailyViews = min(
124 // compute local minimum daily views for each channel, by their "views" attribute
125 this.videoChannels.map(v => minBy(
126 v.viewsPerDay,
127 day => day.views
128 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
129 )
130
131 const videoChannelsMaximumDailyViews = max(
132 // compute local maximum daily views for each channel, by their "views" attribute
133 this.videoChannels.map(v => maxBy(
134 v.viewsPerDay,
135 day => day.views
136 ).views) // the object returned is a ViewPerDate, so we still need to get the views attribute
137 )
138
4f926722 139 this.chartOptions = {
818045ef
C
140 plugins: {
141 legend: {
142 display: false
143 },
144 tooltip: {
145 mode: 'index',
146 intersect: false,
147 external: function ({ tooltip }: { tooltip: TooltipModel<any> }) {
148 if (!tooltip) return
149
150 // disable displaying the color box
151 tooltip.options.displayColors = false
152 },
153 callbacks: {
154 label: (tooltip: TooltipItem<any>) => `${tooltip.formattedValue} views`
155 }
156 }
4f926722
C
157 },
158 scales: {
818045ef 159 x: {
4f926722 160 display: false
818045ef
C
161 },
162 y: {
4f926722 163 display: false,
439f5dfc
C
164 min: Math.max(0, videoChannelsMinimumDailyViews - (3 * videoChannelsMaximumDailyViews / 100)),
165 max: Math.max(1, videoChannelsMaximumDailyViews)
818045ef 166 }
4f926722
C
167 },
168 layout: {
169 padding: {
170 left: 15,
171 right: 15,
172 top: 10,
173 bottom: 0
174 }
175 },
176 elements: {
177 point: {
178 radius: 0
179 }
180 },
4f926722
C
181 hover: {
182 mode: 'index',
183 intersect: false
184 }
185 }
186 }
08c1efbe 187}