]>
Commit | Line | Data |
---|---|---|
dd24f1bb C |
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 | constructor (defaultSort: string, defaultScope: VideoFilterScope) { | |
42 | this.setDefaultSort(defaultSort) | |
43 | this.setDefaultScope(defaultScope) | |
44 | ||
45 | this.reset() | |
46 | } | |
47 | ||
48 | onChange (cb: () => void) { | |
49 | this.onChangeCallbacks.push(cb) | |
50 | } | |
51 | ||
52 | triggerChange () { | |
53 | // Don't run on change if the values did not change | |
54 | const currentFormObjectString = JSON.stringify(this.toFormObject()) | |
55 | if (this.oldFormObjectString && currentFormObjectString === this.oldFormObjectString) return | |
56 | ||
57 | this.oldFormObjectString = currentFormObjectString | |
58 | ||
59 | for (const cb of this.onChangeCallbacks) { | |
60 | cb() | |
61 | } | |
62 | } | |
63 | ||
64 | setDefaultScope (scope: VideoFilterScope) { | |
65 | this.defaultValues.set('scope', scope) | |
66 | } | |
67 | ||
68 | setDefaultSort (sort: string) { | |
69 | this.defaultValues.set('sort', sort) | |
70 | } | |
71 | ||
72 | setNSFWPolicy (nsfwPolicy: NSFWPolicyType) { | |
73 | this.updateDefaultNSFW(nsfwPolicy) | |
74 | } | |
75 | ||
76 | reset (specificKey?: string) { | |
77 | for (const [ key, value ] of this.defaultValues) { | |
78 | if (specificKey && specificKey !== key) continue | |
79 | ||
80 | // FIXME: typings | |
81 | this[key as any] = value | |
82 | } | |
83 | ||
84 | this.buildActiveFilters() | |
85 | } | |
86 | ||
87 | load (obj: Partial<AttributesOnly<VideoFilters>>) { | |
88 | if (obj.sort !== undefined) this.sort = obj.sort | |
89 | ||
90 | if (obj.nsfw !== undefined) this.nsfw = obj.nsfw | |
91 | ||
92 | if (obj.languageOneOf !== undefined) this.languageOneOf = intoArray(obj.languageOneOf) | |
93 | if (obj.categoryOneOf !== undefined) this.categoryOneOf = intoArray(obj.categoryOneOf) | |
94 | ||
95 | if (obj.scope !== undefined) this.scope = obj.scope | |
96 | if (obj.allVideos !== undefined) this.allVideos = toBoolean(obj.allVideos) | |
97 | ||
98 | if (obj.live !== undefined) this.live = obj.live | |
99 | ||
100 | if (obj.search !== undefined) this.search = obj.search | |
101 | ||
102 | this.buildActiveFilters() | |
103 | } | |
104 | ||
105 | buildActiveFilters () { | |
106 | this.activeFilters = [] | |
107 | ||
108 | this.activeFilters.push({ | |
109 | key: 'nsfw', | |
110 | canRemove: false, | |
111 | label: $localize`Sensitive content`, | |
112 | value: this.getNSFWValue() | |
113 | }) | |
114 | ||
115 | this.activeFilters.push({ | |
116 | key: 'scope', | |
117 | canRemove: false, | |
118 | label: $localize`Scope`, | |
119 | value: this.scope === 'federated' | |
120 | ? $localize`Federated` | |
121 | : $localize`Local` | |
122 | }) | |
123 | ||
124 | if (this.languageOneOf && this.languageOneOf.length !== 0) { | |
125 | this.activeFilters.push({ | |
126 | key: 'languageOneOf', | |
127 | canRemove: true, | |
128 | label: $localize`Languages`, | |
129 | value: this.languageOneOf.map(l => l.toUpperCase()).join(', ') | |
130 | }) | |
131 | } | |
132 | ||
133 | if (this.categoryOneOf && this.categoryOneOf.length !== 0) { | |
134 | this.activeFilters.push({ | |
135 | key: 'categoryOneOf', | |
136 | canRemove: true, | |
137 | label: $localize`Categories`, | |
138 | value: this.categoryOneOf.join(', ') | |
139 | }) | |
140 | } | |
141 | ||
142 | if (this.allVideos) { | |
143 | this.activeFilters.push({ | |
144 | key: 'allVideos', | |
145 | canRemove: true, | |
146 | label: $localize`All videos` | |
147 | }) | |
148 | } | |
149 | ||
150 | if (this.live === 'true') { | |
151 | this.activeFilters.push({ | |
152 | key: 'live', | |
153 | canRemove: true, | |
154 | label: $localize`Live videos` | |
155 | }) | |
156 | } else if (this.live === 'false') { | |
157 | this.activeFilters.push({ | |
158 | key: 'live', | |
159 | canRemove: true, | |
160 | label: $localize`VOD videos` | |
161 | }) | |
162 | } | |
163 | } | |
164 | ||
165 | getActiveFilters () { | |
166 | return this.activeFilters | |
167 | } | |
168 | ||
169 | toFormObject (): VideoFiltersKeys { | |
170 | const result: Partial<VideoFiltersKeys> = {} | |
171 | ||
172 | for (const [ key ] of this.defaultValues) { | |
173 | result[key] = this[key] | |
174 | } | |
175 | ||
176 | return result as VideoFiltersKeys | |
177 | } | |
178 | ||
179 | toUrlObject () { | |
180 | const result: { [ id: string ]: any } = {} | |
181 | ||
182 | for (const [ key, defaultValue ] of this.defaultValues) { | |
183 | if (this[key] !== defaultValue) { | |
184 | result[key] = this[key] | |
185 | } | |
186 | } | |
187 | ||
188 | return result | |
189 | } | |
190 | ||
191 | toVideosAPIObject () { | |
192 | let filter: VideoFilter | |
193 | ||
194 | if (this.scope === 'local' && this.allVideos) { | |
195 | filter = 'all-local' | |
196 | } else if (this.scope === 'federated' && this.allVideos) { | |
197 | filter = 'all' | |
198 | } else if (this.scope === 'local') { | |
199 | filter = 'local' | |
200 | } | |
201 | ||
202 | let isLive: boolean | |
203 | if (this.live === 'true') isLive = true | |
204 | else if (this.live === 'false') isLive = false | |
205 | ||
206 | return { | |
207 | sort: this.sort, | |
208 | nsfw: this.nsfw, | |
209 | languageOneOf: this.languageOneOf, | |
210 | categoryOneOf: this.categoryOneOf, | |
211 | search: this.search, | |
212 | filter, | |
213 | isLive | |
214 | } | |
215 | } | |
216 | ||
217 | getNSFWDisplayLabel () { | |
218 | if (this.defaultNSFWPolicy === 'blur') return $localize`Blurred` | |
219 | ||
220 | return $localize`Displayed` | |
221 | } | |
222 | ||
223 | private getNSFWValue () { | |
224 | if (this.nsfw === 'false') return $localize`hidden` | |
225 | if (this.defaultNSFWPolicy === 'blur') return $localize`blurred` | |
226 | ||
227 | return $localize`displayed` | |
228 | } | |
229 | ||
230 | private updateDefaultNSFW (nsfwPolicy: NSFWPolicyType) { | |
231 | const nsfw = nsfwPolicy === 'do_not_list' | |
232 | ? 'false' | |
233 | : 'both' | |
234 | ||
235 | this.defaultValues.set('nsfw', nsfw) | |
236 | this.defaultNSFWPolicy = nsfwPolicy | |
237 | ||
238 | this.reset('nsfw') | |
239 | } | |
240 | } |