]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/app/shared/shared-video-miniature/video-filters.model.ts
Fix NSFW filter and add tests
[github/Chocobozzz/PeerTube.git] / client / src / app / shared / shared-video-miniature / video-filters.model.ts
1 import { intoArray, toBoolean } from '@app/helpers'
2 import { AttributesOnly } from '@shared/core-utils'
3 import { BooleanBothQuery, NSFWPolicyType, VideoFilter, VideoSortField } from '@shared/models'
4
5 type VideoFiltersKeys = {
6 [ id in keyof AttributesOnly<VideoFilters> ]: any
7 }
8
9 export type VideoFilterScope = 'local' | 'federated'
10
11 export class VideoFilters {
12 sort: VideoSortField
13 nsfw: BooleanBothQuery
14
15 languageOneOf: string[]
16 categoryOneOf: number[]
17
18 scope: VideoFilterScope
19 allVideos: boolean
20
21 live: BooleanBothQuery
22
23 search: string
24
25 private defaultValues = new Map<keyof VideoFilters, any>([
26 [ 'sort', '-publishedAt' ],
27 [ 'nsfw', 'false' ],
28 [ 'languageOneOf', undefined ],
29 [ 'categoryOneOf', undefined ],
30 [ 'scope', 'federated' ],
31 [ 'allVideos', false ],
32 [ 'live', 'both' ]
33 ])
34
35 private activeFilters: { key: string, canRemove: boolean, label: string, value?: string }[] = []
36 private defaultNSFWPolicy: NSFWPolicyType
37
38 private onChangeCallbacks: Array<() => void> = []
39 private oldFormObjectString: string
40
41 private readonly hiddenFields: string[] = []
42
43 constructor (defaultSort: string, defaultScope: VideoFilterScope, hiddenFields: string[] = []) {
44 this.setDefaultSort(defaultSort)
45 this.setDefaultScope(defaultScope)
46
47 this.hiddenFields = hiddenFields
48
49 this.reset()
50 }
51
52 onChange (cb: () => void) {
53 this.onChangeCallbacks.push(cb)
54 }
55
56 triggerChange () {
57 // Don't run on change if the values did not change
58 const currentFormObjectString = JSON.stringify(this.toFormObject())
59 if (this.oldFormObjectString && currentFormObjectString === this.oldFormObjectString) return
60
61 this.oldFormObjectString = currentFormObjectString
62
63 for (const cb of this.onChangeCallbacks) {
64 cb()
65 }
66 }
67
68 setDefaultScope (scope: VideoFilterScope) {
69 this.defaultValues.set('scope', scope)
70 }
71
72 setDefaultSort (sort: string) {
73 this.defaultValues.set('sort', sort)
74 }
75
76 setNSFWPolicy (nsfwPolicy: NSFWPolicyType) {
77 console.log(nsfwPolicy)
78
79 this.updateDefaultNSFW(nsfwPolicy)
80 }
81
82 reset (specificKey?: string) {
83 for (const [ key, value ] of this.defaultValues) {
84 if (specificKey && specificKey !== key) continue
85
86 // FIXME: typings
87 this[key as any] = value
88 }
89
90 this.buildActiveFilters()
91 }
92
93 load (obj: Partial<AttributesOnly<VideoFilters>>) {
94 if (obj.sort !== undefined) this.sort = obj.sort
95
96 if (obj.nsfw !== undefined) this.nsfw = obj.nsfw
97
98 if (obj.languageOneOf !== undefined) this.languageOneOf = intoArray(obj.languageOneOf)
99 if (obj.categoryOneOf !== undefined) this.categoryOneOf = intoArray(obj.categoryOneOf)
100
101 if (obj.scope !== undefined) this.scope = obj.scope
102 if (obj.allVideos !== undefined) this.allVideos = toBoolean(obj.allVideos)
103
104 if (obj.live !== undefined) this.live = obj.live
105
106 if (obj.search !== undefined) this.search = obj.search
107
108 this.buildActiveFilters()
109 }
110
111 buildActiveFilters () {
112 this.activeFilters = []
113
114 this.activeFilters.push({
115 key: 'nsfw',
116 canRemove: false,
117 label: $localize`Sensitive content`,
118 value: this.getNSFWValue()
119 })
120
121 this.activeFilters.push({
122 key: 'scope',
123 canRemove: false,
124 label: $localize`Scope`,
125 value: this.scope === 'federated'
126 ? $localize`Federated`
127 : $localize`Local`
128 })
129
130 if (this.languageOneOf && this.languageOneOf.length !== 0) {
131 this.activeFilters.push({
132 key: 'languageOneOf',
133 canRemove: true,
134 label: $localize`Languages`,
135 value: this.languageOneOf.map(l => l.toUpperCase()).join(', ')
136 })
137 }
138
139 if (this.categoryOneOf && this.categoryOneOf.length !== 0) {
140 this.activeFilters.push({
141 key: 'categoryOneOf',
142 canRemove: true,
143 label: $localize`Categories`,
144 value: this.categoryOneOf.join(', ')
145 })
146 }
147
148 if (this.allVideos) {
149 this.activeFilters.push({
150 key: 'allVideos',
151 canRemove: true,
152 label: $localize`All videos`
153 })
154 }
155
156 if (this.live === 'true') {
157 this.activeFilters.push({
158 key: 'live',
159 canRemove: true,
160 label: $localize`Live videos`
161 })
162 } else if (this.live === 'false') {
163 this.activeFilters.push({
164 key: 'live',
165 canRemove: true,
166 label: $localize`VOD videos`
167 })
168 }
169
170 this.activeFilters = this.activeFilters
171 .filter(a => this.hiddenFields.includes(a.key) === false)
172 }
173
174 getActiveFilters () {
175 return this.activeFilters
176 }
177
178 toFormObject (): VideoFiltersKeys {
179 const result: Partial<VideoFiltersKeys> = {}
180
181 for (const [ key ] of this.defaultValues) {
182 result[key] = this[key]
183 }
184
185 return result as VideoFiltersKeys
186 }
187
188 toUrlObject () {
189 const result: { [ id: string ]: any } = {}
190
191 for (const [ key, defaultValue ] of this.defaultValues) {
192 if (this[key] !== defaultValue) {
193 result[key] = this[key]
194 }
195 }
196
197 return result
198 }
199
200 toVideosAPIObject () {
201 let filter: VideoFilter
202
203 if (this.scope === 'local' && this.allVideos) {
204 filter = 'all-local'
205 } else if (this.scope === 'federated' && this.allVideos) {
206 filter = 'all'
207 } else if (this.scope === 'local') {
208 filter = 'local'
209 }
210
211 let isLive: boolean
212 if (this.live === 'true') isLive = true
213 else if (this.live === 'false') isLive = false
214
215 return {
216 sort: this.sort,
217 nsfw: this.nsfw,
218 languageOneOf: this.languageOneOf,
219 categoryOneOf: this.categoryOneOf,
220 search: this.search,
221 filter,
222 isLive
223 }
224 }
225
226 getNSFWDisplayLabel () {
227 if (this.defaultNSFWPolicy === 'blur') return $localize`Blurred`
228
229 return $localize`Displayed`
230 }
231
232 private getNSFWValue () {
233 if (this.nsfw === 'false') return $localize`hidden`
234 if (this.defaultNSFWPolicy === 'blur') return $localize`blurred`
235
236 return $localize`displayed`
237 }
238
239 private updateDefaultNSFW (nsfwPolicy: NSFWPolicyType) {
240 const nsfw = nsfwPolicy === 'do_not_list'
241 ? 'false'
242 : 'both'
243
244 this.defaultValues.set('nsfw', nsfw)
245 this.defaultNSFWPolicy = nsfwPolicy
246
247 this.reset('nsfw')
248 }
249 }