From 51548b31815c6f96f314ae96588a9adca150519d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 15 Nov 2017 10:10:41 +0100 Subject: Add follow tabs Following Follow Followers --- server/models/account/account-follow-interface.ts | 16 +++ server/models/account/account-follow.ts | 130 ++++++++++++++++++++- server/models/account/account-interface.ts | 8 -- server/models/account/account.ts | 132 ++-------------------- 4 files changed, 149 insertions(+), 137 deletions(-) (limited to 'server/models/account') diff --git a/server/models/account/account-follow-interface.ts b/server/models/account/account-follow-interface.ts index efdff915e..413dad190 100644 --- a/server/models/account/account-follow-interface.ts +++ b/server/models/account/account-follow-interface.ts @@ -1,13 +1,26 @@ import * as Sequelize from 'sequelize' import * as Bluebird from 'bluebird' import { FollowState } from '../../../shared/models/accounts/follow.model' +import { ResultList } from '../../../shared/models/result-list.model' +import { AccountInstance } from './account-interface' export namespace AccountFollowMethods { export type LoadByAccountAndTarget = (accountId: number, targetAccountId: number) => Bluebird + + export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList > + export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList > + + export type ListAcceptedFollowerUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList > + export type ListAcceptedFollowingUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList > } export interface AccountFollowClass { loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget + listFollowersForApi: AccountFollowMethods.ListFollowersForApi + listFollowingForApi: AccountFollowMethods.ListFollowingForApi + + listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi + listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi } export interface AccountFollowAttributes { @@ -20,6 +33,9 @@ export interface AccountFollowInstance extends AccountFollowClass, AccountFollow id: number createdAt: Date updatedAt: Date + + AccountFollower?: AccountInstance + AccountFollowing?: AccountInstance } export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model {} diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts index 7c129ab9d..6d7592326 100644 --- a/server/models/account/account-follow.ts +++ b/server/models/account/account-follow.ts @@ -1,12 +1,16 @@ import { values } from 'lodash' import * as Sequelize from 'sequelize' -import { addMethodsToModel } from '../utils' +import { addMethodsToModel, getSort } from '../utils' import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface' import { FOLLOW_STATES } from '../../initializers/constants' let AccountFollow: Sequelize.Model let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget +let listFollowingForApi: AccountFollowMethods.ListFollowingForApi +let listFollowersForApi: AccountFollowMethods.ListFollowersForApi +let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi +let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { AccountFollow = sequelize.define('AccountFollow', @@ -34,7 +38,11 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da const classMethods = [ associate, - loadByAccountAndTarget + loadByAccountAndTarget, + listFollowingForApi, + listFollowersForApi, + listAcceptedFollowerUrlsForApi, + listAcceptedFollowingUrlsForApi ] addMethodsToModel(AccountFollow, classMethods) @@ -49,7 +57,7 @@ function associate (models) { name: 'accountId', allowNull: false }, - as: 'accountFollowers', + as: 'AccountFollower', onDelete: 'CASCADE' }) @@ -58,7 +66,7 @@ function associate (models) { name: 'targetAccountId', allowNull: false }, - as: 'accountFollowing', + as: 'AccountFollowing', onDelete: 'CASCADE' }) } @@ -73,3 +81,117 @@ loadByAccountAndTarget = function (accountId: number, targetAccountId: number) { return AccountFollow.findOne(query) } + +listFollowingForApi = function (id: number, start: number, count: number, sort: string) { + const query = { + distinct: true, + offset: start, + limit: count, + order: [ getSort(sort) ], + include: [ + { + model: AccountFollow[ 'sequelize' ].models.Account, + required: true, + as: 'AccountFollower', + where: { + id + } + }, + { + model: AccountFollow['sequelize'].models.Account, + as: 'AccountFollowing', + required: true, + include: [ AccountFollow['sequelize'].models.Pod ] + } + ] + } + + return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { + return { + data: rows.map(r => r.AccountFollowing), + total: count + } + }) +} + +listFollowersForApi = function (id: number, start: number, count: number, sort: string) { + const query = { + distinct: true, + offset: start, + limit: count, + order: [ getSort(sort) ], + include: [ + { + model: AccountFollow[ 'sequelize' ].models.Account, + required: true, + as: 'AccountFollower', + include: [ AccountFollow['sequelize'].models.Pod ] + }, + { + model: AccountFollow['sequelize'].models.Account, + as: 'AccountFollowing', + required: true, + where: { + id + } + } + ] + } + + return AccountFollow.findAndCountAll(query).then(({ rows, count }) => { + return { + data: rows.map(r => r.AccountFollower), + total: count + } + }) +} + +listAcceptedFollowerUrlsForApi = function (id: number, start: number, count?: number) { + return createListAcceptedFollowForApiQuery('followers', id, start, count) +} + +listAcceptedFollowingUrlsForApi = function (id: number, start: number, count?: number) { + return createListAcceptedFollowForApiQuery('following', id, start, count) +} + +// ------------------------------ UTILS ------------------------------ + +async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', id: number, start: number, count?: number) { + let firstJoin: string + let secondJoin: string + + if (type === 'followers') { + firstJoin = 'targetAccountId' + secondJoin = 'accountId' + } else { + firstJoin = 'accountId' + secondJoin = 'targetAccountId' + } + + const selections = [ '"Followers"."url" AS "url"', 'COUNT(*) AS "total"' ] + const tasks: Promise[] = [] + + for (const selection of selections) { + let query = 'SELECT ' + selection + ' FROM "Account" ' + + 'INNER JOIN "AccountFollow" ON "AccountFollow"."' + firstJoin + '" = "Account"."id" ' + + 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' + + 'WHERE "Account"."id" = $id AND "AccountFollow"."state" = \'accepted\' ' + + 'LIMIT ' + start + + if (count !== undefined) query += ', ' + count + + const options = { + bind: { id }, + type: Sequelize.QueryTypes.SELECT + } + tasks.push(AccountFollow['sequelize'].query(query, options)) + } + + const [ followers, [ { total } ]] = await Promise.all(tasks) + const urls: string[] = followers.map(f => f.url) + + return { + data: urls, + total: parseInt(total, 10) + } +} diff --git a/server/models/account/account-interface.ts b/server/models/account/account-interface.ts index 6fc36ae9d..ce1afec02 100644 --- a/server/models/account/account-interface.ts +++ b/server/models/account/account-interface.ts @@ -15,10 +15,6 @@ export namespace AccountMethods { export type LoadLocalByName = (name: string) => Bluebird export type LoadByNameAndHost = (name: string, host: string) => Bluebird export type ListOwned = () => Bluebird - export type ListAcceptedFollowerUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList > - export type ListAcceptedFollowingUrlsForApi = (id: number, start: number, count?: number) => Promise< ResultList > - export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList > - export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList > export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor export type ToFormattedJSON = (this: AccountInstance) => FormattedAccount @@ -38,10 +34,6 @@ export interface AccountClass { loadLocalByName: AccountMethods.LoadLocalByName loadByNameAndHost: AccountMethods.LoadByNameAndHost listOwned: AccountMethods.ListOwned - listAcceptedFollowerUrlsForApi: AccountMethods.ListAcceptedFollowerUrlsForApi - listAcceptedFollowingUrlsForApi: AccountMethods.ListAcceptedFollowingUrlsForApi - listFollowingForApi: AccountMethods.ListFollowingForApi - listFollowersForApi: AccountMethods.ListFollowersForApi } export interface AccountAttributes { diff --git a/server/models/account/account.ts b/server/models/account/account.ts index d2293a939..e90eaae5e 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts @@ -23,7 +23,7 @@ import { AccountMethods } from './account-interface' import { sendDeleteAccount } from '../../lib/activitypub/send-request' -import { CONSTRAINTS_FIELDS } from '../../initializers/constants' +import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' let Account: Sequelize.Model let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID @@ -34,10 +34,6 @@ let loadByUrl: AccountMethods.LoadByUrl let loadLocalByName: AccountMethods.LoadLocalByName let loadByNameAndHost: AccountMethods.LoadByNameAndHost let listOwned: AccountMethods.ListOwned -let listAcceptedFollowerUrlsForApi: AccountMethods.ListAcceptedFollowerUrlsForApi -let listAcceptedFollowingUrlsForApi: AccountMethods.ListAcceptedFollowingUrlsForApi -let listFollowingForApi: AccountMethods.ListFollowingForApi -let listFollowersForApi: AccountMethods.ListFollowersForApi let isOwned: AccountMethods.IsOwned let toActivityPubObject: AccountMethods.ToActivityPubObject let toFormattedJSON: AccountMethods.ToFormattedJSON @@ -185,7 +181,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes unique: true }, { - fields: [ 'name', 'podId' ], + fields: [ 'name', 'podId', 'applicationId' ], unique: true } ], @@ -202,11 +198,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes loadByUrl, loadLocalByName, loadByNameAndHost, - listOwned, - listAcceptedFollowerUrlsForApi, - listAcceptedFollowingUrlsForApi, - listFollowingForApi, - listFollowersForApi + listOwned ] const instanceMethods = [ isOwned, @@ -286,9 +278,11 @@ function afterDestroy (account: AccountInstance) { } toFormattedJSON = function (this: AccountInstance) { + let host = this.Pod ? this.Pod.host : CONFIG.WEBSERVER.HOST + const json = { id: this.id, - host: this.Pod.host, + host, name: this.name } @@ -346,7 +340,7 @@ getFollowerSharedInboxUrls = function (this: AccountInstance) { } getFollowingUrl = function (this: AccountInstance) { - return this.url + '/followers' + return this.url + '/following' } getFollowersUrl = function (this: AccountInstance) { @@ -369,76 +363,6 @@ listOwned = function () { return Account.findAll(query) } -listAcceptedFollowerUrlsForApi = function (id: number, start: number, count?: number) { - return createListAcceptedFollowForApiQuery('followers', id, start, count) -} - -listAcceptedFollowingUrlsForApi = function (id: number, start: number, count?: number) { - return createListAcceptedFollowForApiQuery('following', id, start, count) -} - -listFollowingForApi = function (id: number, start: number, count: number, sort: string) { - const query = { - distinct: true, - offset: start, - limit: count, - order: [ getSort(sort) ], - include: [ - { - model: Account['sequelize'].models.AccountFollow, - required: true, - as: 'following', - include: [ - { - model: Account['sequelize'].models.Account, - as: 'accountFollowing', - required: true, - include: [ Account['sequelize'].models.Pod ] - } - ] - } - ] - } - - return Account.findAndCountAll(query).then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) -} - -listFollowersForApi = function (id: number, start: number, count: number, sort: string) { - const query = { - distinct: true, - offset: start, - limit: count, - order: [ getSort(sort) ], - include: [ - { - model: Account['sequelize'].models.AccountFollow, - required: true, - as: 'followers', - include: [ - { - model: Account['sequelize'].models.Account, - as: 'accountFollowers', - required: true, - include: [ Account['sequelize'].models.Pod ] - } - ] - } - ] - } - - return Account.findAndCountAll(query).then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) -} - loadApplication = function () { return Account.findOne({ include: [ @@ -527,45 +451,3 @@ loadAccountByPodAndUUID = function (uuid: string, podId: number, transaction: Se return Account.find(query) } - -// ------------------------------ UTILS ------------------------------ - -async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', id: number, start: number, count?: number) { - let firstJoin: string - let secondJoin: string - - if (type === 'followers') { - firstJoin = 'targetAccountId' - secondJoin = 'accountId' - } else { - firstJoin = 'accountId' - secondJoin = 'targetAccountId' - } - - const selections = [ '"Followers"."url" AS "url"', 'COUNT(*) AS "total"' ] - const tasks: Promise[] = [] - - for (const selection of selections) { - let query = 'SELECT ' + selection + ' FROM "Account" ' + - 'INNER JOIN "AccountFollow" ON "AccountFollow"."' + firstJoin + '" = "Account"."id" ' + - 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' + - 'WHERE "Account"."id" = $id AND "AccountFollow"."state" = \'accepted\' ' + - 'LIMIT ' + start - - if (count !== undefined) query += ', ' + count - - const options = { - bind: { id }, - type: Sequelize.QueryTypes.SELECT - } - tasks.push(Account['sequelize'].query(query, options)) - } - - const [ followers, [ { total } ]] = await Promise.all(tasks) - const urls: string[] = followers.map(f => f.url) - - return { - data: urls, - total: parseInt(total, 10) - } -} -- cgit v1.2.3