mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 07:24:13 +00:00 
			
		
		
		
	リモートのピン留め投稿取得対応 (#2798)
* Fetch featured * Handle featured change * Fix typo
This commit is contained in:
		
							parent
							
								
									b5745877ca
								
							
						
					
					
						commit
						c09a2a37fe
					
				
					 11 changed files with 199 additions and 63 deletions
				
			
		| 
						 | 
				
			
			@ -113,6 +113,7 @@ export interface ILocalUser extends IUserBase {
 | 
			
		|||
export interface IRemoteUser extends IUserBase {
 | 
			
		||||
	inbox: string;
 | 
			
		||||
	sharedInbox?: string;
 | 
			
		||||
	featured?: string;
 | 
			
		||||
	endpoints: string[];
 | 
			
		||||
	uri: string;
 | 
			
		||||
	url?: string;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/remote/activitypub/kernel/add/index.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/remote/activitypub/kernel/add/index.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
import { IRemoteUser } from '../../../../models/user';
 | 
			
		||||
import { IAdd } from '../../type';
 | 
			
		||||
import { resolveNote } from '../../models/note';
 | 
			
		||||
import { addPinned } from '../../../../services/i/pin';
 | 
			
		||||
 | 
			
		||||
export default async (actor: IRemoteUser, activity: IAdd): Promise<void> => {
 | 
			
		||||
	if ('actor' in activity && actor.uri !== activity.actor) {
 | 
			
		||||
		throw new Error('invalid actor');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (activity.target == null) {
 | 
			
		||||
		throw new Error('target is null');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (activity.target === actor.featured) {
 | 
			
		||||
		const note = await resolveNote(activity.object);
 | 
			
		||||
		await addPinned(actor, note._id);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	throw new Error(`unknown target: ${activity.target}`);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,8 @@ import like from './like';
 | 
			
		|||
import announce from './announce';
 | 
			
		||||
import accept from './accept';
 | 
			
		||||
import reject from './reject';
 | 
			
		||||
import add from './add';
 | 
			
		||||
import remove from './remove';
 | 
			
		||||
 | 
			
		||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 | 
			
		||||
	switch (activity.type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +33,14 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 | 
			
		|||
		await reject(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Add':
 | 
			
		||||
		await add(actor, activity).catch(err => console.log(err));
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Remove':
 | 
			
		||||
		await remove(actor, activity).catch(err => console.log(err));
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Announce':
 | 
			
		||||
		await announce(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/remote/activitypub/kernel/remove/index.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/remote/activitypub/kernel/remove/index.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
import { IRemoteUser } from '../../../../models/user';
 | 
			
		||||
import { IRemove } from '../../type';
 | 
			
		||||
import { resolveNote } from '../../models/note';
 | 
			
		||||
import { removePinned } from '../../../../services/i/pin';
 | 
			
		||||
 | 
			
		||||
export default async (actor: IRemoteUser, activity: IRemove): Promise<void> => {
 | 
			
		||||
	if ('actor' in activity && actor.uri !== activity.actor) {
 | 
			
		||||
		throw new Error('invalid actor');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (activity.target == null) {
 | 
			
		||||
		throw new Error('target is null');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (activity.target === actor.featured) {
 | 
			
		||||
		const note = await resolveNote(activity.object);
 | 
			
		||||
		await removePinned(actor, note._id);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	throw new Error(`unknown target: ${activity.target}`);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 | 
			
		|||
	log(`Creating the Note: ${note.id}`);
 | 
			
		||||
 | 
			
		||||
	// 投稿者をフェッチ
 | 
			
		||||
	const actor = await resolvePerson(note.attributedTo) as IRemoteUser;
 | 
			
		||||
	const actor = await resolvePerson(note.attributedTo, null, resolver) as IRemoteUser;
 | 
			
		||||
 | 
			
		||||
	// 投稿者が凍結されていたらスキップ
 | 
			
		||||
	if (actor.isSuspended) {
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 | 
			
		|||
			visibility = 'followers';
 | 
			
		||||
		} else {
 | 
			
		||||
			visibility = 'specified';
 | 
			
		||||
			visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri)));
 | 
			
		||||
			visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, null, resolver)));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//#endergion
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,15 +3,16 @@ import { toUnicode } from 'punycode';
 | 
			
		|||
import * as debug from 'debug';
 | 
			
		||||
 | 
			
		||||
import config from '../../../config';
 | 
			
		||||
import User, { validateUsername, isValidName, IUser, IRemoteUser } from '../../../models/user';
 | 
			
		||||
import User, { validateUsername, isValidName, IUser, IRemoteUser, isRemoteUser } from '../../../models/user';
 | 
			
		||||
import Resolver from '../resolver';
 | 
			
		||||
import { resolveImage } from './image';
 | 
			
		||||
import { isCollectionOrOrderedCollection, IPerson } from '../type';
 | 
			
		||||
import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type';
 | 
			
		||||
import { IDriveFile } from '../../../models/drive-file';
 | 
			
		||||
import Meta from '../../../models/meta';
 | 
			
		||||
import htmlToMFM from '../../../mfm/html-to-mfm';
 | 
			
		||||
import { updateUserStats } from '../../../services/update-chart';
 | 
			
		||||
import { URL } from 'url';
 | 
			
		||||
import { resolveNote } from './note';
 | 
			
		||||
 | 
			
		||||
const log = debug('misskey:activitypub');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -155,6 +156,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
 | 
			
		|||
			},
 | 
			
		||||
			inbox: person.inbox,
 | 
			
		||||
			sharedInbox: person.sharedInbox,
 | 
			
		||||
			featured: person.featured,
 | 
			
		||||
			endpoints: person.endpoints,
 | 
			
		||||
			uri: person.id,
 | 
			
		||||
			url: person.url,
 | 
			
		||||
| 
						 | 
				
			
			@ -211,6 +213,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
 | 
			
		|||
	user.bannerUrl = bannerUrl;
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	await updateFeatured(user._id).catch(err => console.log(err));
 | 
			
		||||
	return user;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -282,6 +285,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
 | 
			
		|||
			updatedAt: new Date(),
 | 
			
		||||
			inbox: person.inbox,
 | 
			
		||||
			sharedInbox: person.sharedInbox,
 | 
			
		||||
			featured: person.featured,
 | 
			
		||||
			avatarId: avatar ? avatar._id : null,
 | 
			
		||||
			bannerId: banner ? banner._id : null,
 | 
			
		||||
			avatarUrl: (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null,
 | 
			
		||||
| 
						 | 
				
			
			@ -303,6 +307,8 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
 | 
			
		|||
			},
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	await updateFeatured(exist._id).catch(err => console.log(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -311,7 +317,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
 | 
			
		|||
 * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
 | 
			
		||||
 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
 | 
			
		||||
 */
 | 
			
		||||
export async function resolvePerson(uri: string, verifier?: string): Promise<IUser> {
 | 
			
		||||
export async function resolvePerson(uri: string, verifier?: string, resolver?: Resolver): Promise<IUser> {
 | 
			
		||||
	if (typeof uri !== 'string') throw 'uri is not string';
 | 
			
		||||
 | 
			
		||||
	//#region このサーバーに既に登録されていたらそれを返す
 | 
			
		||||
| 
						 | 
				
			
			@ -323,5 +329,37 @@ export async function resolvePerson(uri: string, verifier?: string): Promise<IUs
 | 
			
		|||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	// リモートサーバーからフェッチしてきて登録
 | 
			
		||||
	return await createPerson(uri);
 | 
			
		||||
	if (resolver == null) resolver = new Resolver();
 | 
			
		||||
	return await createPerson(uri, resolver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function updateFeatured(userId: mongo.ObjectID) {
 | 
			
		||||
	const user = await User.findOne({ _id: userId });
 | 
			
		||||
	if (!isRemoteUser(user)) return;
 | 
			
		||||
	if (!user.featured) return;
 | 
			
		||||
 | 
			
		||||
	log(`Updating the featured: ${user.uri}`);
 | 
			
		||||
 | 
			
		||||
	const resolver = new Resolver();
 | 
			
		||||
 | 
			
		||||
	// Resolve to (Ordered)Collection Object
 | 
			
		||||
	const collection = await resolver.resolveCollection(user.featured);
 | 
			
		||||
	if (!isCollectionOrOrderedCollection(collection)) throw new Error(`Object is not Collection or OrderedCollection`);
 | 
			
		||||
 | 
			
		||||
	// Resolve to Object(may be Note) arrays
 | 
			
		||||
	const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems;
 | 
			
		||||
	const items = await resolver.resolve(unresolvedItems);
 | 
			
		||||
	if (!Array.isArray(items)) throw new Error(`Collection items is not an array`);
 | 
			
		||||
 | 
			
		||||
	// Resolve and regist Notes
 | 
			
		||||
	const featuredNotes = await Promise.all(items
 | 
			
		||||
		.filter(item => item.type === 'Note')
 | 
			
		||||
		.slice(0, 5)
 | 
			
		||||
		.map(item => resolveNote(item, resolver)));
 | 
			
		||||
 | 
			
		||||
	await User.update({ _id: user._id }, {
 | 
			
		||||
		$set: {
 | 
			
		||||
			pinnedNoteIds: featuredNotes.map(note => note._id)
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,11 +19,11 @@ export default class Resolver {
 | 
			
		|||
 | 
			
		||||
		switch (collection.type) {
 | 
			
		||||
		case 'Collection':
 | 
			
		||||
			collection.objects = collection.object.items;
 | 
			
		||||
			collection.objects = collection.items;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case 'OrderedCollection':
 | 
			
		||||
			collection.objects = collection.object.orderedItems;
 | 
			
		||||
			collection.objects = collection.orderedItems;
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,14 @@ export interface IReject extends IActivity {
 | 
			
		|||
	type: 'Reject';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IAdd extends IActivity {
 | 
			
		||||
	type: 'Add';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IRemove extends IActivity {
 | 
			
		||||
	type: 'Remove';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ILike extends IActivity {
 | 
			
		||||
	type: 'Like';
 | 
			
		||||
	_misskey_reaction: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -109,5 +117,7 @@ export type Object =
 | 
			
		|||
	IFollow |
 | 
			
		||||
	IAccept |
 | 
			
		||||
	IReject |
 | 
			
		||||
	IAdd |
 | 
			
		||||
	IRemove |
 | 
			
		||||
	ILike |
 | 
			
		||||
	IAnnounce;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
 | 
			
		||||
import User, { ILocalUser } from '../../../../models/user';
 | 
			
		||||
import Note from '../../../../models/note';
 | 
			
		||||
import { ILocalUser } from '../../../../models/user';
 | 
			
		||||
import { pack } from '../../../../models/user';
 | 
			
		||||
import { deliverPinnedChange } from '../../../../services/i/pin';
 | 
			
		||||
import { addPinned } from '../../../../services/i/pin';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
| 
						 | 
				
			
			@ -27,41 +26,18 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
 | 
			
		|||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) return rej(psErr);
 | 
			
		||||
 | 
			
		||||
	// Fetch pinee
 | 
			
		||||
	const note = await Note.findOne({
 | 
			
		||||
		_id: ps.noteId,
 | 
			
		||||
		userId: user._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (note === null) {
 | 
			
		||||
		return rej('note not found');
 | 
			
		||||
	// Processing
 | 
			
		||||
	try {
 | 
			
		||||
		await addPinned(user, ps.noteId);
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		return rej(e.message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const pinnedNoteIds = user.pinnedNoteIds || [];
 | 
			
		||||
 | 
			
		||||
	if (pinnedNoteIds.length > 5) {
 | 
			
		||||
		return rej('cannot pin more notes');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pinnedNoteIds.some(id => id.equals(note._id))) {
 | 
			
		||||
		return rej('already exists');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pinnedNoteIds.unshift(note._id);
 | 
			
		||||
 | 
			
		||||
	await User.update(user._id, {
 | 
			
		||||
		$set: {
 | 
			
		||||
			pinnedNoteIds: pinnedNoteIds
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Serialize
 | 
			
		||||
	const iObj = await pack(user, user, {
 | 
			
		||||
		detail: true
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Send response
 | 
			
		||||
	res(iObj);
 | 
			
		||||
 | 
			
		||||
	// Send Add to followers
 | 
			
		||||
	deliverPinnedChange(user._id, note._id, true);
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
 | 
			
		||||
import User, { ILocalUser } from '../../../../models/user';
 | 
			
		||||
import Note from '../../../../models/note';
 | 
			
		||||
import { ILocalUser } from '../../../../models/user';
 | 
			
		||||
import { pack } from '../../../../models/user';
 | 
			
		||||
import { deliverPinnedChange } from '../../../../services/i/pin';
 | 
			
		||||
import { removePinned } from '../../../../services/i/pin';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
| 
						 | 
				
			
			@ -27,31 +26,18 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
 | 
			
		|||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) return rej(psErr);
 | 
			
		||||
 | 
			
		||||
	// Fetch unpinee
 | 
			
		||||
	const note = await Note.findOne({
 | 
			
		||||
		_id: ps.noteId,
 | 
			
		||||
		userId: user._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (note === null) {
 | 
			
		||||
		return rej('note not found');
 | 
			
		||||
	// Processing
 | 
			
		||||
	try {
 | 
			
		||||
		await removePinned(user, ps.noteId);
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		return rej(e.message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const pinnedNoteIds = (user.pinnedNoteIds || []).filter(id => !id.equals(note._id));
 | 
			
		||||
 | 
			
		||||
	await User.update(user._id, {
 | 
			
		||||
		$set: {
 | 
			
		||||
			pinnedNoteIds: pinnedNoteIds
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Serialize
 | 
			
		||||
	const iObj = await pack(user, user, {
 | 
			
		||||
		detail: true
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Send response
 | 
			
		||||
	res(iObj);
 | 
			
		||||
 | 
			
		||||
	// Send Remove to followers
 | 
			
		||||
	deliverPinnedChange(user._id, note._id, false);
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,83 @@
 | 
			
		|||
import config from '../../config';
 | 
			
		||||
import * as mongo from 'mongodb';
 | 
			
		||||
import User, { isLocalUser, isRemoteUser, ILocalUser } from '../../models/user';
 | 
			
		||||
import User, { isLocalUser, isRemoteUser, ILocalUser, IUser } from '../../models/user';
 | 
			
		||||
import Note from '../../models/note';
 | 
			
		||||
import Following from '../../models/following';
 | 
			
		||||
import renderAdd from '../../remote/activitypub/renderer/add';
 | 
			
		||||
import renderRemove from '../../remote/activitypub/renderer/remove';
 | 
			
		||||
import packAp from '../../remote/activitypub/renderer';
 | 
			
		||||
import { deliver } from '../../queue';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 指定した投稿をピン留めします
 | 
			
		||||
 * @param user
 | 
			
		||||
 * @param noteId
 | 
			
		||||
 */
 | 
			
		||||
export async function addPinned(user: IUser, noteId: mongo.ObjectID) {
 | 
			
		||||
	// Fetch pinee
 | 
			
		||||
	const note = await Note.findOne({
 | 
			
		||||
		_id: noteId,
 | 
			
		||||
		userId: user._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (note === null) {
 | 
			
		||||
		throw new Error('note not found');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const pinnedNoteIds = user.pinnedNoteIds || [];
 | 
			
		||||
 | 
			
		||||
	if (pinnedNoteIds.length > 5) {
 | 
			
		||||
		throw new Error('cannot pin more notes');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pinnedNoteIds.some(id => id.equals(note._id))) {
 | 
			
		||||
		throw new Error('already exists');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pinnedNoteIds.unshift(note._id);
 | 
			
		||||
 | 
			
		||||
	await User.update(user._id, {
 | 
			
		||||
		$set: {
 | 
			
		||||
			pinnedNoteIds: pinnedNoteIds
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Deliver to remote followers
 | 
			
		||||
	if (isLocalUser(user)) {
 | 
			
		||||
		deliverPinnedChange(user._id, note._id, true);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 指定した投稿のピン留めを解除します
 | 
			
		||||
 * @param user
 | 
			
		||||
 * @param noteId
 | 
			
		||||
 */
 | 
			
		||||
export async function removePinned(user: IUser, noteId: mongo.ObjectID) {
 | 
			
		||||
	// Fetch unpinee
 | 
			
		||||
	const note = await Note.findOne({
 | 
			
		||||
		_id: noteId,
 | 
			
		||||
		userId: user._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (note === null) {
 | 
			
		||||
		throw new Error('note not found');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const pinnedNoteIds = (user.pinnedNoteIds || []).filter(id => !id.equals(note._id));
 | 
			
		||||
 | 
			
		||||
	await User.update(user._id, {
 | 
			
		||||
		$set: {
 | 
			
		||||
			pinnedNoteIds: pinnedNoteIds
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// Deliver to remote followers
 | 
			
		||||
	if (isLocalUser(user)) {
 | 
			
		||||
		deliverPinnedChange(user._id, noteId, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function deliverPinnedChange(userId: mongo.ObjectID, noteId: mongo.ObjectID, isAddition: boolean) {
 | 
			
		||||
	const user = await User.findOne({
 | 
			
		||||
		_id: userId
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue