1 import { values } from 'lodash'
2 import * as Sequelize from 'sequelize'
4 import { addMethodsToModel, getSort } from '../utils'
5 import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface'
6 import { FOLLOW_STATES } from '../../initializers/constants'
8 let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes>
9 let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget
10 let listFollowingForApi: AccountFollowMethods.ListFollowingForApi
11 let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
12 let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
13 let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
14 let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
15 let toFormattedJSON: AccountFollowMethods.ToFormattedJSON
17 export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
18 AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
21 type: DataTypes.ENUM(values(FOLLOW_STATES)),
28 fields: [ 'accountId' ]
31 fields: [ 'targetAccountId' ]
34 fields: [ 'accountId', 'targetAccountId' ],
41 const classMethods = [
43 loadByAccountAndTarget,
46 listAcceptedFollowerUrlsForApi,
47 listAcceptedFollowingUrlsForApi,
48 listAcceptedFollowerSharedInboxUrls
50 const instanceMethods = [
53 addMethodsToModel(AccountFollow, classMethods, instanceMethods)
58 // ------------------------------ STATICS ------------------------------
60 function associate (models) {
61 AccountFollow.belongsTo(models.Account, {
66 as: 'AccountFollower',
70 AccountFollow.belongsTo(models.Account, {
72 name: 'targetAccountId',
75 as: 'AccountFollowing',
80 toFormattedJSON = function (this: AccountFollowInstance) {
81 const follower = this.AccountFollower.toFormattedJSON()
82 const following = this.AccountFollowing.toFormattedJSON()
89 createdAt: this.createdAt,
90 updatedAt: this.updatedAt
96 loadByAccountAndTarget = function (accountId: number, targetAccountId: number, t?: Sequelize.Transaction) {
104 model: AccountFollow[ 'sequelize' ].models.Account,
106 as: 'AccountFollower'
109 model: AccountFollow['sequelize'].models.Account,
111 as: 'AccountFollowing'
117 return AccountFollow.findOne(query)
120 listFollowingForApi = function (id: number, start: number, count: number, sort: string) {
125 order: [ getSort(sort) ],
128 model: AccountFollow[ 'sequelize' ].models.Account,
130 as: 'AccountFollower',
136 model: AccountFollow['sequelize'].models.Account,
137 as: 'AccountFollowing',
139 include: [ AccountFollow['sequelize'].models.Server ]
144 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
152 listFollowersForApi = function (id: number, start: number, count: number, sort: string) {
157 order: [ getSort(sort) ],
160 model: AccountFollow[ 'sequelize' ].models.Account,
162 as: 'AccountFollower',
163 include: [ AccountFollow['sequelize'].models.Server ]
166 model: AccountFollow['sequelize'].models.Account,
167 as: 'AccountFollowing',
176 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
184 listAcceptedFollowerUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
185 return createListAcceptedFollowForApiQuery('followers', accountIds, start, count)
188 listAcceptedFollowerSharedInboxUrls = function (accountIds: number[]) {
189 return createListAcceptedFollowForApiQuery('followers', accountIds, undefined, undefined, 'sharedInboxUrl')
192 listAcceptedFollowingUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
193 return createListAcceptedFollowForApiQuery('following', accountIds, start, count)
196 // ------------------------------ UTILS ------------------------------
198 async function createListAcceptedFollowForApiQuery (
199 type: 'followers' | 'following',
200 accountIds: number[],
205 let firstJoin: string
206 let secondJoin: string
208 if (type === 'followers') {
209 firstJoin = 'targetAccountId'
210 secondJoin = 'accountId'
212 firstJoin = 'accountId'
213 secondJoin = 'targetAccountId'
216 const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
217 const tasks: Promise<any>[] = []
219 for (const selection of selections) {
220 let query = 'SELECT ' + selection + ' FROM "Accounts" ' +
221 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' +
222 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' +
223 'WHERE "Accounts"."id" = ANY ($accountIds) AND "AccountFollows"."state" = \'accepted\' '
225 if (count !== undefined) query += 'LIMIT ' + count
226 if (start !== undefined) query += ' OFFSET ' + start
229 bind: { accountIds },
230 type: Sequelize.QueryTypes.SELECT
232 tasks.push(AccountFollow['sequelize'].query(query, options))
235 const [ followers, [ { total } ]] = await Promise.all(tasks)
236 const urls: string[] = followers.map(f => f.url)
240 total: parseInt(total, 10)