mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-28 22:49:24 +09:00
fix(backend/channel): チャンネルへのノートがリストのFTTに保存されない問題を修正、個人ハイライトに反映されるように (MisskeyIO#245)
This commit is contained in:
parent
3f5d0c60dd
commit
0c0c8f5144
@ -743,6 +743,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
if (renote.channelId != null) {
|
if (renote.channelId != null) {
|
||||||
if (renote.replyId == null) {
|
if (renote.replyId == null) {
|
||||||
this.featuredService.updateInChannelNotesRanking(renote.channelId, renote.id, 5);
|
this.featuredService.updateInChannelNotesRanking(renote.channelId, renote.id, 5);
|
||||||
|
this.featuredService.updatePerUserNotesRanking(renote.userId, renote.id, 5);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (renote.visibility === 'public' && renote.userHost == null && renote.replyId == null) {
|
if (renote.visibility === 'public' && renote.userHost == null && renote.replyId == null) {
|
||||||
@ -843,25 +844,6 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
const r = this.redisForTimelines.pipeline();
|
const r = this.redisForTimelines.pipeline();
|
||||||
|
|
||||||
if (note.channelId) {
|
|
||||||
this.funoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r);
|
|
||||||
|
|
||||||
this.funoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
|
|
||||||
|
|
||||||
const channelFollowings = await this.channelFollowingsRepository.find({
|
|
||||||
where: {
|
|
||||||
followeeId: note.channelId,
|
|
||||||
},
|
|
||||||
select: ['followerId'],
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const channelFollowing of channelFollowings) {
|
|
||||||
this.funoutTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
|
|
||||||
if (note.fileIds.length > 0) {
|
|
||||||
this.funoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: キャッシュ?
|
// TODO: キャッシュ?
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let [followings, userListMemberships] = await Promise.all([
|
let [followings, userListMemberships] = await Promise.all([
|
||||||
@ -886,22 +868,6 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
userListMemberships = userListMemberships.filter(x => x.userListUserId === user.id || followings.some(f => f.followerId === x.userListUserId));
|
userListMemberships = userListMemberships.filter(x => x.userListUserId === user.id || followings.some(f => f.followerId === x.userListUserId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする
|
|
||||||
for (const following of followings) {
|
|
||||||
// 基本的にvisibleUserIdsには自身のidが含まれている前提であること
|
|
||||||
if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue;
|
|
||||||
|
|
||||||
// 「自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合
|
|
||||||
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) {
|
|
||||||
if (!following.withReplies) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.funoutTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
|
|
||||||
if (note.fileIds.length > 0) {
|
|
||||||
this.funoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const userListMembership of userListMemberships) {
|
for (const userListMembership of userListMemberships) {
|
||||||
// ダイレクトのとき、そのリストが対象外のユーザーの場合
|
// ダイレクトのとき、そのリストが対象外のユーザーの場合
|
||||||
if (
|
if (
|
||||||
@ -920,6 +886,41 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (note.channelId) {
|
||||||
|
this.funoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r);
|
||||||
|
|
||||||
|
this.funoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
|
||||||
|
|
||||||
|
const channelFollowings = await this.channelFollowingsRepository.find({
|
||||||
|
where: {
|
||||||
|
followeeId: note.channelId,
|
||||||
|
},
|
||||||
|
select: ['followerId'],
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const channelFollowing of channelFollowings) {
|
||||||
|
this.funoutTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
|
||||||
|
if (note.fileIds.length > 0) {
|
||||||
|
this.funoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: あまりにも数が多いと redisPipeline.exec に失敗する(理由は不明)ため、3万件程度を目安に分割して実行するようにする
|
||||||
|
for (const following of followings) {
|
||||||
|
// 基本的にvisibleUserIdsには自身のidが含まれている前提であること
|
||||||
|
if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue;
|
||||||
|
|
||||||
|
// 「自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合
|
||||||
|
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) {
|
||||||
|
if (!following.withReplies) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.funoutTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
|
||||||
|
if (note.fileIds.length > 0) {
|
||||||
|
this.funoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL
|
if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL
|
||||||
this.funoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r);
|
this.funoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r);
|
||||||
if (note.fileIds.length > 0) {
|
if (note.fileIds.length > 0) {
|
||||||
@ -947,13 +948,13 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Math.random() < 0.1) {
|
if (Math.random() < 0.1) {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
this.checkHibernation(followings);
|
this.checkHibernation(followings);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
r.exec();
|
r.exec();
|
||||||
}
|
}
|
||||||
|
@ -208,6 +208,7 @@ export class ReactionService {
|
|||||||
if (note.channelId != null) {
|
if (note.channelId != null) {
|
||||||
if (note.replyId == null) {
|
if (note.replyId == null) {
|
||||||
this.featuredService.updateInChannelNotesRanking(note.channelId, note.id, 1);
|
this.featuredService.updateInChannelNotesRanking(note.channelId, note.id, 1);
|
||||||
|
this.featuredService.updatePerUserNotesRanking(note.userId, note.id, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (note.visibility === 'public' && note.userHost == null && note.replyId == null) {
|
if (note.visibility === 'public' && note.userHost == null && note.replyId == null) {
|
||||||
|
@ -200,7 +200,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||||
.andWhere('userListMemberships.userListId = :userListId', { userListId: list.id })
|
.andWhere('userListMemberships.userListId = :userListId', { userListId: list.id })
|
||||||
.andWhere('note.channelId IS NULL') // チャンネルノートではない
|
|
||||||
.andWhere(new Brackets(qb => {
|
.andWhere(new Brackets(qb => {
|
||||||
qb
|
qb
|
||||||
.where('note.replyId IS NULL') // 返信ではない
|
.where('note.replyId IS NULL') // 返信ではない
|
||||||
|
@ -978,7 +978,7 @@ describe('Timelines', () => {
|
|||||||
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
|
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi');
|
||||||
});
|
});
|
||||||
|
|
||||||
test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => {
|
test.concurrent('リスインしているユーザーのチャンネルノートが含まれる', async () => {
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body);
|
const channel = await api('/channels/create', { name: 'channel' }, bob).then(x => x.body);
|
||||||
@ -991,7 +991,8 @@ describe('Timelines', () => {
|
|||||||
|
|
||||||
const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
|
const res = await api('/notes/user-list-timeline', { listId: list.id }, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false);
|
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true);
|
||||||
|
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi');
|
||||||
});
|
});
|
||||||
|
|
||||||
test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => {
|
test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user