]>
Commit | Line | Data |
---|---|---|
7a7724e6 | 1 | import { values } from 'lodash' |
e4f97bab C |
2 | import * as Sequelize from 'sequelize' |
3 | ||
51548b31 | 4 | import { addMethodsToModel, getSort } from '../utils' |
7a7724e6 C |
5 | import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface' |
6 | import { FOLLOW_STATES } from '../../initializers/constants' | |
e4f97bab C |
7 | |
8 | let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> | |
7a7724e6 | 9 | let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget |
51548b31 C |
10 | let listFollowingForApi: AccountFollowMethods.ListFollowingForApi |
11 | let listFollowersForApi: AccountFollowMethods.ListFollowersForApi | |
12 | let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi | |
13 | let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi | |
e4f97bab C |
14 | |
15 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | |
16 | AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow', | |
7a7724e6 C |
17 | { |
18 | state: { | |
19 | type: DataTypes.ENUM(values(FOLLOW_STATES)), | |
20 | allowNull: false | |
21 | } | |
22 | }, | |
e4f97bab C |
23 | { |
24 | indexes: [ | |
25 | { | |
350e31d6 C |
26 | fields: [ 'accountId' ] |
27 | }, | |
28 | { | |
29 | fields: [ 'targetAccountId' ] | |
e4f97bab C |
30 | }, |
31 | { | |
350e31d6 | 32 | fields: [ 'accountId', 'targetAccountId' ], |
e4f97bab C |
33 | unique: true |
34 | } | |
35 | ] | |
36 | } | |
37 | ) | |
38 | ||
39 | const classMethods = [ | |
350e31d6 | 40 | associate, |
51548b31 C |
41 | loadByAccountAndTarget, |
42 | listFollowingForApi, | |
43 | listFollowersForApi, | |
44 | listAcceptedFollowerUrlsForApi, | |
45 | listAcceptedFollowingUrlsForApi | |
e4f97bab C |
46 | ] |
47 | addMethodsToModel(AccountFollow, classMethods) | |
48 | ||
49 | return AccountFollow | |
50 | } | |
51 | ||
52 | // ------------------------------ STATICS ------------------------------ | |
53 | ||
54 | function associate (models) { | |
55 | AccountFollow.belongsTo(models.Account, { | |
56 | foreignKey: { | |
57 | name: 'accountId', | |
58 | allowNull: false | |
59 | }, | |
51548b31 | 60 | as: 'AccountFollower', |
e4f97bab C |
61 | onDelete: 'CASCADE' |
62 | }) | |
63 | ||
64 | AccountFollow.belongsTo(models.Account, { | |
65 | foreignKey: { | |
66 | name: 'targetAccountId', | |
67 | allowNull: false | |
68 | }, | |
51548b31 | 69 | as: 'AccountFollowing', |
e4f97bab C |
70 | onDelete: 'CASCADE' |
71 | }) | |
72 | } | |
7a7724e6 C |
73 | |
74 | loadByAccountAndTarget = function (accountId: number, targetAccountId: number) { | |
75 | const query = { | |
76 | where: { | |
77 | accountId, | |
78 | targetAccountId | |
79 | } | |
80 | } | |
81 | ||
82 | return AccountFollow.findOne(query) | |
83 | } | |
51548b31 C |
84 | |
85 | listFollowingForApi = function (id: number, start: number, count: number, sort: string) { | |
86 | const query = { | |
87 | distinct: true, | |
88 | offset: start, | |
89 | limit: count, | |
90 | order: [ getSort(sort) ], | |
91 | include: [ | |
92 | { | |
93 | model: AccountFollow[ 'sequelize' ].models.Account, | |
94 | required: true, | |
95 | as: 'AccountFollower', | |
96 | where: { | |
97 | id | |
98 | } | |
99 | }, | |
100 | { | |
101 | model: AccountFollow['sequelize'].models.Account, | |
102 | as: 'AccountFollowing', | |
103 | required: true, | |
60862425 | 104 | include: [ AccountFollow['sequelize'].models.Server ] |
51548b31 C |
105 | } |
106 | ] | |
107 | } | |
108 | ||
109 | return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { | |
110 | return { | |
111 | data: rows.map(r => r.AccountFollowing), | |
112 | total: count | |
113 | } | |
114 | }) | |
115 | } | |
116 | ||
117 | listFollowersForApi = function (id: number, start: number, count: number, sort: string) { | |
118 | const query = { | |
119 | distinct: true, | |
120 | offset: start, | |
121 | limit: count, | |
122 | order: [ getSort(sort) ], | |
123 | include: [ | |
124 | { | |
125 | model: AccountFollow[ 'sequelize' ].models.Account, | |
126 | required: true, | |
127 | as: 'AccountFollower', | |
60862425 | 128 | include: [ AccountFollow['sequelize'].models.Server ] |
51548b31 C |
129 | }, |
130 | { | |
131 | model: AccountFollow['sequelize'].models.Account, | |
132 | as: 'AccountFollowing', | |
133 | required: true, | |
134 | where: { | |
135 | id | |
136 | } | |
137 | } | |
138 | ] | |
139 | } | |
140 | ||
141 | return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { | |
142 | return { | |
143 | data: rows.map(r => r.AccountFollower), | |
144 | total: count | |
145 | } | |
146 | }) | |
147 | } | |
148 | ||
149 | listAcceptedFollowerUrlsForApi = function (id: number, start: number, count?: number) { | |
150 | return createListAcceptedFollowForApiQuery('followers', id, start, count) | |
151 | } | |
152 | ||
153 | listAcceptedFollowingUrlsForApi = function (id: number, start: number, count?: number) { | |
154 | return createListAcceptedFollowForApiQuery('following', id, start, count) | |
155 | } | |
156 | ||
157 | // ------------------------------ UTILS ------------------------------ | |
158 | ||
159 | async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', id: number, start: number, count?: number) { | |
160 | let firstJoin: string | |
161 | let secondJoin: string | |
162 | ||
163 | if (type === 'followers') { | |
164 | firstJoin = 'targetAccountId' | |
165 | secondJoin = 'accountId' | |
166 | } else { | |
167 | firstJoin = 'accountId' | |
168 | secondJoin = 'targetAccountId' | |
169 | } | |
170 | ||
171 | const selections = [ '"Followers"."url" AS "url"', 'COUNT(*) AS "total"' ] | |
172 | const tasks: Promise<any>[] = [] | |
173 | ||
174 | for (const selection of selections) { | |
175 | let query = 'SELECT ' + selection + ' FROM "Account" ' + | |
176 | 'INNER JOIN "AccountFollow" ON "AccountFollow"."' + firstJoin + '" = "Account"."id" ' + | |
177 | 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' + | |
178 | 'WHERE "Account"."id" = $id AND "AccountFollow"."state" = \'accepted\' ' + | |
179 | 'LIMIT ' + start | |
180 | ||
181 | if (count !== undefined) query += ', ' + count | |
182 | ||
183 | const options = { | |
184 | bind: { id }, | |
185 | type: Sequelize.QueryTypes.SELECT | |
186 | } | |
187 | tasks.push(AccountFollow['sequelize'].query(query, options)) | |
188 | } | |
189 | ||
190 | const [ followers, [ { total } ]] = await Promise.all(tasks) | |
191 | const urls: string[] = followers.map(f => f.url) | |
192 | ||
193 | return { | |
194 | data: urls, | |
195 | total: parseInt(total, 10) | |
196 | } | |
197 | } |