]>
Commit | Line | Data |
---|---|---|
818045ef | 1 | import { ChartData, ChartOptions, TooltipItem, TooltipModel } from 'chart.js' |
67ed6552 | 2 | import { max, maxBy, min, minBy } from 'lodash-es' |
439f5dfc | 3 | import { Subject } from 'rxjs' |
2e46eb97 | 4 | import { Component } from '@angular/core' |
439f5dfc | 5 | import { AuthService, ComponentPagination, ConfirmService, hasMoreItems, Notifier, ScreenService } from '@app/core' |
67ed6552 | 6 | import { 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 | 12 | export 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}? |
56 | It will delete ${videoChannel.videosCount} videos uploaded in this channel, and you will not be able to create another | |
57 | channel 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 | } |