X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Faccount%2Fuser-notification.ts;h=5a725187a2918f1cb5ed36df1e223379e4d686e3;hb=a3b7421abb4192e215aa280418b62e96958c5e42;hp=9e4f982a335aa4b735e36ac870212555519a8738;hpb=2f1548fda32c3ba9e53913270394eedfacd55986;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts index 9e4f982a3..5a725187a 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/account/user-notification.ts @@ -1,17 +1,4 @@ -import { - AllowNull, - BelongsTo, - Column, - CreatedAt, - Default, - ForeignKey, - IFindOptions, - Is, - Model, - Scopes, - Table, - UpdatedAt -} from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { UserNotification, UserNotificationType } from '../../../shared' import { getSort, throwIfNotValid } from '../utils' import { isBooleanValid } from '../../helpers/custom-validators/misc' @@ -19,7 +6,7 @@ import { isUserNotificationTypeValid } from '../../helpers/custom-validators/use import { UserModel } from './user' import { VideoModel } from '../video/video' import { VideoCommentModel } from '../video/video-comment' -import { Op } from 'sequelize' +import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' import { VideoChannelModel } from '../video/video-channel' import { AccountModel } from './account' import { VideoAbuseModel } from '../video/video-abuse' @@ -27,117 +14,211 @@ import { VideoBlacklistModel } from '../video/video-blacklist' import { VideoImportModel } from '../video/video-import' import { ActorModel } from '../activitypub/actor' import { ActorFollowModel } from '../activitypub/actor-follow' +import { AvatarModel } from '../avatar/avatar' +import { ServerModel } from '../server/server' +import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/typings/models/user' enum ScopeNames { WITH_ALL = 'WITH_ALL' } +function buildActorWithAvatarInclude () { + return { + attributes: [ 'preferredUsername' ], + model: ActorModel.unscoped(), + required: true, + include: [ + { + attributes: [ 'filename' ], + model: AvatarModel.unscoped(), + required: false + }, + { + attributes: [ 'host' ], + model: ServerModel.unscoped(), + required: false + } + ] + } +} + function buildVideoInclude (required: boolean) { return { attributes: [ 'id', 'uuid', 'name' ], - model: () => VideoModel.unscoped(), + model: VideoModel.unscoped(), required } } -function buildChannelInclude (required: boolean) { +function buildChannelInclude (required: boolean, withActor = false) { return { required, attributes: [ 'id', 'name' ], - model: () => VideoChannelModel.unscoped() + model: VideoChannelModel.unscoped(), + include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] } } -function buildAccountInclude (required: boolean) { +function buildAccountInclude (required: boolean, withActor = false) { return { required, attributes: [ 'id', 'name' ], - model: () => AccountModel.unscoped() + model: AccountModel.unscoped(), + include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] } } -@Scopes({ +@Scopes(() => ({ [ScopeNames.WITH_ALL]: { include: [ Object.assign(buildVideoInclude(false), { - include: [ buildChannelInclude(true) ] + include: [ buildChannelInclude(true, true) ] }), + { attributes: [ 'id', 'originCommentId' ], - model: () => VideoCommentModel.unscoped(), + model: VideoCommentModel.unscoped(), required: false, include: [ - buildAccountInclude(true), + buildAccountInclude(true, true), buildVideoInclude(true) ] }, + { attributes: [ 'id' ], - model: () => VideoAbuseModel.unscoped(), + model: VideoAbuseModel.unscoped(), required: false, include: [ buildVideoInclude(true) ] }, + { attributes: [ 'id' ], - model: () => VideoBlacklistModel.unscoped(), + model: VideoBlacklistModel.unscoped(), required: false, include: [ buildVideoInclude(true) ] }, + { attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ], - model: () => VideoImportModel.unscoped(), + model: VideoImportModel.unscoped(), required: false, include: [ buildVideoInclude(false) ] }, + { - attributes: [ 'id', 'name' ], - model: () => AccountModel.unscoped(), - required: false, - include: [ - { - attributes: [ 'id', 'preferredUsername' ], - model: () => ActorModel.unscoped(), - required: true - } - ] - }, - { - attributes: [ 'id' ], - model: () => ActorFollowModel.unscoped(), + attributes: [ 'id', 'state' ], + model: ActorFollowModel.unscoped(), required: false, include: [ { attributes: [ 'preferredUsername' ], - model: () => ActorModel.unscoped(), + model: ActorModel.unscoped(), required: true, as: 'ActorFollower', - include: [ buildAccountInclude(true) ] + include: [ + { + attributes: [ 'id', 'name' ], + model: AccountModel.unscoped(), + required: true + }, + { + attributes: [ 'filename' ], + model: AvatarModel.unscoped(), + required: false + }, + { + attributes: [ 'host' ], + model: ServerModel.unscoped(), + required: false + } + ] }, { - attributes: [ 'preferredUsername' ], - model: () => ActorModel.unscoped(), + attributes: [ 'preferredUsername', 'type' ], + model: ActorModel.unscoped(), required: true, as: 'ActorFollowing', include: [ buildChannelInclude(false), - buildAccountInclude(false) + buildAccountInclude(false), + { + attributes: [ 'host' ], + model: ServerModel.unscoped(), + required: false + } ] } ] - } + }, + + buildAccountInclude(false, true) ] } -}) +})) @Table({ tableName: 'userNotification', indexes: [ { - fields: [ 'videoId' ] + fields: [ 'userId' ] + }, + { + fields: [ 'videoId' ], + where: { + videoId: { + [Op.ne]: null + } + } + }, + { + fields: [ 'commentId' ], + where: { + commentId: { + [Op.ne]: null + } + } + }, + { + fields: [ 'videoAbuseId' ], + where: { + videoAbuseId: { + [Op.ne]: null + } + } + }, + { + fields: [ 'videoBlacklistId' ], + where: { + videoBlacklistId: { + [Op.ne]: null + } + } + }, + { + fields: [ 'videoImportId' ], + where: { + videoImportId: { + [Op.ne]: null + } + } + }, + { + fields: [ 'accountId' ], + where: { + accountId: { + [Op.ne]: null + } + } }, { - fields: [ 'commentId' ] + fields: [ 'actorFollowId' ], + where: { + actorFollowId: { + [Op.ne]: null + } + } } - ] + ] as (ModelIndexesOptions & { where?: WhereOptions })[] }) export class UserNotificationModel extends Model { @@ -256,25 +337,25 @@ export class UserNotificationModel extends Model { ActorFollow: ActorFollowModel static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) { - const query: IFindOptions = { + const where = { userId } + + const query: FindOptions = { offset: start, limit: count, order: getSort(sort), - where: { - userId - } + where } if (unread !== undefined) query.where['read'] = !unread - return UserNotificationModel.scope(ScopeNames.WITH_ALL) - .findAndCountAll(query) - .then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) + return Promise.all([ + UserNotificationModel.count({ where }) + .then(count => count || 0), + + count === 0 + ? [] + : UserNotificationModel.scope(ScopeNames.WITH_ALL).findAll(query) + ]).then(([ total, data ]) => ({ total, data })) } static markAsRead (userId: number, notificationIds: number[]) { @@ -282,7 +363,7 @@ export class UserNotificationModel extends Model { where: { userId, id: { - [Op.any]: notificationIds + [Op.in]: notificationIds } } } @@ -296,13 +377,10 @@ export class UserNotificationModel extends Model { return UserNotificationModel.update({ read: true }, query) } - toFormattedJSON (): UserNotification { - const video = this.Video ? Object.assign(this.formatVideo(this.Video), { - channel: { - id: this.Video.VideoChannel.id, - displayName: this.Video.VideoChannel.getDisplayName() - } - }) : undefined + toFormattedJSON (this: UserNotificationModelForApi): UserNotification { + const video = this.Video + ? Object.assign(this.formatVideo(this.Video), { channel: this.formatActor(this.Video.VideoChannel) }) + : undefined const videoImport = this.VideoImport ? { id: this.VideoImport.id, @@ -315,10 +393,7 @@ export class UserNotificationModel extends Model { const comment = this.Comment ? { id: this.Comment.id, threadId: this.Comment.getThreadId(), - account: { - id: this.Comment.Account.id, - displayName: this.Comment.Account.getDisplayName() - }, + account: this.formatActor(this.Comment.Account), video: this.formatVideo(this.Comment.Video) } : undefined @@ -332,22 +407,28 @@ export class UserNotificationModel extends Model { video: this.formatVideo(this.VideoBlacklist.Video) } : undefined - const account = this.Account ? { - id: this.Account.id, - displayName: this.Account.getDisplayName(), - name: this.Account.Actor.preferredUsername - } : undefined + const account = this.Account ? this.formatActor(this.Account) : undefined + const actorFollowingType = { + Application: 'instance' as 'instance', + Group: 'channel' as 'channel', + Person: 'account' as 'account' + } const actorFollow = this.ActorFollow ? { id: this.ActorFollow.id, + state: this.ActorFollow.state, follower: { + id: this.ActorFollow.ActorFollower.Account.id, displayName: this.ActorFollow.ActorFollower.Account.getDisplayName(), - name: this.ActorFollow.ActorFollower.preferredUsername + name: this.ActorFollow.ActorFollower.preferredUsername, + avatar: this.ActorFollow.ActorFollower.Avatar ? { path: this.ActorFollow.ActorFollower.Avatar.getStaticPath() } : undefined, + host: this.ActorFollow.ActorFollower.getHost() }, following: { - type: this.ActorFollow.ActorFollowing.VideoChannel ? 'channel' as 'channel' : 'account' as 'account', + type: actorFollowingType[this.ActorFollow.ActorFollowing.type], displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(), - name: this.ActorFollow.ActorFollowing.preferredUsername + name: this.ActorFollow.ActorFollowing.preferredUsername, + host: this.ActorFollow.ActorFollowing.getHost() } } : undefined @@ -367,11 +448,28 @@ export class UserNotificationModel extends Model { } } - private formatVideo (video: VideoModel) { + formatVideo (this: UserNotificationModelForApi, video: UserNotificationIncludes.VideoInclude) { return { id: video.id, uuid: video.uuid, name: video.name } } + + formatActor ( + this: UserNotificationModelForApi, + accountOrChannel: UserNotificationIncludes.AccountIncludeActor | UserNotificationIncludes.VideoChannelIncludeActor + ) { + const avatar = accountOrChannel.Actor.Avatar + ? { path: accountOrChannel.Actor.Avatar.getStaticPath() } + : undefined + + return { + id: accountOrChannel.id, + displayName: accountOrChannel.getDisplayName(), + name: accountOrChannel.Actor.preferredUsername, + host: accountOrChannel.Actor.getHost(), + avatar + } + } }