]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/models/account/user-notification.ts
Add import finished and video published notifs
[github/Chocobozzz/PeerTube.git] / server / models / account / user-notification.ts
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'
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'
27 import { VideoImportModel } from '../video/video-import'
28
29 enum ScopeNames {
30 WITH_ALL = 'WITH_ALL'
31 }
32
33 function buildVideoInclude (required: boolean) {
34 return {
35 attributes: [ 'id', 'uuid', 'name' ],
36 model: () => VideoModel.unscoped(),
37 required
38 }
39 }
40
41 function buildChannelInclude () {
42 return {
43 required: true,
44 attributes: [ 'id', 'name' ],
45 model: () => VideoChannelModel.unscoped()
46 }
47 }
48
49 function buildAccountInclude () {
50 return {
51 required: true,
52 attributes: [ 'id', 'name' ],
53 model: () => AccountModel.unscoped()
54 }
55 }
56
57 @Scopes({
58 [ScopeNames.WITH_ALL]: {
59 include: [
60 Object.assign(buildVideoInclude(false), {
61 include: [ buildChannelInclude() ]
62 }),
63 {
64 attributes: [ 'id', 'originCommentId' ],
65 model: () => VideoCommentModel.unscoped(),
66 required: false,
67 include: [
68 buildAccountInclude(),
69 buildVideoInclude(true)
70 ]
71 },
72 {
73 attributes: [ 'id' ],
74 model: () => VideoAbuseModel.unscoped(),
75 required: false,
76 include: [ buildVideoInclude(true) ]
77 },
78 {
79 attributes: [ 'id' ],
80 model: () => VideoBlacklistModel.unscoped(),
81 required: false,
82 include: [ buildVideoInclude(true) ]
83 },
84 {
85 attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ],
86 model: () => VideoImportModel.unscoped(),
87 required: false,
88 include: [ buildVideoInclude(false) ]
89 }
90 ]
91 }
92 })
93 @Table({
94 tableName: 'userNotification',
95 indexes: [
96 {
97 fields: [ 'videoId' ]
98 },
99 {
100 fields: [ 'commentId' ]
101 }
102 ]
103 })
104 export class UserNotificationModel extends Model<UserNotificationModel> {
105
106 @AllowNull(false)
107 @Default(null)
108 @Is('UserNotificationType', value => throwIfNotValid(value, isUserNotificationTypeValid, 'type'))
109 @Column
110 type: UserNotificationType
111
112 @AllowNull(false)
113 @Default(false)
114 @Is('UserNotificationRead', value => throwIfNotValid(value, isBooleanValid, 'read'))
115 @Column
116 read: boolean
117
118 @CreatedAt
119 createdAt: Date
120
121 @UpdatedAt
122 updatedAt: Date
123
124 @ForeignKey(() => UserModel)
125 @Column
126 userId: number
127
128 @BelongsTo(() => UserModel, {
129 foreignKey: {
130 allowNull: false
131 },
132 onDelete: 'cascade'
133 })
134 User: UserModel
135
136 @ForeignKey(() => VideoModel)
137 @Column
138 videoId: number
139
140 @BelongsTo(() => VideoModel, {
141 foreignKey: {
142 allowNull: true
143 },
144 onDelete: 'cascade'
145 })
146 Video: VideoModel
147
148 @ForeignKey(() => VideoCommentModel)
149 @Column
150 commentId: number
151
152 @BelongsTo(() => VideoCommentModel, {
153 foreignKey: {
154 allowNull: true
155 },
156 onDelete: 'cascade'
157 })
158 Comment: VideoCommentModel
159
160 @ForeignKey(() => VideoAbuseModel)
161 @Column
162 videoAbuseId: number
163
164 @BelongsTo(() => VideoAbuseModel, {
165 foreignKey: {
166 allowNull: true
167 },
168 onDelete: 'cascade'
169 })
170 VideoAbuse: VideoAbuseModel
171
172 @ForeignKey(() => VideoBlacklistModel)
173 @Column
174 videoBlacklistId: number
175
176 @BelongsTo(() => VideoBlacklistModel, {
177 foreignKey: {
178 allowNull: true
179 },
180 onDelete: 'cascade'
181 })
182 VideoBlacklist: VideoBlacklistModel
183
184 @ForeignKey(() => VideoImportModel)
185 @Column
186 videoImportId: number
187
188 @BelongsTo(() => VideoImportModel, {
189 foreignKey: {
190 allowNull: true
191 },
192 onDelete: 'cascade'
193 })
194 VideoImport: VideoImportModel
195
196 static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) {
197 const query: IFindOptions<UserNotificationModel> = {
198 offset: start,
199 limit: count,
200 order: getSort(sort),
201 where: {
202 userId
203 }
204 }
205
206 if (unread !== undefined) query.where['read'] = !unread
207
208 return UserNotificationModel.scope(ScopeNames.WITH_ALL)
209 .findAndCountAll(query)
210 .then(({ rows, count }) => {
211 return {
212 data: rows,
213 total: count
214 }
215 })
216 }
217
218 static markAsRead (userId: number, notificationIds: number[]) {
219 const query = {
220 where: {
221 userId,
222 id: {
223 [Op.any]: notificationIds
224 }
225 }
226 }
227
228 return UserNotificationModel.update({ read: true }, query)
229 }
230
231 toFormattedJSON (): UserNotification {
232 const video = this.Video ? Object.assign(this.formatVideo(this.Video), {
233 channel: {
234 id: this.Video.VideoChannel.id,
235 displayName: this.Video.VideoChannel.getDisplayName()
236 }
237 }) : undefined
238
239 const videoImport = this.VideoImport ? {
240 id: this.VideoImport.id,
241 video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined,
242 torrentName: this.VideoImport.torrentName,
243 magnetUri: this.VideoImport.magnetUri,
244 targetUrl: this.VideoImport.targetUrl
245 } : undefined
246
247 const comment = this.Comment ? {
248 id: this.Comment.id,
249 threadId: this.Comment.getThreadId(),
250 account: {
251 id: this.Comment.Account.id,
252 displayName: this.Comment.Account.getDisplayName()
253 },
254 video: this.formatVideo(this.Comment.Video)
255 } : undefined
256
257 const videoAbuse = this.VideoAbuse ? {
258 id: this.VideoAbuse.id,
259 video: this.formatVideo(this.VideoAbuse.Video)
260 } : undefined
261
262 const videoBlacklist = this.VideoBlacklist ? {
263 id: this.VideoBlacklist.id,
264 video: this.formatVideo(this.VideoBlacklist.Video)
265 } : undefined
266
267 return {
268 id: this.id,
269 type: this.type,
270 read: this.read,
271 video,
272 videoImport,
273 comment,
274 videoAbuse,
275 videoBlacklist,
276 createdAt: this.createdAt.toISOString(),
277 updatedAt: this.updatedAt.toISOString()
278 }
279 }
280
281 private formatVideo (video: VideoModel) {
282 return {
283 id: video.id,
284 uuid: video.uuid,
285 name: video.name
286 }
287 }
288 }