diff options
Diffstat (limited to 'client/src/app/shared/shared-video-miniature/video-filters.model.ts')
-rw-r--r-- | client/src/app/shared/shared-video-miniature/video-filters.model.ts | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-video-miniature/video-filters.model.ts b/client/src/app/shared/shared-video-miniature/video-filters.model.ts new file mode 100644 index 000000000..a3b8129f0 --- /dev/null +++ b/client/src/app/shared/shared-video-miniature/video-filters.model.ts | |||
@@ -0,0 +1,240 @@ | |||
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 | } | ||