mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-30 21:14:12 +00:00 
			
		
		
		
	View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/652 Closes #712 Approved-by: Marie <github@yuugi.dev> Approved-by: dakkar <dakkar@thenautilus.net>
This commit is contained in:
		
						commit
						fa03c4cebe
					
				
					 6 changed files with 96 additions and 52 deletions
				
			
		|  | @ -8,11 +8,14 @@ import { DI } from '@/di-symbols.js'; | |||
| import type { FollowingsRepository } from '@/models/_.js'; | ||||
| import { awaitAll } from '@/misc/prelude/await-all.js'; | ||||
| import type { Packed } from '@/misc/json-schema.js'; | ||||
| import type { } from '@/models/Blocking.js'; | ||||
| import type { MiUser } from '@/models/User.js'; | ||||
| import type { MiFollowing } from '@/models/Following.js'; | ||||
| import { MiBlocking } from '@/models/Blocking.js'; | ||||
| import { MiUserProfile } from '@/models/UserProfile.js'; | ||||
| import type { MiLocalUser, MiUser } from '@/models/User.js'; | ||||
| import { MiFollowing } from '@/models/Following.js'; | ||||
| import { bindThis } from '@/decorators.js'; | ||||
| import { IdService } from '@/core/IdService.js'; | ||||
| import { QueryService } from '@/core/QueryService.js'; | ||||
| import { RoleService } from '@/core/RoleService.js'; | ||||
| import { UserEntityService } from './UserEntityService.js'; | ||||
| 
 | ||||
