]>
Commit | Line | Data |
---|---|---|
dd24f1bb C |
1 | import { intoArray, toBoolean } from '@app/helpers' |
2 | import { AttributesOnly } from '@shared/core-utils' | |
2760b454 | 3 | import { BooleanBothQuery, NSFWPolicyType, VideoInclude, VideoSortField } from '@shared/models' |
dd24f1bb C |
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 | ||
1b206245 C |
41 | private readonly hiddenFields: string[] = [] |
42 | ||
43 | constructor (defaultSort: string, defaultScope: VideoFilterScope, hiddenFields: string[] = []) { | |
dd24f1bb C |
44 | this.setDefaultSort(defaultSort) |
45 | this.setDefaultScope(defaultScope) | |
46 | ||
1b206245 C |
47 | this.hiddenFields = hiddenFields |
48 | ||
dd24f1bb C |
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 | this.updateDefaultNSFW(nsfwPolicy) | |
78 | } | |
79 | ||
80 | reset (specificKey?: string) { | |
81 | for (const [ key, value ] of this.defaultValues) { | |
82 | if (specificKey && specificKey !== key) continue | |
83 | ||
84 | // FIXME: typings | |
85 | this[key as any] = value | |
86 | } | |
87 | ||
88 | this.buildActiveFilters() | |
89 | } | |
90 | ||
91 | load (obj: Partial<AttributesOnly<VideoFilters>>) { | |
92 | if (obj.sort !== undefined) this.sort = obj.sort | |
93 | ||
94 | if (obj.nsfw !== undefined) this.nsfw = obj.nsfw | |
95 | ||
96 | if (obj.languageOneOf !== undefined) this.languageOneOf = intoArray(obj.languageOneOf) | |
97 | if (obj.categoryOneOf !== undefined) this.categoryOneOf = intoArray(obj.categoryOneOf) | |
98 | ||
99 | if (obj.scope !== undefined) this.scope = obj.scope | |
100 | if (obj.allVideos !== undefined) this.allVideos = toBoolean(obj.allVideos) | |
101 | ||
102 | if (obj.live !== undefined) this.live = obj.live | |
103 | ||
104 | if (obj.search !== undefined) this.search = obj.search | |
105 | ||
106 | this.buildActiveFilters() | |
107 | } | |
108 | ||
109 | buildActiveFilters () { | |
110 | this.activeFilters = [] | |
111 | ||
112 | this.activeFilters.push({ | |
113 | key: 'nsfw', | |
114 | canRemove: false, | |
115 | label: $localize`Sensitive content`, | |
116 | value: this.getNSFWValue() | |
117 | }) | |
118 | ||
119 | this.activeFilters.push({ | |
120 | key: 'scope', | |
121 | canRemove: false, | |
122 | label: $localize`Scope`, | |
123 | value: this.scope === 'federated' | |
124 | ? $localize`Federated` | |
125 | : $localize`Local` | |
126 | }) | |
127 | ||
128 | if (this.languageOneOf && this.languageOneOf.length !== 0) { | |
129 | this.activeFilters.push({ | |
130 | key: 'languageOneOf', | |
131 | canRemove: true, | |
132 | label: $localize`Languages`, | |
133 | value: this.languageOneOf.map(l => l.toUpperCase()).join(', ') | |
134 | }) | |
135 | } | |
136 | ||
137 | if (this.categoryOneOf && this.categoryOneOf.length !== 0) { | |
138 | this.activeFilters.push({ | |
139 | key: 'categoryOneOf', | |
140 | canRemove: true, | |
141 | label: $localize`Categories`, | |
142 | value: this.categoryOneOf.join(', ') | |
143 | }) | |
144 | } | |
145 | ||
146 | if (this.allVideos) { | |
147 | this.activeFilters.push({ | |
148 | key: 'allVideos', | |
149 | canRemove: true, | |
150 | label: $localize`All videos` | |
151 | }) | |
152 | } | |
153 | ||
154 | if (this.live === 'true') { | |
155 | this.activeFilters.push({ | |
156 | key: 'live', | |
157 | canRemove: true, | |
158 | label: $localize`Live videos` | |
159 | }) | |
160 | } else if (this.live === 'false') { | |
161 | this.activeFilters.push({ | |
162 | key: 'live', | |
163 | canRemove: true, | |
164 | label: $localize`VOD videos` | |
165 | }) | |
166 | } | |
1b206245 C |
167 | |
168 | this.activeFilters = this.activeFilters | |
169 | .filter(a => this.hiddenFields.includes(a.key) === false) | |
dd24f1bb C |
170 | } |
171 | ||
172 | getActiveFilters () { | |
173 | return this.activeFilters | |
174 | } | |
175 | ||
176 | toFormObject (): VideoFiltersKeys { | |
177 | const result: Partial<VideoFiltersKeys> = {} | |
178 | ||
179 | for (const [ key ] of this.defaultValues) { | |
180 | result[key] = this[key] | |
181 | } | |
182 | ||
183 | return result as VideoFiltersKeys | |
184 | } | |
185 | ||
186 | toUrlObject () { | |
187 | const result: { [ id: string ]: any } = {} | |
188 | ||
189 | for (const [ key, defaultValue ] of this.defaultValues) { | |
190 | if (this[key] !== defaultValue) { | |
191 | result[key] = this[key] | |
192 | } | |
193 | } | |
194 | ||
195 | return result | |
196 | } | |
197 | ||
198 | toVideosAPIObject () { | |
2760b454 C |
199 | let isLocal: boolean |
200 | let include: VideoInclude | |
201 | ||
202 | if (this.scope === 'local') { | |
203 | isLocal = true | |
204 | } | |
205 | ||
206 | if (this.allVideos) { | |
207 | include = VideoInclude.NOT_PUBLISHED_STATE | VideoInclude.HIDDEN_PRIVACY | |
dd24f1bb C |
208 | } |
209 | ||
210 | let isLive: boolean | |
211 | if (this.live === 'true') isLive = true | |
212 | else if (this.live === 'false') isLive = false | |
213 | ||
214 | return { | |
215 | sort: this.sort, | |
216 | nsfw: this.nsfw, | |
217 | languageOneOf: this.languageOneOf, | |
218 | categoryOneOf: this.categoryOneOf, | |
219 | search: this.search, | |
2760b454 C |
220 | isLocal, |
221 | include, | |
dd24f1bb C |
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 | } |