From 191b2692d21cc2b6f3540b1b94da106bec559777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sidor?= Date: Tue, 14 Jun 2022 21:02:22 +0200 Subject: [PATCH] Modify social timeline to exclude convos with only 1 person I like This is an attempt at introducing filtering of replies in timeline in the style of Mastodon or Pleroma's "only replies directed at me or someone I follow". Currently one way this surely fails is that self-replies by someone I follow in a conversation solely with someone I don't follow will pass this filter, and I will see a conversation I don't want to see. This probably needs more testing to verify that it's doing what's expected of it. --- .../src/server/api/common/generate-replies-query.ts | 9 ++++++++- .../src/server/api/endpoints/notes/global-timeline.ts | 2 +- .../src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- .../src/server/api/endpoints/notes/local-timeline.ts | 2 +- .../backend/src/server/api/endpoints/notes/timeline.ts | 2 +- .../src/server/api/stream/channels/hybrid-timeline.ts | 7 ++----- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/server/api/common/generate-replies-query.ts b/packages/backend/src/server/api/common/generate-replies-query.ts index 31880382e..487cb8eca 100644 --- a/packages/backend/src/server/api/common/generate-replies-query.ts +++ b/packages/backend/src/server/api/common/generate-replies-query.ts @@ -1,7 +1,7 @@ import { Brackets, SelectQueryBuilder } from 'typeorm'; import { User } from '@/models/entities/user.js'; -export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null) { +export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null, followingQuery: SelectQueryBuilder | null) { if (me == null) { q.andWhere(new Brackets(qb => { qb .where('note.replyId IS NULL') // 返信ではない @@ -14,6 +14,7 @@ export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick { qb .where('note.replyId IS NULL') // 返信ではない .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信 + .orWhere('note.mentions && array[:meId]::varchar[]', { meId: me.id }) .orWhere(new Brackets(qb => { qb // 返信だけど自分の行った返信 .where('note.replyId IS NOT NULL') .andWhere('note.userId = :meId', { meId: me.id }); @@ -22,6 +23,12 @@ export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick { qb + .where(`note.mentions && array(${ followingQuery.getQuery() })`) + .setParameters(followingQuery.getParameters()) + })) + } })); } } diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 3d32e7c7a..ce77a56e3 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -75,7 +75,7 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateRepliesQuery(query, user); + generateRepliesQuery(query, user, null); if (user) { generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index f2e86915f..cf82bb500 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -89,7 +89,7 @@ export default define(meta, paramDef, async (ps, user) => { .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, user, followingQuery); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 6a65c028a..97a6b4500 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -82,7 +82,7 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, user, null); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); if (user) generateMutedNoteQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index d042e555f..13ee536ad 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -81,7 +81,7 @@ export default define(meta, paramDef, async (ps, user) => { .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, user, followingQuery); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 989e70590..05e83e35e 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -40,11 +40,8 @@ export default class extends Channel { // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; - // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { - const reply = note.reply; - // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 - if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; + if (note.reply && note.mentions && !this.user!.showTimelineReplies) { + if (!note.mentions.includes(this.user!.id) && !note.mentions.some((user: string) => this.following.has(user))) return; } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する