parent
							
								
									d403869945
								
							
						
					
					
						commit
						0d5bc3be66
					
				
					 22 changed files with 310 additions and 80 deletions
				
			
		|  | @ -419,6 +419,9 @@ desktop/views/pages/user/user.photos.vue: | ||||||
| 
 | 
 | ||||||
| desktop/views/pages/user/user.profile.vue: | desktop/views/pages/user/user.profile.vue: | ||||||
|   follows-you: "Follows you" |   follows-you: "Follows you" | ||||||
|  |   stalk: "Stalk" | ||||||
|  |   stalking: "Stalking" | ||||||
|  |   unstalk: "Unstalk" | ||||||
|   mute: "Mute" |   mute: "Mute" | ||||||
|   muted: "Muting" |   muted: "Muting" | ||||||
|   unmute: "Unmute" |   unmute: "Unmute" | ||||||
|  |  | ||||||
|  | @ -419,6 +419,9 @@ desktop/views/pages/user/user.photos.vue: | ||||||
| 
 | 
 | ||||||
| desktop/views/pages/user/user.profile.vue: | desktop/views/pages/user/user.profile.vue: | ||||||
|   follows-you: "Vous suis" |   follows-you: "Vous suis" | ||||||
|  |   stalk: "ストークする" | ||||||
|  |   stalking: "ストーキングしています" | ||||||
|  |   unstalk: "ストーク解除" | ||||||
|   mute: "Mettre en sourdine" |   mute: "Mettre en sourdine" | ||||||
|   muted: "Muting" |   muted: "Muting" | ||||||
|   unmute: "Enlever la sourdine" |   unmute: "Enlever la sourdine" | ||||||
|  |  | ||||||
|  | @ -419,6 +419,9 @@ desktop/views/pages/user/user.photos.vue: | ||||||
| 
 | 
 | ||||||
| desktop/views/pages/user/user.profile.vue: | desktop/views/pages/user/user.profile.vue: | ||||||
|   follows-you: "フォローされています" |   follows-you: "フォローされています" | ||||||
|  |   stalk: "ストークする" | ||||||
|  |   stalking: "ストーキングしています" | ||||||
|  |   unstalk: "ストーク解除" | ||||||
|   mute: "ミュートする" |   mute: "ミュートする" | ||||||
|   muted: "ミュートしています" |   muted: "ミュートしています" | ||||||
|   unmute: "ミュート解除" |   unmute: "ミュート解除" | ||||||
|  |  | ||||||
							
								
								
									
										49
									
								
								migration/2018-04-19.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								migration/2018-04-19.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | // for Node.js interpret
 | ||||||
|  | 
 | ||||||
|  | const { default: User } = require('../built/models/user'); | ||||||
|  | const { default: Following } = require('../built/models/following'); | ||||||
|  | const { default: zip } = require('@prezzemolo/zip') | ||||||
|  | 
 | ||||||
