]>
Commit | Line | Data |
---|---|---|
dc133480 C |
1 | import { |
2 | AllowNull, | |
3 | BelongsTo, | |
4 | Column, | |
5 | CreatedAt, | |
6 | Default, | |
7 | ForeignKey, | |
8 | IFindOptions, | |
9 | Is, | |
10 | Model, | |
11 | Scopes, | |
12 | Table, | |
13 | UpdatedAt | |
14 | } from 'sequelize-typescript' | |
cef534ed C |
15 | import { UserNotification, UserNotificationType } from '../../../shared' |
16 | import { getSort, throwIfNotValid } from '../utils' | |
17 | import { isBooleanValid } from '../../helpers/custom-validators/misc' | |
18 | import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications' | |
19 | import { UserModel } from './user' | |
20 | import { VideoModel } from '../video/video' | |
21 | import { VideoCommentModel } from '../video/video-comment' | |
22 | import { Op } from 'sequelize' | |
23 | import { VideoChannelModel } from '../video/video-channel' | |
24 | import { AccountModel } from './account' | |
25 | import { VideoAbuseModel } from '../video/video-abuse' | |
26 | import { VideoBlacklistModel } from '../video/video-blacklist' | |
dc133480 | 27 | import { VideoImportModel } from '../video/video-import' |
f7cc67b4 C |
28 | import { ActorModel } from '../activitypub/actor' |
29 | import { ActorFollowModel } from '../activitypub/actor-follow' | |
cef534ed C |
30 | |
31 | enum ScopeNames { | |
32 | WITH_ALL = 'WITH_ALL' | |
33 | } | |
34 | ||
dc133480 C |
35 | function buildVideoInclude (required: boolean) { |
36 | return { | |
37 | attributes: [ 'id', 'uuid', 'name' ], | |
38 | model: () => VideoModel.unscoped(), | |
39 | required | |
40 | } | |
41 | } | |
42 | ||
f7cc67b4 | 43 | function buildChannelInclude (required: boolean) { |
dc133480 | 44 | return { |
f7cc67b4 | 45 | required, |
dc133480 C |
46 | attributes: [ 'id', 'name' ], |
47 | model: () => VideoChannelModel.unscoped() | |
48 | } | |
49 | } | |
50 | ||
f7cc67b4 | 51 | function buildAccountInclude (required: boolean) { |
dc133480 | 52 | return { |
f7cc67b4 | 53 | required, |
dc133480 C |
54 | attributes: [ 'id', 'name' ], |
55 | model: () => AccountModel.unscoped() | |
56 | } | |
57 | } | |
58 | ||
cef534ed C |
59 | @Scopes({ |
60 | [ScopeNames.WITH_ALL]: { | |
61 | include: [ | |
dc133480 | 62 | Object.assign(buildVideoInclude(false), { |
f7cc67b4 | 63 | include: [ buildChannelInclude(true) ] |
dc133480 | 64 | }), |
cef534ed | 65 | { |
dc133480 | 66 | attributes: [ 'id', 'originCommentId' ], |
cef534ed C |
67 | model: () => VideoCommentModel.unscoped(), |
68 | required: false, | |
69 | include: [ | |
f7cc67b4 | 70 | buildAccountInclude(true), |
dc133480 | 71 | buildVideoInclude(true) |
cef534ed C |
72 | ] |
73 | }, | |
74 | { | |
75 | attributes: [ 'id' ], | |
76 | model: () => VideoAbuseModel.unscoped(), | |
77 | required: false, | |
dc133480 | 78 | include: [ buildVideoInclude(true) ] |
cef534ed C |
79 | }, |
80 | { | |
81 | attributes: [ 'id' ], | |
82 | model: () => VideoBlacklistModel.unscoped(), | |
83 | required: false, | |
dc133480 C |
84 | include: [ buildVideoInclude(true) ] |
85 | }, | |
86 | { | |
87 | attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ], | |
88 | model: () => VideoImportModel.unscoped(), | |
89 | required: false, | |
90 | include: [ buildVideoInclude(false) ] | |
f7cc67b4 C |
91 | }, |
92 | { | |
93 | attributes: [ 'id', 'name' ], | |
94 | model: () => AccountModel.unscoped(), | |
95 | required: false, | |
96 | include: [ | |
97 | { | |
98 | attributes: [ 'id', 'preferredUsername' ], | |
99 | model: () => ActorModel.unscoped(), | |
100 | required: true | |
101 | } | |
102 | ] | |
103 | }, | |
104 | { | |
105 | attributes: [ 'id' ], | |
106 | model: () => ActorFollowModel.unscoped(), | |
107 | required: false, | |
108 | include: [ | |
109 | { | |
110 | attributes: [ 'preferredUsername' ], | |
111 | model: () => ActorModel.unscoped(), | |
112 | required: true, | |
113 | as: 'ActorFollower', | |
114 | include: [ buildAccountInclude(true) ] | |
115 | }, | |
116 | { | |
117 | attributes: [ 'preferredUsername' ], | |
118 | model: () => ActorModel.unscoped(), | |
119 | required: true, | |
120 | as: 'ActorFollowing', | |
121 | include: [ | |
122 | buildChannelInclude(false), | |
123 | buildAccountInclude(false) | |
124 | ] | |
125 | } | |
126 | ] | |
cef534ed C |
127 | } |
128 | ] | |
129 | } | |
130 | }) | |
131 | @Table({ | |
132 | tableName: 'userNotification', | |
133 | indexes: [ | |
134 | { | |
135 | fields: [ 'videoId' ] | |
136 | }, | |
137 | { | |
138 | fields: [ 'commentId' ] | |
139 | } | |
140 | ] | |
141 | }) | |
142 | export class UserNotificationModel extends Model<UserNotificationModel> { | |
143 | ||
144 | @AllowNull(false) | |
145 | @Default(null) | |
146 | @Is('UserNotificationType', value => throwIfNotValid(value, isUserNotificationTypeValid, 'type')) | |
147 | @Column | |
148 | type: UserNotificationType | |
149 | ||
150 | @AllowNull(false) | |
151 | @Default(false) | |
152 | @Is('UserNotificationRead', value => throwIfNotValid(value, isBooleanValid, 'read')) | |
153 | @Column | |
154 | read: boolean | |
155 | ||
156 | @CreatedAt | |
157 | createdAt: Date | |
158 | ||
159 | @UpdatedAt | |
160 | updatedAt: Date | |
161 | ||
162 | @ForeignKey(() => UserModel) | |
163 | @Column | |
164 | userId: number | |
165 | ||
166 | @BelongsTo(() => UserModel, { | |
167 | foreignKey: { | |
168 | allowNull: false | |
169 | }, | |
170 | onDelete: 'cascade' | |
171 | }) | |
172 | User: UserModel | |
173 | ||
174 | @ForeignKey(() => VideoModel) | |
175 | @Column | |
176 | videoId: number | |
177 | ||
178 | @BelongsTo(() => VideoModel, { | |
179 | foreignKey: { | |
180 | allowNull: true | |
181 | }, | |
182 | onDelete: 'cascade' | |
183 | }) | |
184 | Video: VideoModel | |
185 | ||
186 | @ForeignKey(() => VideoCommentModel) | |
187 | @Column | |
188 | commentId: number | |
189 | ||
190 | @BelongsTo(() => VideoCommentModel, { | |
191 | foreignKey: { | |
192 | allowNull: true | |
193 | }, | |
194 | onDelete: 'cascade' | |
195 | }) | |
196 | Comment: VideoCommentModel | |
197 | ||
198 | @ForeignKey(() => VideoAbuseModel) | |
199 | @Column | |
200 | videoAbuseId: number | |
201 | ||
202 | @BelongsTo(() => VideoAbuseModel, { | |
203 | foreignKey: { | |
204 | allowNull: true | |
205 | }, | |
206 | onDelete: 'cascade' | |
207 | }) | |
208 | VideoAbuse: VideoAbuseModel | |
209 | ||
210 | @ForeignKey(() => VideoBlacklistModel) | |
211 | @Column | |
212 | videoBlacklistId: number | |
213 | ||
214 | @BelongsTo(() => VideoBlacklistModel, { | |
215 | foreignKey: { | |
216 | allowNull: true | |
217 | }, | |
218 | onDelete: 'cascade' | |
219 | }) | |
220 | VideoBlacklist: VideoBlacklistModel | |
221 | ||
dc133480 C |
222 | @ForeignKey(() => VideoImportModel) |
223 | @Column | |
224 | videoImportId: number | |
225 | ||
226 | @BelongsTo(() => VideoImportModel, { | |
227 | foreignKey: { | |
228 | allowNull: true | |
229 | }, | |
230 | onDelete: 'cascade' | |
231 | }) | |
232 | VideoImport: VideoImportModel | |
233 | ||
f7cc67b4 C |
234 | @ForeignKey(() => AccountModel) |
235 | @Column | |
236 | accountId: number | |
237 | ||
238 | @BelongsTo(() => AccountModel, { | |
239 | foreignKey: { | |
240 | allowNull: true | |
241 | }, | |
242 | onDelete: 'cascade' | |
243 | }) | |
244 | Account: AccountModel | |
245 | ||
246 | @ForeignKey(() => ActorFollowModel) | |
247 | @Column | |
248 | actorFollowId: number | |
249 | ||
250 | @BelongsTo(() => ActorFollowModel, { | |
251 | foreignKey: { | |
252 | allowNull: true | |
253 | }, | |
254 | onDelete: 'cascade' | |
255 | }) | |
256 | ActorFollow: ActorFollowModel | |
257 | ||
dc133480 C |
258 | static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) { |
259 | const query: IFindOptions<UserNotificationModel> = { | |
cef534ed C |
260 | offset: start, |
261 | limit: count, | |
262 | order: getSort(sort), | |
263 | where: { | |
264 | userId | |
265 | } | |
266 | } | |
267 | ||
dc133480 C |
268 | if (unread !== undefined) query.where['read'] = !unread |
269 | ||
cef534ed C |
270 | return UserNotificationModel.scope(ScopeNames.WITH_ALL) |
271 | .findAndCountAll(query) | |
272 | .then(({ rows, count }) => { | |
273 | return { | |
274 | data: rows, | |
275 | total: count | |
276 | } | |
277 | }) | |
278 | } | |
279 | ||
280 | static markAsRead (userId: number, notificationIds: number[]) { | |
281 | const query = { | |
282 | where: { | |
283 | userId, | |
284 | id: { | |
285 | [Op.any]: notificationIds | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
290 | return UserNotificationModel.update({ read: true }, query) | |
291 | } | |
292 | ||
2f1548fd C |
293 | static markAllAsRead (userId: number) { |
294 | const query = { where: { userId } } | |
295 | ||
296 | return UserNotificationModel.update({ read: true }, query) | |
297 | } | |
298 | ||
cef534ed | 299 | toFormattedJSON (): UserNotification { |
dc133480 | 300 | const video = this.Video ? Object.assign(this.formatVideo(this.Video), { |
cef534ed C |
301 | channel: { |
302 | id: this.Video.VideoChannel.id, | |
303 | displayName: this.Video.VideoChannel.getDisplayName() | |
304 | } | |
dc133480 C |
305 | }) : undefined |
306 | ||
307 | const videoImport = this.VideoImport ? { | |
308 | id: this.VideoImport.id, | |
309 | video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined, | |
310 | torrentName: this.VideoImport.torrentName, | |
311 | magnetUri: this.VideoImport.magnetUri, | |
312 | targetUrl: this.VideoImport.targetUrl | |
cef534ed C |
313 | } : undefined |
314 | ||
315 | const comment = this.Comment ? { | |
316 | id: this.Comment.id, | |
dc133480 | 317 | threadId: this.Comment.getThreadId(), |
cef534ed C |
318 | account: { |
319 | id: this.Comment.Account.id, | |
320 | displayName: this.Comment.Account.getDisplayName() | |
321 | }, | |
dc133480 | 322 | video: this.formatVideo(this.Comment.Video) |
cef534ed C |
323 | } : undefined |
324 | ||
325 | const videoAbuse = this.VideoAbuse ? { | |
326 | id: this.VideoAbuse.id, | |
dc133480 | 327 | video: this.formatVideo(this.VideoAbuse.Video) |
cef534ed C |
328 | } : undefined |
329 | ||
330 | const videoBlacklist = this.VideoBlacklist ? { | |
331 | id: this.VideoBlacklist.id, | |
dc133480 | 332 | video: this.formatVideo(this.VideoBlacklist.Video) |
cef534ed C |
333 | } : undefined |
334 | ||
f7cc67b4 C |
335 | const account = this.Account ? { |
336 | id: this.Account.id, | |
337 | displayName: this.Account.getDisplayName(), | |
338 | name: this.Account.Actor.preferredUsername | |
339 | } : undefined | |
340 | ||
341 | const actorFollow = this.ActorFollow ? { | |
342 | id: this.ActorFollow.id, | |
343 | follower: { | |
344 | displayName: this.ActorFollow.ActorFollower.Account.getDisplayName(), | |
345 | name: this.ActorFollow.ActorFollower.preferredUsername | |
346 | }, | |
347 | following: { | |
348 | type: this.ActorFollow.ActorFollowing.VideoChannel ? 'channel' as 'channel' : 'account' as 'account', | |
349 | displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(), | |
350 | name: this.ActorFollow.ActorFollowing.preferredUsername | |
351 | } | |
352 | } : undefined | |
353 | ||
cef534ed C |
354 | return { |
355 | id: this.id, | |
356 | type: this.type, | |
357 | read: this.read, | |
358 | video, | |
dc133480 | 359 | videoImport, |
cef534ed C |
360 | comment, |
361 | videoAbuse, | |
362 | videoBlacklist, | |
f7cc67b4 C |
363 | account, |
364 | actorFollow, | |
cef534ed C |
365 | createdAt: this.createdAt.toISOString(), |
366 | updatedAt: this.updatedAt.toISOString() | |
367 | } | |
368 | } | |
dc133480 C |
369 | |
370 | private formatVideo (video: VideoModel) { | |
371 | return { | |
372 | id: video.id, | |
373 | uuid: video.uuid, | |
374 | name: video.name | |
375 | } | |
376 | } | |
cef534ed | 377 | } |