| type LocalFollowerFollowing = MiFollowing & { | ||||
|  | @ -47,6 +50,8 @@ export class FollowingEntityService { | |||
| 
 | ||||
| 		private userEntityService: UserEntityService, | ||||
| 		private idService: IdService, | ||||
| 		private queryService: QueryService, | ||||
| 		private roleService: RoleService, | ||||
| 	) { | ||||
| 	} | ||||
| 
 | ||||
|  | @ -70,6 +75,53 @@ export class FollowingEntityService { | |||
| 		return following.followeeHost != null; | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	public async getFollowing(me: MiLocalUser, params: FollowsQueryParams) { | ||||
| 		return await this.getFollows(me, params, 'following.followerHost = :host'); | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	public async getFollowers(me: MiLocalUser, params: FollowsQueryParams) { | ||||
| 		return await this.getFollows(me, params, 'following.followeeHost = :host'); | ||||
| 	} | ||||
| 
 | ||||
| 	private async getFollows(me: MiLocalUser, params: FollowsQueryParams, condition: string) { | ||||
| 		const builder = this.followingsRepository.createQueryBuilder('following'); | ||||
| 		const query = this.queryService | ||||
| 			.makePaginationQuery(builder, params.sinceId, params.untilId) | ||||
| 			.andWhere(condition, { host: params.host }) | ||||
| 			.limit(params.limit); | ||||
| 
 | ||||
| 		if (!await this.roleService.isModerator(me)) { | ||||
| 			query.setParameter('me', me.id); | ||||
| 
 | ||||
| 			// Make sure that the followee doesn't block us, if their profile will be included.
 | ||||
| 			if (params.includeFollowee) { | ||||
| 				query.leftJoin(MiBlocking, 'followee_blocking', 'followee_blocking."blockerId" = following."followeeId" AND followee_blocking."blockeeId" = :me'); | ||||
| 				query.andWhere('followee_blocking.id IS NULL'); | ||||
| 			} | ||||
| 
 | ||||
| 			// Make sure that the follower doesn't block us, if their profile will be included.
 | ||||
| 			if (params.includeFollower) { | ||||
| 				query.leftJoin(MiBlocking, 'follower_blocking', 'follower_blocking."blockerId" = following."followerId" AND follower_blocking."blockeeId" = :me'); | ||||
| 				query.andWhere('follower_blocking.id IS NULL'); | ||||
| 			} | ||||
| 
 | ||||
| 			// Make sure that the followee hasn't hidden this connection.
 | ||||
| 			query.leftJoin(MiUserProfile, 'followee', 'followee."userId" = following."followeeId"'); | ||||
| 			query.leftJoin(MiFollowing, 'me_following_followee', 'me_following_followee."followerId" = :me AND me_following_followee."followeeId" = following."followerId"'); | ||||
| 			query.andWhere('(followee."userId" = :me OR followee."followersVisibility" = \'public\' OR (followee."followersVisibility" = \'followers\' AND me_following_followee.id IS NOT NULL))'); | ||||
| 
 | ||||
| 			// Make sure that the follower hasn't hidden this connection.
 | ||||
| 			query.leftJoin(MiUserProfile, 'follower', 'follower."userId" = following."followerId"'); | ||||
| 			query.leftJoin(MiFollowing, 'me_following_follower', 'me_following_follower."followerId" = :me AND me_following_follower."followeeId" = following."followerId"'); | ||||
| 			query.andWhere('(follower."userId" = :me OR follower."followingVisibility" = \'public\' OR (follower."followingVisibility" = \'followers\' AND me_following_follower.id IS NOT NULL))'); | ||||
| 		} | ||||
| 
 | ||||
| 		const followings = await query.getMany(); | ||||
| 		return await this.packMany(followings, me, { populateFollowee: params.includeFollowee, populateFollower: params.includeFollower }); | ||||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	public async pack( | ||||
| 		src: MiFollowing['id'] | MiFollowing, | ||||
|  | @ -124,3 +176,12 @@ export class FollowingEntityService { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| interface FollowsQueryParams { | ||||
| 	readonly host: string; | ||||
| 	readonly limit: number; | ||||
| 	readonly includeFollower: boolean; | ||||
| 	readonly includeFollowee: boolean; | ||||
| 
 | ||||
| 	readonly sinceId?: string; | ||||
| 	readonly untilId?: string; | ||||
| } | ||||
|  |  | |||
|  | @ -3,17 +3,15 @@ | |||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
| 
 | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import type { FollowingsRepository } from '@/models/_.js'; | ||||
| import { QueryService } from '@/core/QueryService.js'; | ||||
| import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['federation'], | ||||
| 
 | ||||
| 	requireCredential: false, | ||||
| 	requireCredential: true, | ||||
| 	kind: 'read:account', | ||||
| 
 | ||||
| 	res: { | ||||
| 		type: 'array', | ||||
|  | @ -42,21 +40,10 @@ export const paramDef = { | |||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 | ||||
| 	constructor( | ||||
| 		@Inject(DI.followingsRepository) | ||||
| 		private followingsRepository: FollowingsRepository, | ||||
| 
 | ||||
| 		private followingEntityService: FollowingEntityService, | ||||
| 		private queryService: QueryService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) | ||||
| 				.andWhere('following.followeeHost = :host', { host: ps.host }); | ||||
| 
 | ||||
| 			const followings = await query | ||||
| 				.limit(ps.limit) | ||||
| 				.getMany(); | ||||
| 
 | ||||
| 			return await this.followingEntityService.packMany(followings, me, { populateFollowee: ps.includeFollowee, populateFollower: ps.includeFollower }); | ||||
| 			return this.followingEntityService.getFollowers(me, ps); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -3,17 +3,15 @@ | |||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
| 
 | ||||
| import { Inject, Injectable } from '@nestjs/common'; | ||||
| import { Injectable } from '@nestjs/common'; | ||||
| import { Endpoint } from '@/server/api/endpoint-base.js'; | ||||
| import type { FollowingsRepository } from '@/models/_.js'; | ||||
| import { QueryService } from '@/core/QueryService.js'; | ||||
| import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; | ||||
| import { DI } from '@/di-symbols.js'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	tags: ['federation'], | ||||
| 
 | ||||
| 	requireCredential: false, | ||||
| 	requireCredential: true, | ||||
| 	kind: 'read:account', | ||||
| 
 | ||||
| 	res: { | ||||
| 		type: 'array', | ||||
|  | @ -42,21 +40,10 @@ export const paramDef = { | |||
| @Injectable() | ||||
| export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 | ||||
| 	constructor( | ||||
| 		@Inject(DI.followingsRepository) | ||||
| 		private followingsRepository: FollowingsRepository, | ||||
| 
 | ||||
| 		private followingEntityService: FollowingEntityService, | ||||
| 		private queryService: QueryService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) | ||||
| 				.andWhere('following.followerHost = :host', { host: ps.host }); | ||||
| 
 | ||||
| 			const followings = await query | ||||
| 				.limit(ps.limit) | ||||
| 				.getMany(); | ||||
| 
 | ||||
| 			return await this.followingEntityService.packMany(followings, me, { populateFollowee: ps.includeFollowee, populateFollower: ps.includeFollower }); | ||||
| 			return this.followingEntityService.getFollowing(me, ps); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -194,6 +194,7 @@ import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; | |||
| import { dateString } from '@/filters/date.js'; | ||||
| import MkTextarea from '@/components/MkTextarea.vue'; | ||||
| import MkInfo from '@/components/MkInfo.vue'; | ||||
| import { $i } from '@/account.js'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
| 	host: string; | ||||
|  | @ -422,20 +423,28 @@ const headerTabs = computed(() => [{ | |||
| 	key: 'users', | ||||
| 	title: i18n.ts.users, | ||||
| 	icon: 'ti ti-users', | ||||
| }, { | ||||
| 	key: 'following', | ||||
| 	title: i18n.ts.following, | ||||
| 	icon: 'ti ti-arrow-right', | ||||
| }, { | ||||
| 	key: 'followers', | ||||
| 	title: i18n.ts.followers, | ||||
| 	icon: 'ti ti-arrow-left', | ||||
| }, { | ||||
| }, ...getFollowingTabs(), { | ||||
| 	key: 'raw', | ||||
| 	title: 'Raw', | ||||
| 	icon: 'ti ti-code', | ||||
| }]); | ||||
| 
 | ||||
| function getFollowingTabs() { | ||||
| 	if (!$i) return []; | ||||
| 	return [ | ||||
| 		{ | ||||
| 			key: 'following', | ||||
| 			title: i18n.ts.following, | ||||
| 			icon: 'ti ti-arrow-right', | ||||
| 		}, | ||||
| 		{ | ||||
| 			key: 'followers', | ||||
| 			title: i18n.ts.followers, | ||||
| 			icon: 'ti ti-arrow-left', | ||||
| 		}, | ||||
| 	]; | ||||
| } | ||||
| 
 | ||||
| definePageMetadata(() => ({ | ||||
| 	title: props.host, | ||||
| 	icon: 'ti ti-server', | ||||
|  |  | |||
|  | @ -1890,7 +1890,7 @@ declare module '../api.js' { | |||
|     /** | ||||
|      * No description provided. | ||||
|      *  | ||||
|      * **Credential required**: *No* | ||||
|      * **Credential required**: *Yes* / **Permission**: *read:account* | ||||
|      */ | ||||
|     request<E extends 'federation/followers', P extends Endpoints[E]['req']>( | ||||
|       endpoint: E, | ||||
|  | @ -1901,7 +1901,7 @@ declare module '../api.js' { | |||
|     /** | ||||
|      * No description provided. | ||||
|      *  | ||||
|      * **Credential required**: *No* | ||||
|      * **Credential required**: *Yes* / **Permission**: *read:account* | ||||
|      */ | ||||
|     request<E extends 'federation/following', P extends Endpoints[E]['req']>( | ||||
|       endpoint: E, | ||||
|  |  | |||
|  | @ -1645,7 +1645,7 @@ export type paths = { | |||
|      * federation/followers | ||||
|      * @description No description provided. | ||||
|      * | ||||
|      * **Credential required**: *No* | ||||
|      * **Credential required**: *Yes* / **Permission**: *read:account* | ||||
|      */ | ||||
|     post: operations['federation___followers']; | ||||
|   }; | ||||
|  | @ -1654,7 +1654,7 @@ export type paths = { | |||
|      * federation/following | ||||
|      * @description No description provided. | ||||
|      * | ||||
|      * **Credential required**: *No* | ||||
|      * **Credential required**: *Yes* / **Permission**: *read:account* | ||||
|      */ | ||||
|     post: operations['federation___following']; | ||||
|   }; | ||||
|  | @ -15563,7 +15563,7 @@ export type operations = { | |||
|    * federation/followers | ||||
|    * @description No description provided. | ||||
|    * | ||||
|    * **Credential required**: *No* | ||||
|    * **Credential required**: *Yes* / **Permission**: *read:account* | ||||
|    */ | ||||
|   federation___followers: { | ||||
|     requestBody: { | ||||
|  | @ -15626,7 +15626,7 @@ export type operations = { | |||
|    * federation/following | ||||
|    * @description No description provided. | ||||
|    * | ||||
|    * **Credential required**: *No* | ||||
|    * **Credential required**: *Yes* / **Permission**: *read:account* | ||||
|    */ | ||||
|   federation___following: { | ||||
|     requestBody: { | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue