diff options
Diffstat (limited to 'client/src/app/+videos/video-list/videos-list-common-page.component.ts')
-rw-r--r-- | client/src/app/+videos/video-list/videos-list-common-page.component.ts | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/client/src/app/+videos/video-list/videos-list-common-page.component.ts b/client/src/app/+videos/video-list/videos-list-common-page.component.ts new file mode 100644 index 000000000..ba64d4fec --- /dev/null +++ b/client/src/app/+videos/video-list/videos-list-common-page.component.ts | |||
@@ -0,0 +1,219 @@ | |||
1 | import { Component, OnDestroy, OnInit } from '@angular/core' | ||
2 | import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router' | ||
3 | import { ComponentPaginationLight, DisableForReuseHook, MetaService, RedirectService, ServerService } from '@app/core' | ||
4 | import { HooksService } from '@app/core/plugins/hooks.service' | ||
5 | import { VideoService } from '@app/shared/shared-main' | ||
6 | import { VideoFilters, VideoFilterScope } from '@app/shared/shared-video-miniature/video-filters.model' | ||
7 | import { ClientFilterHookName, VideoSortField } from '@shared/models' | ||
8 | import { Subscription } from 'rxjs' | ||
9 | |||
10 | export type VideosListCommonPageRouteData = { | ||
11 | sort: VideoSortField | ||
12 | |||
13 | scope: VideoFilterScope | ||
14 | hookParams: ClientFilterHookName | ||
15 | hookResult: ClientFilterHookName | ||
16 | } | ||
17 | |||
18 | @Component({ | ||
19 | templateUrl: './videos-list-common-page.component.html' | ||
20 | }) | ||
21 | export class VideosListCommonPageComponent implements OnInit, OnDestroy, DisableForReuseHook { | ||
22 | getVideosObservableFunction = this.getVideosObservable.bind(this) | ||
23 | getSyndicationItemsFunction = this.getSyndicationItems.bind(this) | ||
24 | baseRouteBuilderFunction = this.baseRouteBuilder.bind(this) | ||
25 | |||
26 | title: string | ||
27 | titleTooltip: string | ||
28 | |||
29 | groupByDate: boolean | ||
30 | |||
31 | defaultSort: VideoSortField | ||
32 | defaultScope: VideoFilterScope | ||
33 | |||
34 | hookParams: ClientFilterHookName | ||
35 | hookResult: ClientFilterHookName | ||
36 | |||
37 | loadUserVideoPreferences = true | ||
38 | |||
39 | displayFilters = true | ||
40 | |||
41 | disabled = false | ||
42 | |||
43 | private trendingDays: number | ||
44 | private routeSub: Subscription | ||
45 | |||
46 | constructor ( | ||
47 | private server: ServerService, | ||
48 | private route: ActivatedRoute, | ||
49 | private videoService: VideoService, | ||
50 | private hooks: HooksService, | ||
51 | private meta: MetaService, | ||
52 | private redirectService: RedirectService | ||
53 | ) { | ||
54 | } | ||
55 | |||
56 | ngOnInit () { | ||
57 | this.trendingDays = this.server.getHTMLConfig().trending.videos.intervalDays | ||
58 | |||
59 | this.routeSub = this.route.params.subscribe(params => { | ||
60 | this.update(params['page']) | ||
61 | }) | ||
62 | } | ||
63 | |||
64 | ngOnDestroy () { | ||
65 | if (this.routeSub) this.routeSub.unsubscribe() | ||
66 | } | ||
67 | |||
68 | getVideosObservable (pagination: ComponentPaginationLight, filters: VideoFilters) { | ||
69 | const params = { | ||
70 | ...filters.toVideosAPIObject(), | ||
71 | |||
72 | videoPagination: pagination, | ||
73 | skipCount: true | ||
74 | } | ||
75 | |||
76 | return this.hooks.wrapObsFun( | ||
77 | this.videoService.getVideos.bind(this.videoService), | ||
78 | params, | ||
79 | 'common', | ||
80 | this.hookParams, | ||
81 | this.hookResult | ||
82 | ) | ||
83 | } | ||
84 | |||
85 | getSyndicationItems (filters: VideoFilters) { | ||
86 | const result = filters.toVideosAPIObject() | ||
87 | |||
88 | return this.videoService.getVideoFeedUrls(result.sort, result.filter) | ||
89 | } | ||
90 | |||
91 | onFiltersChanged (filters: VideoFilters) { | ||
92 | this.buildTitle(filters.scope, filters.sort) | ||
93 | this.updateGroupByDate(filters.sort) | ||
94 | } | ||
95 | |||
96 | baseRouteBuilder (filters: VideoFilters) { | ||
97 | const sanitizedSort = this.getSanitizedSort(filters.sort) | ||
98 | |||
99 | let suffix: string | ||
100 | |||
101 | if (filters.scope === 'local') suffix = 'local' | ||
102 | else if (sanitizedSort === 'publishedAt') suffix = 'recently-added' | ||
103 | else suffix = 'trending' | ||
104 | |||
105 | return [ '/videos', suffix ] | ||
106 | } | ||
107 | |||
108 | disableForReuse () { | ||
109 | this.disabled = true | ||
110 | } | ||
111 | |||
112 | enabledForReuse () { | ||
113 | this.disabled = false | ||
114 | } | ||
115 | |||
116 | update (page: string) { | ||
117 | const data = this.getData(page) | ||
118 | |||
119 | this.hookParams = data.hookParams | ||
120 | this.hookResult = data.hookResult | ||
121 | |||
122 | this.defaultSort = data.sort | ||
123 | this.defaultScope = data.scope | ||
124 | |||
125 | this.buildTitle() | ||
126 | this.updateGroupByDate(this.defaultSort) | ||
127 | |||
128 | this.meta.setTitle(this.title) | ||
129 | } | ||
130 | |||
131 | private getData (page: string) { | ||
132 | if (page === 'trending') return this.generateTrendingData(this.route.snapshot) | ||
133 | |||
134 | if (page === 'local') return this.generateLocalData() | ||
135 | |||
136 | return this.generateRecentlyAddedData() | ||
137 | } | ||
138 | |||
139 | private generateRecentlyAddedData (): VideosListCommonPageRouteData { | ||
140 | return { | ||
141 | sort: '-publishedAt', | ||
142 | scope: 'federated', | ||
143 | hookParams: 'filter:api.recently-added-videos.videos.list.params', | ||
144 | hookResult: 'filter:api.recently-added-videos.videos.list.result' | ||
145 | } | ||
146 | } | ||
147 | |||
148 | private generateLocalData (): VideosListCommonPageRouteData { | ||
149 | return { | ||
150 | sort: '-publishedAt' as VideoSortField, | ||
151 | scope: 'local', | ||
152 | hookParams: 'filter:api.local-videos.videos.list.params', | ||
153 | hookResult: 'filter:api.local-videos.videos.list.result' | ||
154 | } | ||
155 | } | ||
156 | |||
157 | private generateTrendingData (route: ActivatedRouteSnapshot): VideosListCommonPageRouteData { | ||
158 | const sort = route.queryParams['sort'] ?? this.parseTrendingAlgorithm(this.redirectService.getDefaultTrendingAlgorithm()) | ||
159 | |||
160 | return { | ||
161 | sort, | ||
162 | scope: 'federated', | ||
163 | hookParams: 'filter:api.trending-videos.videos.list.params', | ||
164 | hookResult: 'filter:api.trending-videos.videos.list.result' | ||
165 | } | ||
166 | } | ||
167 | |||
168 | private parseTrendingAlgorithm (algorithm: string): VideoSortField { | ||
169 | switch (algorithm) { | ||
170 | case 'most-viewed': | ||
171 | return '-trending' | ||
172 | |||
173 | case 'most-liked': | ||
174 | return '-likes' | ||
175 | |||
176 | default: | ||
177 | return '-' + algorithm as VideoSortField | ||
178 | } | ||
179 | } | ||
180 | |||
181 | private updateGroupByDate (sort: VideoSortField) { | ||
182 | this.groupByDate = sort === '-publishedAt' || sort === 'publishedAt' | ||
183 | } | ||
184 | |||
185 | private buildTitle (scope: VideoFilterScope = this.defaultScope, sort: VideoSortField = this.defaultSort) { | ||
186 | const sanitizedSort = this.getSanitizedSort(sort) | ||
187 | |||
188 | if (scope === 'local') { | ||
189 | this.title = $localize`Local videos` | ||
190 | this.titleTooltip = $localize`Only videos uploaded on this instance are displayed` | ||
191 | return | ||
192 | } | ||
193 | |||
194 | if (sanitizedSort === 'publishedAt') { | ||
195 | this.title = $localize`Recently added` | ||
196 | this.titleTooltip = undefined | ||
197 | return | ||
198 | } | ||
199 | |||
200 | if ([ 'best', 'hot', 'trending', 'likes' ].includes(sanitizedSort)) { | ||
201 | this.title = $localize`Trending` | ||
202 | |||
203 | if (sanitizedSort === 'best') this.titleTooltip = $localize`Videos with the most interactions for recent videos, minus user history` | ||
204 | if (sanitizedSort === 'hot') this.titleTooltip = $localize`Videos with the most interactions for recent videos` | ||
205 | if (sanitizedSort === 'likes') this.titleTooltip = $localize`Videos that have the most likes` | ||
206 | |||
207 | if (sanitizedSort === 'trending') { | ||
208 | if (this.trendingDays === 1) this.titleTooltip = $localize`Videos with the most views during the last 24 hours` | ||
209 | else this.titleTooltip = $localize`Videos with the most views during the last ${this.trendingDays} days` | ||
210 | } | ||
211 | |||
212 | return | ||
213 | } | ||
214 | } | ||
215 | |||
216 | private getSanitizedSort (sort: VideoSortField) { | ||
217 | return sort.replace(/^-/, '') as VideoSortField | ||
218 | } | ||
219 | } | ||