|  | const migrate = async (following) => { | ||||||
|  | 	const follower = await User.findOne({ _id: following.followerId }); | ||||||
|  | 	const followee = await User.findOne({ _id: following.followeeId }); | ||||||
|  | 	const result = await Following.update(following._id, { | ||||||
|  | 		$set: { | ||||||
|  | 			stalk: true, | ||||||
|  | 			_follower: { | ||||||
|  | 				host: follower.host, | ||||||
|  | 				inbox: follower.host != null ? follower.inbox : undefined | ||||||
|  | 			}, | ||||||
|  | 			_followee: { | ||||||
|  | 				host: followee.host, | ||||||
|  | 				inbox: followee.host != null ? followee.inbox : undefined | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  | 	return result.ok === 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function main() { | ||||||
|  | 	const count = await Following.count({}); | ||||||
|  | 
 | ||||||
|  | 	const dop = Number.parseInt(process.argv[2]) || 5 | ||||||
|  | 	const idop = ((count - (count % dop)) / dop) + 1 | ||||||
|  | 
 | ||||||
|  | 	return zip( | ||||||
|  | 		1, | ||||||
|  | 		async (time) => { | ||||||
|  | 			console.log(`${time} / ${idop}`) | ||||||
|  | 			const doc = await Following.find({}, { | ||||||
|  | 				limit: dop, skip: time * dop | ||||||
|  | 			}) | ||||||
|  | 			return Promise.all(doc.map(migrate)) | ||||||
|  | 		}, | ||||||
|  | 		idop | ||||||
|  | 	).then(a => { | ||||||
|  | 		const rv = [] | ||||||
|  | 		a.forEach(e => rv.push(...e)) | ||||||
|  | 		return rv | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | main().then(console.dir).catch(console.error) | ||||||
|  | @ -3,8 +3,14 @@ | ||||||
| 	<div class="friend-form" v-if="os.isSignedIn && os.i.id != user.id"> | 	<div class="friend-form" v-if="os.isSignedIn && os.i.id != user.id"> | ||||||
| 		<mk-follow-button :user="user" size="big"/> | 		<mk-follow-button :user="user" size="big"/> | ||||||
| 		<p class="followed" v-if="user.isFollowed">%i18n:@follows-you%</p> | 		<p class="followed" v-if="user.isFollowed">%i18n:@follows-you%</p> | ||||||
| 		<p v-if="user.isMuted">%i18n:@muted% <a @click="unmute">%i18n:@unmute%</a></p> | 		<p class="stalk"> | ||||||
| 		<p v-if="!user.isMuted"><a @click="mute">%i18n:@mute%</a></p> | 			<span v-if="user.isStalking">%i18n:@stalking% <a @click="unstalk">%i18n:@unstalk%</a></span> | ||||||
|  | 			<span v-if="!user.isStalking"><a @click="stalk">%i18n:@stalk%</a></span> | ||||||
|  | 		</p> | ||||||
|  | 		<p class="mute"> | ||||||
|  | 			<span v-if="user.isMuted">%i18n:@muted% <a @click="unmute">%i18n:@unmute%</a></span> | ||||||
|  | 			<span v-if="!user.isMuted"><a @click="mute">%i18n:@mute%</a></span> | ||||||
|  | 		</p> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div class="description" v-if="user.description">{{ user.description }}</div> | 	<div class="description" v-if="user.description">{{ user.description }}</div> | ||||||
| 	<div class="birthday" v-if="user.host === null && user.profile.birthday"> | 	<div class="birthday" v-if="user.host === null && user.profile.birthday"> | ||||||
|  | @ -47,6 +53,26 @@ export default Vue.extend({ | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | 		stalk() { | ||||||
|  | 			(this as any).api('following/stalk', { | ||||||
|  | 				userId: this.user.id | ||||||
|  | 			}).then(() => { | ||||||
|  | 				this.user.isStalking = true; | ||||||
|  | 			}, () => { | ||||||
|  | 				alert('error'); | ||||||
|  | 			}); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		unstalk() { | ||||||
|  | 			(this as any).api('following/unstalk', { | ||||||
|  | 				userId: this.user.id | ||||||
|  | 			}).then(() => { | ||||||
|  | 				this.user.isStalking = false; | ||||||
|  | 			}, () => { | ||||||
|  | 				alert('error'); | ||||||
|  | 			}); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
| 		mute() { | 		mute() { | ||||||
| 			(this as any).api('mute/create', { | 			(this as any).api('mute/create', { | ||||||
| 				userId: this.user.id | 				userId: this.user.id | ||||||
|  |  | ||||||
|  | @ -10,6 +10,17 @@ export type IFollowing = { | ||||||
| 	createdAt: Date; | 	createdAt: Date; | ||||||
| 	followeeId: mongo.ObjectID; | 	followeeId: mongo.ObjectID; | ||||||
| 	followerId: mongo.ObjectID; | 	followerId: mongo.ObjectID; | ||||||
|  | 	stalk: boolean; | ||||||
|  | 
 | ||||||
|  | 	// 非正規化
 | ||||||
|  | 	_followee: { | ||||||
|  | 		host: string; | ||||||
|  | 		inbox?: string; | ||||||
|  | 	}, | ||||||
|  | 	_follower: { | ||||||
|  | 		host: string; | ||||||
|  | 		inbox?: string; | ||||||
|  | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  |  | ||||||
|  | @ -58,6 +58,7 @@ export type INote = { | ||||||
| 	}; | 	}; | ||||||
| 	uri: string; | 	uri: string; | ||||||
| 
 | 
 | ||||||
|  | 	// 非正規化
 | ||||||
| 	_reply?: { | 	_reply?: { | ||||||
| 		userId: mongo.ObjectID; | 		userId: mongo.ObjectID; | ||||||
| 	}; | 	}; | ||||||
|  | @ -66,9 +67,7 @@ export type INote = { | ||||||
| 	}; | 	}; | ||||||
| 	_user: { | 	_user: { | ||||||
| 		host: string; | 		host: string; | ||||||
| 		account: { | 		inbox?: string; | ||||||
| 			inbox?: string; |  | ||||||
| 		}; |  | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import db from '../db/mongodb'; | ||||||
| import Note, { pack as packNote, deleteNote } from './note'; | import Note, { pack as packNote, deleteNote } from './note'; | ||||||
| import Following, { deleteFollowing } from './following'; | import Following, { deleteFollowing } from './following'; | ||||||
| import Mute, { deleteMute } from './mute'; | import Mute, { deleteMute } from './mute'; | ||||||
| import getFriends from '../server/api/common/get-friends'; | import { getFriendIds } from '../server/api/common/get-friends'; | ||||||
| import config from '../config'; | import config from '../config'; | ||||||
| import AccessToken, { deleteAccessToken } from './access-token'; | import AccessToken, { deleteAccessToken } from './access-token'; | ||||||
| import NoteWatching, { deleteNoteWatching } from './note-watching'; | import NoteWatching, { deleteNoteWatching } from './note-watching'; | ||||||
|  | @ -375,33 +375,30 @@ export const pack = ( | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (meId && !meId.equals(_user.id)) { | 	if (meId && !meId.equals(_user.id)) { | ||||||
| 		// Whether the user is following
 | 		const [following1, following2, mute] = await Promise.all([ | ||||||
| 		_user.isFollowing = (async () => { | 			Following.findOne({ | ||||||
| 			const follow = await Following.findOne({ |  | ||||||
| 				followerId: meId, | 				followerId: meId, | ||||||
| 				followeeId: _user.id | 				followeeId: _user.id | ||||||
| 			}); | 			}), | ||||||
| 			return follow !== null; | 			Following.findOne({ | ||||||
| 		})(); |  | ||||||
| 
 |  | ||||||
| 		// Whether the user is followed
 |  | ||||||
| 		_user.isFollowed = (async () => { |  | ||||||
| 			const follow2 = await Following.findOne({ |  | ||||||
| 				followerId: _user.id, | 				followerId: _user.id, | ||||||
| 				followeeId: meId | 				followeeId: meId | ||||||
| 			}); | 			}), | ||||||
| 			return follow2 !== null; | 			Mute.findOne({ | ||||||
| 		})(); | 				muterId: meId, | ||||||
|  | 				muteeId: _user.id | ||||||
|  | 			}) | ||||||
|  | 		]); | ||||||
|  | 
 | ||||||
|  | 		// Whether the user is following
 | ||||||
|  | 		_user.isFollowing = following1 !== null; | ||||||
|  | 		_user.isStalking = following1 && following1.stalk; | ||||||
|  | 
 | ||||||
|  | 		// Whether the user is followed
 | ||||||
|  | 		_user.isFollowed = following2 !== null; | ||||||
| 
 | 
 | ||||||
| 		// Whether the user is muted
 | 		// Whether the user is muted
 | ||||||
| 		_user.isMuted = (async () => { | 		_user.isMuted = mute !== null; | ||||||
| 			const mute = await Mute.findOne({ |  | ||||||
| 				muterId: meId, |  | ||||||
| 				muteeId: _user.id, |  | ||||||
| 				deletedAt: { $exists: false } |  | ||||||
| 			}); |  | ||||||
| 			return mute !== null; |  | ||||||
| 		})(); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (opts.detail) { | 	if (opts.detail) { | ||||||
|  | @ -413,7 +410,7 @@ export const pack = ( | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (meId && !meId.equals(_user.id)) { | 		if (meId && !meId.equals(_user.id)) { | ||||||
| 			const myFollowingIds = await getFriends(meId); | 			const myFollowingIds = await getFriendIds(meId); | ||||||
| 
 | 
 | ||||||
| 			// Get following you know count
 | 			// Get following you know count
 | ||||||
| 			_user.followingYouKnowCount = Following.count({ | 			_user.followingYouKnowCount = Following.count({ | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| import * as mongodb from 'mongodb'; | import * as mongodb from 'mongodb'; | ||||||
| import Following from '../../../models/following'; | import Following from '../../../models/following'; | ||||||
| 
 | 
 | ||||||
| export default async (me: mongodb.ObjectID, includeMe: boolean = true) => { | export const getFriendIds = async (me: mongodb.ObjectID, includeMe = true) => { | ||||||
| 	// Fetch relation to other users who the I follows
 | 	// Fetch relation to other users who the I follows
 | ||||||
| 	// SELECT followee
 | 	// SELECT followee
 | ||||||
| 	const myfollowing = await Following | 	const followings = await Following | ||||||
| 		.find({ | 		.find({ | ||||||
| 			followerId: me | 			followerId: me | ||||||
| 		}, { | 		}, { | ||||||
|  | @ -14,7 +14,7 @@ export default async (me: mongodb.ObjectID, includeMe: boolean = true) => { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 	// ID list of other users who the I follows
 | 	// ID list of other users who the I follows
 | ||||||
| 	const myfollowingIds = myfollowing.map(follow => follow.followeeId); | 	const myfollowingIds = followings.map(following => following.followeeId); | ||||||
| 
 | 
 | ||||||
| 	if (includeMe) { | 	if (includeMe) { | ||||||
| 		myfollowingIds.push(me); | 		myfollowingIds.push(me); | ||||||
|  | @ -22,3 +22,26 @@ export default async (me: mongodb.ObjectID, includeMe: boolean = true) => { | ||||||
| 
 | 
 | ||||||
| 	return myfollowingIds; | 	return myfollowingIds; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | export const getFriends = async (me: mongodb.ObjectID, includeMe = true) => { | ||||||
|  | 	// Fetch relation to other users who the I follows
 | ||||||
|  | 	const followings = await Following | ||||||
|  | 		.find({ | ||||||
|  | 			followerId: me | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 	// ID list of other users who the I follows
 | ||||||
|  | 	const myfollowings = followings.map(following => ({ | ||||||
|  | 		id: following.followeeId, | ||||||
|  | 		stalk: following.stalk | ||||||
|  | 	})); | ||||||
|  | 
 | ||||||
|  | 	if (includeMe) { | ||||||
|  | 		myfollowings.push({ | ||||||
|  | 			id: me, | ||||||
|  | 			stalk: true | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return myfollowings; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @ -426,6 +426,24 @@ const endpoints: Endpoint[] = [ | ||||||
| 		}, | 		}, | ||||||
| 		kind: 'following-write' | 		kind: 'following-write' | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		name: 'following/stalk', | ||||||
|  | 		withCredential: true, | ||||||
|  | 		limit: { | ||||||
|  | 			duration: ms('1hour'), | ||||||
|  | 			max: 100 | ||||||
|  | 		}, | ||||||
|  | 		kind: 'following-write' | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		name: 'following/unstalk', | ||||||
|  | 		withCredential: true, | ||||||
|  | 		limit: { | ||||||
|  | 			duration: ms('1hour'), | ||||||
|  | 			max: 100 | ||||||
|  | 		}, | ||||||
|  | 		kind: 'following-write' | ||||||
|  | 	}, | ||||||
| 
 | 
 | ||||||
| 	{ | 	{ | ||||||
| 		name: 'notes' | 		name: 'notes' | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								src/server/api/endpoints/following/stalk.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/server/api/endpoints/following/stalk.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | import $ from 'cafy'; | ||||||
|  | import Following from '../../../../models/following'; | ||||||
|  | import { isLocalUser } from '../../../../models/user'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Stalk a user | ||||||
|  |  */ | ||||||
|  | module.exports = (params, user) => new Promise(async (res, rej) => { | ||||||
|  | 	const follower = user; | ||||||
|  | 
 | ||||||
|  | 	// Get 'userId' parameter
 | ||||||
|  | 	const [userId, userIdErr] = $(params.userId).id().$; | ||||||
|  | 	if (userIdErr) return rej('invalid userId param'); | ||||||
|  | 
 | ||||||
|  | 	// Fetch following
 | ||||||
|  | 	const following = await Following.findOne({ | ||||||
|  | 		followerId: follower._id, | ||||||
|  | 		followeeId: userId | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	if (following === null) { | ||||||
|  | 		return rej('following not found'); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Stalk
 | ||||||
|  | 	await Following.update({ _id: following._id }, { | ||||||
|  | 		$set: { | ||||||
|  | 			stalk: true | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	// Send response
 | ||||||
|  | 	res(); | ||||||
|  | 
 | ||||||
|  | 	// TODO: イベント
 | ||||||
|  | }); | ||||||
							
								
								
									
										35
									
								
								src/server/api/endpoints/following/unstalk.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/server/api/endpoints/following/unstalk.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | import $ from 'cafy'; | ||||||
|  | import Following from '../../../../models/following'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Unstalk a user | ||||||
|  |  */ | ||||||
|  | module.exports = (params, user) => new Promise(async (res, rej) => { | ||||||
|  | 	const follower = user; | ||||||
|  | 
 | ||||||
|  | 	// Get 'userId' parameter
 | ||||||
|  | 	const [userId, userIdErr] = $(params.userId).id().$; | ||||||
|  | 	if (userIdErr) return rej('invalid userId param'); | ||||||
|  | 
 | ||||||
|  | 	// Fetch following
 | ||||||
|  | 	const following = await Following.findOne({ | ||||||
|  | 		followerId: follower._id, | ||||||
|  | 		followeeId: userId | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	if (following === null) { | ||||||
|  | 		return rej('following not found'); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Stalk
 | ||||||
|  | 	await Following.update({ _id: following._id }, { | ||||||
|  | 		$set: { | ||||||
|  | 			stalk: false | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	// Send response
 | ||||||
|  | 	res(); | ||||||
|  | 
 | ||||||
|  | 	// TODO: イベント
 | ||||||
|  | }); | ||||||
|  | @ -5,7 +5,7 @@ import $ from 'cafy'; | ||||||
| import Notification from '../../../../models/notification'; | import Notification from '../../../../models/notification'; | ||||||
| import Mute from '../../../../models/mute'; | import Mute from '../../../../models/mute'; | ||||||
| import { pack } from '../../../../models/notification'; | import { pack } from '../../../../models/notification'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriendIds } from '../../common/get-friends'; | ||||||
| import read from '../../common/read-notification'; | import read from '../../common/read-notification'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -62,7 +62,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { | ||||||
| 
 | 
 | ||||||
| 	if (following) { | 	if (following) { | ||||||
| 		// ID list of the user itself and other users who the user follows
 | 		// ID list of the user itself and other users who the user follows
 | ||||||
| 		const followingIds = await getFriends(user._id); | 		const followingIds = await getFriendIds(user._id); | ||||||
| 
 | 
 | ||||||
| 		query.$and.push({ | 		query.$and.push({ | ||||||
| 			notifierId: { | 			notifierId: { | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| import $ from 'cafy'; | import $ from 'cafy'; | ||||||
| import Mute from '../../../../models/mute'; | import Mute from '../../../../models/mute'; | ||||||
| import { pack } from '../../../../models/user'; | import { pack } from '../../../../models/user'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriendIds } from '../../common/get-friends'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get muted users of a user |  * Get muted users of a user | ||||||
|  | @ -34,7 +34,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { | ||||||
| 
 | 
 | ||||||
| 	if (iknow) { | 	if (iknow) { | ||||||
| 		// Get my friends
 | 		// Get my friends
 | ||||||
| 		const myFriends = await getFriends(me._id); | 		const myFriends = await getFriendIds(me._id); | ||||||
| 
 | 
 | ||||||
| 		query.muteeId = { | 		query.muteeId = { | ||||||
| 			$in: myFriends | 			$in: myFriends | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
|  */ |  */ | ||||||
| import $ from 'cafy'; | import $ from 'cafy'; | ||||||
| import Note from '../../../../models/note'; | import Note from '../../../../models/note'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriendIds } from '../../common/get-friends'; | ||||||
| import { pack } from '../../../../models/note'; | import { pack } from '../../../../models/note'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -46,7 +46,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	if (following) { | 	if (following) { | ||||||
| 		const followingIds = await getFriends(user._id); | 		const followingIds = await getFriendIds(user._id); | ||||||
| 
 | 
 | ||||||
| 		query.userId = { | 		query.userId = { | ||||||
| 			$in: followingIds | 			$in: followingIds | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ const escapeRegexp = require('escape-regexp'); | ||||||
| import Note from '../../../../models/note'; | import Note from '../../../../models/note'; | ||||||
| import User from '../../../../models/user'; | import User from '../../../../models/user'; | ||||||
| import Mute from '../../../../models/mute'; | import Mute from '../../../../models/mute'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriendIds } from '../../common/get-friends'; | ||||||
| import { pack } from '../../../../models/note'; | import { pack } from '../../../../models/note'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -156,7 +156,7 @@ async function search( | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (following != null && me != null) { | 	if (following != null && me != null) { | ||||||
| 		const ids = await getFriends(me._id, false); | 		const ids = await getFriendIds(me._id, false); | ||||||
| 		push({ | 		push({ | ||||||
| 			userId: following ? { | 			userId: following ? { | ||||||
| 				$in: ids | 				$in: ids | ||||||
|  |  | ||||||
|  | @ -2,11 +2,10 @@ | ||||||
|  * Module dependencies |  * Module dependencies | ||||||
|  */ |  */ | ||||||
| import $ from 'cafy'; | import $ from 'cafy'; | ||||||
| import rap from '@prezzemolo/rap'; |  | ||||||
| import Note from '../../../../models/note'; | import Note from '../../../../models/note'; | ||||||
| import Mute from '../../../../models/mute'; | import Mute from '../../../../models/mute'; | ||||||
| import ChannelWatching from '../../../../models/channel-watching'; | import ChannelWatching from '../../../../models/channel-watching'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriends } from '../../common/get-friends'; | ||||||
| import { pack } from '../../../../models/note'; | import { pack } from '../../../../models/note'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -38,41 +37,66 @@ module.exports = async (params, user, app) => { | ||||||
| 		throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; | 		throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const { followingIds, watchingChannelIds, mutedUserIds } = await rap({ | 	const [followings, watchingChannelIds, mutedUserIds] = await Promise.all([ | ||||||
| 		// ID list of the user itself and other users who the user follows
 | 		// フォローを取得
 | ||||||
| 		followingIds: getFriends(user._id), | 		// Fetch following
 | ||||||
|  | 		getFriends(user._id), | ||||||
| 
 | 
 | ||||||
| 		// Watchしているチャンネルを取得
 | 		// Watchしているチャンネルを取得
 | ||||||
| 		watchingChannelIds: ChannelWatching.find({ | 		ChannelWatching.find({ | ||||||
| 			userId: user._id, | 			userId: user._id, | ||||||
| 			// 削除されたドキュメントは除く
 | 			// 削除されたドキュメントは除く
 | ||||||
| 			deletedAt: { $exists: false } | 			deletedAt: { $exists: false } | ||||||
| 		}).then(watches => watches.map(w => w.channelId)), | 		}).then(watches => watches.map(w => w.channelId)), | ||||||
| 
 | 
 | ||||||
| 		// ミュートしているユーザーを取得
 | 		// ミュートしているユーザーを取得
 | ||||||
| 		mutedUserIds: Mute.find({ | 		Mute.find({ | ||||||
| 			muterId: user._id | 			muterId: user._id | ||||||
| 		}).then(ms => ms.map(m => m.muteeId)) | 		}).then(ms => ms.map(m => m.muteeId)) | ||||||
| 	}); | 	]); | ||||||
| 
 | 
 | ||||||
| 	//#region Construct query
 | 	//#region Construct query
 | ||||||
| 	const sort = { | 	const sort = { | ||||||
| 		_id: -1 | 		_id: -1 | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	const followQuery = followings.map(f => f.stalk ? { | ||||||
|  | 		userId: f.id | ||||||
|  | 	} : { | ||||||
|  | 		userId: f.id, | ||||||
|  | 
 | ||||||
|  | 		// ストーキングしてないならリプライは含めない(ただし投稿者自身の投稿へのリプライ、自分の投稿へのリプライ、自分のリプライは含める)
 | ||||||
|  | 		$or: [{ | ||||||
|  | 			// リプライでない
 | ||||||
|  | 			replyId: null | ||||||
|  | 		}, { // または
 | ||||||
|  | 			// リプライだが返信先が投稿者自身の投稿
 | ||||||
|  | 			$expr: { | ||||||
|  | 				'$_reply.userId': '$userId' | ||||||
|  | 			} | ||||||
|  | 		}, { // または
 | ||||||
|  | 			// リプライだが返信先が自分(フォロワー)の投稿
 | ||||||
|  | 			'_reply.userId': user._id | ||||||
|  | 		}, { // または
 | ||||||
|  | 			// 自分(フォロワー)が送信したリプライ
 | ||||||
|  | 			userId: user._id | ||||||
|  | 		}] | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
| 	const query = { | 	const query = { | ||||||
| 		$or: [{ | 		$or: [{ | ||||||
| 			// フォローしている人のタイムラインへの投稿
 | 			$and: [{ | ||||||
| 			userId: { | 				// フォローしている人のタイムラインへの投稿
 | ||||||
| 				$in: followingIds | 				$or: followQuery | ||||||
| 			}, |  | ||||||
| 			// 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る
 |  | ||||||
| 			$or: [{ |  | ||||||
| 				channelId: { |  | ||||||
| 					$exists: false |  | ||||||
| 				} |  | ||||||
| 			}, { | 			}, { | ||||||
| 				channelId: null | 				// 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る
 | ||||||
|  | 				$or: [{ | ||||||
|  | 					channelId: { | ||||||
|  | 						$exists: false | ||||||
|  | 					} | ||||||
|  | 				}, { | ||||||
|  | 					channelId: null | ||||||
|  | 				}] | ||||||
| 			}] | 			}] | ||||||
| 		}, { | 		}, { | ||||||
| 			// Watchしているチャンネルへの投稿
 | 			// Watchしているチャンネルへの投稿
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import $ from 'cafy'; | ||||||
| import User from '../../../../models/user'; | import User from '../../../../models/user'; | ||||||
| import Following from '../../../../models/following'; | import Following from '../../../../models/following'; | ||||||
| import { pack } from '../../../../models/user'; | import { pack } from '../../../../models/user'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriendIds } from '../../common/get-friends'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get followers of a user |  * Get followers of a user | ||||||
|  | @ -52,7 +52,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { | ||||||
| 	// ログインしていてかつ iknow フラグがあるとき
 | 	// ログインしていてかつ iknow フラグがあるとき
 | ||||||
| 	if (me && iknow) { | 	if (me && iknow) { | ||||||
| 		// Get my friends
 | 		// Get my friends
 | ||||||
| 		const myFriends = await getFriends(me._id); | 		const myFriends = await getFriendIds(me._id); | ||||||
| 
 | 
 | ||||||
| 		query.followerId = { | 		query.followerId = { | ||||||
| 			$in: myFriends | 			$in: myFriends | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import $ from 'cafy'; | ||||||
| import User from '../../../../models/user'; | import User from '../../../../models/user'; | ||||||
| import Following from '../../../../models/following'; | import Following from '../../../../models/following'; | ||||||
| import { pack } from '../../../../models/user'; | import { pack } from '../../../../models/user'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriendIds } from '../../common/get-friends'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get following users of a user |  * Get following users of a user | ||||||
|  | @ -52,7 +52,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { | ||||||
| 	// ログインしていてかつ iknow フラグがあるとき
 | 	// ログインしていてかつ iknow フラグがあるとき
 | ||||||
| 	if (me && iknow) { | 	if (me && iknow) { | ||||||
| 		// Get my friends
 | 		// Get my friends
 | ||||||
| 		const myFriends = await getFriends(me._id); | 		const myFriends = await getFriendIds(me._id); | ||||||
| 
 | 
 | ||||||
| 		query.followeeId = { | 		query.followeeId = { | ||||||
| 			$in: myFriends | 			$in: myFriends | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| const ms = require('ms'); | const ms = require('ms'); | ||||||
| import $ from 'cafy'; | import $ from 'cafy'; | ||||||
| import User, { pack } from '../../../../models/user'; | import User, { pack } from '../../../../models/user'; | ||||||
| import getFriends from '../../common/get-friends'; | import { getFriendIds } from '../../common/get-friends'; | ||||||
| import Mute from '../../../../models/mute'; | import Mute from '../../../../models/mute'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -24,7 +24,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => { | ||||||
| 	if (offsetErr) return rej('invalid offset param'); | 	if (offsetErr) return rej('invalid offset param'); | ||||||
| 
 | 
 | ||||||
| 	// ID list of the user itself and other users who the user follows
 | 	// ID list of the user itself and other users who the user follows
 | ||||||
| 	const followingIds = await getFriends(me._id); | 	const followingIds = await getFriendIds(me._id); | ||||||
| 
 | 
 | ||||||
| 	// ミュートしているユーザーを取得
 | 	// ミュートしているユーザーを取得
 | ||||||
| 	const mutedUserIds = (await Mute.find({ | 	const mutedUserIds = (await Mute.find({ | ||||||
|  |  | ||||||
|  | @ -13,7 +13,18 @@ export default async function(follower: IUser, followee: IUser, activity?) { | ||||||
| 	const following = await Following.insert({ | 	const following = await Following.insert({ | ||||||
| 		createdAt: new Date(), | 		createdAt: new Date(), | ||||||
| 		followerId: follower._id, | 		followerId: follower._id, | ||||||
| 		followeeId: followee._id | 		followeeId: followee._id, | ||||||
|  | 		stalk: true, | ||||||
|  | 
 | ||||||
|  | 		// 非正規化
 | ||||||
|  | 		_follower: { | ||||||
|  | 			host: follower.host, | ||||||
|  | 			inbox: isRemoteUser(follower) ? follower.inbox : undefined | ||||||
|  | 		}, | ||||||
|  | 		_followee: { | ||||||
|  | 			host: followee.host, | ||||||
|  | 			inbox: isRemoteUser(followee) ? followee.inbox : undefined | ||||||
|  | 		} | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	//#region Increment following count
 | 	//#region Increment following count
 | ||||||
|  |  | ||||||
|  | @ -124,19 +124,8 @@ export default async (user: IUser, data: { | ||||||
| 		publishGlobalTimelineStream(noteObj); | 		publishGlobalTimelineStream(noteObj); | ||||||
| 
 | 
 | ||||||
| 		// Fetch all followers
 | 		// Fetch all followers
 | ||||||
| 		const followers = await Following.aggregate([{ | 		const followers = await Following.find({ | ||||||
| 			$lookup: { | 			followeeId: note.userId | ||||||
| 				from: 'users', |  | ||||||
| 				localField: 'followerId', |  | ||||||
| 				foreignField: '_id', |  | ||||||
| 				as: 'user' |  | ||||||
| 			} |  | ||||||
| 		}, { |  | ||||||
| 			$match: { |  | ||||||
| 				followeeId: note.userId |  | ||||||
| 			} |  | ||||||
| 		}], { |  | ||||||
| 			_id: false |  | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		if (!silent) { | 		if (!silent) { | ||||||
|  | @ -157,12 +146,15 @@ export default async (user: IUser, data: { | ||||||
| 				deliver(user, await render(), data.renote._user.inbox); | 				deliver(user, await render(), data.renote._user.inbox); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			Promise.all(followers.map(async follower => { | 			Promise.all(followers.map(async following => { | ||||||
| 				follower = follower.user[0]; | 				const follower = following._follower; | ||||||
| 
 | 
 | ||||||
| 				if (isLocalUser(follower)) { | 				if (isLocalUser(follower)) { | ||||||
|  | 					// この投稿が返信かつstalkフォローでないならスキップ
 | ||||||
|  | 					if (note.replyId && !following.stalk) return; | ||||||
|  | 
 | ||||||
| 					// Publish event to followers stream
 | 					// Publish event to followers stream
 | ||||||
| 					stream(follower._id, 'note', noteObj); | 					stream(following.followerId, 'note', noteObj); | ||||||
| 				} else { | 				} else { | ||||||
| 					// フォロワーがリモートユーザーかつ投稿者がローカルユーザーなら投稿を配信
 | 					// フォロワーがリモートユーザーかつ投稿者がローカルユーザーなら投稿を配信
 | ||||||
| 					if (isLocalUser(user)) { | 					if (isLocalUser(user)) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue