]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/shared/video/abstract-video-list.ts
Add language filters in user preferences
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / video / abstract-video-list.ts
CommitLineData
3caf77d3 1import { debounceTime, first, tap } from 'rxjs/operators'
489290b8 2import { OnDestroy, OnInit } from '@angular/core'
fd45e8f4 3import { ActivatedRoute, Router } from '@angular/router'
3caf77d3 4import { fromEvent, Observable, of, Subscription } from 'rxjs'
b2731bff 5import { AuthService } from '../../core/auth'
4635f59d 6import { ComponentPagination } from '../rest/component-pagination.model'
7b87d2d5 7import { VideoSortField } from './sort-field.type'
202f6b6c 8import { Video } from './video.model'
bbe0f064 9import { ScreenService } from '@app/shared/misc/screen.service'
abf325b4 10import { MiniatureDisplayOptions, OwnerDisplayType } from '@app/shared/video/video-miniature.component'
c199c427 11import { Syndication } from '@app/shared/video/syndication.model'
489290b8
C
12import { Notifier, ServerService } from '@app/core'
13import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
34c7f429 14import { I18n } from '@ngx-translate/i18n-polyfill'
93aa8552 15import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date'
34c7f429
C
16
17enum GroupDate {
18 UNKNOWN = 0,
19 TODAY = 1,
20 YESTERDAY = 2,
93aa8552
C
21 LAST_WEEK = 3,
22 LAST_MONTH = 4,
34c7f429
C
23 OLDER = 5
24}
0cd4344f 25
489290b8 26export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableForReuseHook {
4635f59d 27 pagination: ComponentPagination = {
fd45e8f4 28 currentPage: 1,
489290b8 29 itemsPerPage: 25,
fd45e8f4
C
30 totalItems: null
31 }
136cce4d 32 sort: VideoSortField = '-publishedAt'
489290b8 33
d59cba29 34 categoryOneOf?: number
3caf77d3 35 languageOneOf?: string[]
136cce4d 36 defaultSort: VideoSortField = '-publishedAt'
489290b8 37
c199c427 38 syndicationItems: Syndication[] = []
244e76a5 39
f3aaa9a9 40 loadOnInit = true
3caf77d3 41 useUserVideoLanguagePreferences = false
22a16e36 42 ownerDisplayType: OwnerDisplayType = 'account'
017c3dca 43 displayModerationBlock = false
9b4b15f9 44 titleTooltip: string
3a0fb65c 45 displayVideoActions = true
34c7f429 46 groupByDate = false
fd45e8f4 47
3caf77d3 48 videos: Video[] = []
489290b8 49 disabled = false
9af61e84 50
abf325b4
C
51 displayOptions: MiniatureDisplayOptions = {
52 date: true,
53 views: true,
54 by: true,
55 privacyLabel: true,
56 privacyText: false,
57 state: false,
58 blacklistInfo: false
59 }
60
f8b2c1b4 61 protected abstract notifier: Notifier
b2731bff 62 protected abstract authService: AuthService
b2731bff 63 protected abstract route: ActivatedRoute
489290b8 64 protected abstract serverService: ServerService
bbe0f064 65 protected abstract screenService: ScreenService
489290b8 66 protected abstract router: Router
34c7f429 67 protected abstract i18n: I18n
9bf9d2a5 68 abstract titlePage: string
c88593f7 69
9af61e84 70 private resizeSubscription: Subscription
489290b8
C
71 private angularState: number
72
34c7f429
C
73 private groupedDateLabels: { [id in GroupDate]: string }
74 private groupedDates: { [id: number]: GroupDate } = {}
75
489290b8 76 abstract getVideosObservable (page: number): Observable<{ videos: Video[], totalVideos: number }>
9af61e84 77
c199c427 78 abstract generateSyndicationList (): void
fd45e8f4 79
b2731bff
C
80 get user () {
81 return this.authService.getUser()
82 }
83
fd45e8f4 84 ngOnInit () {
34c7f429
C
85 this.groupedDateLabels = {
86 [GroupDate.UNKNOWN]: null,
87 [GroupDate.TODAY]: this.i18n('Today'),
88 [GroupDate.YESTERDAY]: this.i18n('Yesterday'),
93aa8552
C
89 [GroupDate.LAST_WEEK]: this.i18n('Last week'),
90 [GroupDate.LAST_MONTH]: this.i18n('Last month'),
34c7f429
C
91 [GroupDate.OLDER]: this.i18n('Older')
92 }
93
fd45e8f4 94 // Subscribe to route changes
5b5e333f 95 const routeParams = this.route.snapshot.queryParams
2bbb3412 96 this.loadRouteParams(routeParams)
a2b817d3 97
9af61e84 98 this.resizeSubscription = fromEvent(window, 'resize')
db400f44 99 .pipe(debounceTime(500))
6194c1b4 100 .subscribe(() => this.calcPageSizes())
3290f37c 101
6194c1b4 102 this.calcPageSizes()
3caf77d3
C
103
104 const loadUserObservable = this.loadUserVideoLanguagesIfNeeded()
105
106 if (this.loadOnInit === true) {
107 loadUserObservable.subscribe(() => this.loadMoreVideos())
108 }
fd45e8f4
C
109 }
110
9af61e84
C
111 ngOnDestroy () {
112 if (this.resizeSubscription) this.resizeSubscription.unsubscribe()
113 }
114
489290b8
C
115 disableForReuse () {
116 this.disabled = true
89724816
C
117 }
118
489290b8
C
119 enabledForReuse () {
120 this.disabled = false
89724816
C
121 }
122
489290b8
C
123 videoById (index: number, video: Video) {
124 return video.id
2bbb3412
C
125 }
126
127 onNearOfBottom () {
489290b8 128 if (this.disabled) return
2bbb3412 129
489290b8
C
130 // Last page
131 if (this.pagination.totalItems <= (this.pagination.currentPage * this.pagination.itemsPerPage)) return
0cd4344f 132
489290b8 133 this.pagination.currentPage += 1
a8ecc6f6 134
489290b8 135 this.setScrollRouteParams()
a8ecc6f6 136
489290b8
C
137 this.loadMoreVideos()
138 }
fd45e8f4 139
489290b8
C
140 loadMoreVideos () {
141 const observable = this.getVideosObservable(this.pagination.currentPage)
fd45e8f4
C
142
143 observable.subscribe(
144 ({ videos, totalVideos }) => {
fd45e8f4 145 this.pagination.totalItems = totalVideos
489290b8 146 this.videos = this.videos.concat(videos)
693263e9 147
34c7f429
C
148 if (this.groupByDate) this.buildGroupedDateLabels()
149
693263e9 150 this.onMoreVideos()
fd45e8f4 151 },
017c3dca 152
489290b8
C
153 error => this.notifier.error(error.message)
154 )
2bbb3412
C
155 }
156
489290b8
C
157 reloadVideos () {
158 this.pagination.currentPage = 1
159 this.videos = []
160 this.loadMoreVideos()
fd45e8f4
C
161 }
162
489290b8
C
163 toggleModerationDisplay () {
164 throw new Error('toggleModerationDisplay is not implemented')
fd45e8f4
C
165 }
166
3a0fb65c
C
167 removeVideoFromArray (video: Video) {
168 this.videos = this.videos.filter(v => v.id !== video.id)
169 }
170
34c7f429
C
171 buildGroupedDateLabels () {
172 let currentGroupedDate: GroupDate = GroupDate.UNKNOWN
173
174 for (const video of this.videos) {
175 const publishedDate = video.publishedAt
176
4e0c1793
C
177 if (currentGroupedDate <= GroupDate.TODAY && isToday(publishedDate)) {
178 if (currentGroupedDate === GroupDate.TODAY) continue
179
34c7f429
C
180 currentGroupedDate = GroupDate.TODAY
181 this.groupedDates[ video.id ] = currentGroupedDate
182 continue
183 }
184
4e0c1793
C
185 if (currentGroupedDate <= GroupDate.YESTERDAY && isYesterday(publishedDate)) {
186 if (currentGroupedDate === GroupDate.YESTERDAY) continue
187
34c7f429
C
188 currentGroupedDate = GroupDate.YESTERDAY
189 this.groupedDates[ video.id ] = currentGroupedDate
190 continue
191 }
192
93aa8552
C
193 if (currentGroupedDate <= GroupDate.LAST_WEEK && isLastWeek(publishedDate)) {
194 if (currentGroupedDate === GroupDate.LAST_WEEK) continue
4e0c1793 195
93aa8552 196 currentGroupedDate = GroupDate.LAST_WEEK
34c7f429
C
197 this.groupedDates[ video.id ] = currentGroupedDate
198 continue
199 }
200
93aa8552
C
201 if (currentGroupedDate <= GroupDate.LAST_MONTH && isLastMonth(publishedDate)) {
202 if (currentGroupedDate === GroupDate.LAST_MONTH) continue
4e0c1793 203
93aa8552 204 currentGroupedDate = GroupDate.LAST_MONTH
34c7f429
C
205 this.groupedDates[ video.id ] = currentGroupedDate
206 continue
207 }
208
4e0c1793
C
209 if (currentGroupedDate <= GroupDate.OLDER) {
210 if (currentGroupedDate === GroupDate.OLDER) continue
211
34c7f429
C
212 currentGroupedDate = GroupDate.OLDER
213 this.groupedDates[ video.id ] = currentGroupedDate
214 }
215 }
216 }
217
218 getCurrentGroupedDateLabel (video: Video) {
219 if (this.groupByDate === false) return undefined
220
221 return this.groupedDateLabels[this.groupedDates[video.id]]
222 }
223
693263e9
C
224 // On videos hook for children that want to do something
225 protected onMoreVideos () { /* empty */ }
226
fd45e8f4 227 protected loadRouteParams (routeParams: { [ key: string ]: any }) {
489290b8
C
228 this.sort = routeParams[ 'sort' ] as VideoSortField || this.defaultSort
229 this.categoryOneOf = routeParams[ 'categoryOneOf' ]
230 this.angularState = routeParams[ 'a-state' ]
0cd4344f 231 }
6194c1b4
C
232
233 private calcPageSizes () {
489290b8 234 if (this.screenService.isInMobileView()) {
6194c1b4 235 this.pagination.itemsPerPage = 5
6194c1b4 236 }
489290b8 237 }
6194c1b4 238
489290b8
C
239 private setScrollRouteParams () {
240 // Already set
241 if (this.angularState) return
6194c1b4 242
489290b8 243 this.angularState = 42
6194c1b4 244
489290b8
C
245 const queryParams = {
246 'a-state': this.angularState,
247 categoryOneOf: this.categoryOneOf
9af61e84 248 }
6194c1b4 249
489290b8
C
250 let path = this.router.url
251 if (!path || path === '/') path = this.serverService.getConfig().instance.defaultClientRoute
252
253 this.router.navigate([ path ], { queryParams, replaceUrl: true, queryParamsHandling: 'merge' })
6194c1b4 254 }
3caf77d3
C
255
256 private loadUserVideoLanguagesIfNeeded () {
257 if (!this.authService.isLoggedIn() || !this.useUserVideoLanguagePreferences) {
258 return of(true)
259 }
260
261 return this.authService.userInformationLoaded
262 .pipe(
263 first(),
264 tap(() => this.languageOneOf = this.user.videoLanguages)
265 )
266 }
fd45e8f4 267}