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