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
16 export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
17 AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
20 type: DataTypes.ENUM(values(FOLLOW_STATES)),
27 fields: [ 'accountId' ]
30 fields: [ 'targetAccountId' ]
33 fields: [ 'accountId', 'targetAccountId' ],
40 const classMethods = [
42 loadByAccountAndTarget,
45 listAcceptedFollowerUrlsForApi,
46 listAcceptedFollowingUrlsForApi,
47 listAcceptedFollowerSharedInboxUrls
49 addMethodsToModel(AccountFollow, classMethods)
54 // ------------------------------ STATICS ------------------------------
56 function associate (models) {
57 AccountFollow.belongsTo(models.Account, {
62 as: 'AccountFollower',
66 AccountFollow.belongsTo(models.Account, {
68 name: 'targetAccountId',
71 as: 'AccountFollowing',
76 loadByAccountAndTarget = function (accountId: number, targetAccountId: number) {
84 return AccountFollow.findOne(query)
87 listFollowingForApi = function (id: number, start: number, count: number, sort: string) {
92 order: [ getSort(sort) ],
95 model: AccountFollow[ 'sequelize' ].models.Account,
97 as: 'AccountFollower',
103 model: AccountFollow['sequelize'].models.Account,
104 as: 'AccountFollowing',
106 include: [ AccountFollow['sequelize'].models.Server ]
111 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
113 data: rows.map(r => r.AccountFollowing),
119 listFollowersForApi = function (id: number, start: number, count: number, sort: string) {
124 order: [ getSort(sort) ],
127 model: AccountFollow[ 'sequelize' ].models.Account,
129 as: 'AccountFollower',
130 include: [ AccountFollow['sequelize'].models.Server ]
133 model: AccountFollow['sequelize'].models.Account,
134 as: 'AccountFollowing',
143 return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
145 data: rows.map(r => r.AccountFollower),
151 listAcceptedFollowerUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
152 return createListAcceptedFollowForApiQuery('followers', accountIds, start, count)
155 listAcceptedFollowerSharedInboxUrls = function (accountIds: number[]) {
156 return createListAcceptedFollowForApiQuery('followers', accountIds, undefined, undefined, 'sharedInboxUrl')
159 listAcceptedFollowingUrlsForApi = function (accountIds: number[], start?: number, count?: number) {
160 return createListAcceptedFollowForApiQuery('following', accountIds, start, count)
163 // ------------------------------ UTILS ------------------------------
165 async function createListAcceptedFollowForApiQuery (
166 type: 'followers' | 'following',
167 accountIds: number[],
172 let firstJoin: string
173 let secondJoin: string
175 if (type === 'followers') {
176 firstJoin = 'targetAccountId'
177 secondJoin = 'accountId'
179 firstJoin = 'accountId'
180 secondJoin = 'targetAccountId'
183 const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
184 const tasks: Promise<any>[] = []
186 for (const selection of selections) {
187 let query = 'SELECT ' + selection + ' FROM "Accounts" ' +
188 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' +
189 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' +
190 'WHERE "Accounts"."id" = ANY ($accountIds) AND "AccountFollows"."state" = \'accepted\' '
192 if (start !== undefined) query += 'LIMIT ' + start
193 if (count !== undefined) query += ', ' + count
196 bind: { accountIds },
197 type: Sequelize.QueryTypes.SELECT
199 tasks.push(AccountFollow['sequelize'].query(query, options))
202 const [ followers, [ { total } ]] = await Promise.all(tasks)
203 const urls: string[] = followers.map(f => f.url)
207 total: parseInt(total, 10)