From 7f3b9b171c37ce37b36ccb3b5324f3eb97e95fbc Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Wed, 14 Jun 2023 20:17:56 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=9A=B8=20make=20"show=20replies=20?= =?UTF-8?q?in=20timeline"=20work=20as=20expected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Syuilo --- ...684206886988-remove-showTimelineReplies.js | 15 ++++++++ .../native-utils/src/model/entity/user.rs | 2 - packages/backend/src/models/entities/user.ts | 6 --- .../backend/src/models/repositories/user.ts | 1 - .../src/remote/activitypub/models/person.ts | 1 - .../api/common/generate-replies-query.ts | 17 ++++----- .../src/server/api/endpoints/i/update.ts | 3 -- .../api/endpoints/notes/global-timeline.ts | 7 +++- .../api/endpoints/notes/hybrid-timeline.ts | 7 +++- .../api/endpoints/notes/local-timeline.ts | 7 +++- .../endpoints/notes/recommended-timeline.ts | 7 +++- .../server/api/endpoints/notes/timeline.ts | 7 +++- .../api/stream/channels/global-timeline.ts | 5 ++- .../api/stream/channels/home-timeline.ts | 5 ++- .../api/stream/channels/hybrid-timeline.ts | 5 ++- .../api/stream/channels/local-timeline.ts | 5 ++- .../stream/channels/recommended-timeline.ts | 5 ++- packages/backend/test/e2e/users.ts | 19 +--------- .../client/src/components/MkFollowButton.vue | 7 +++- packages/client/src/components/MkTimeline.vue | 35 +++++++++++++++--- .../client/src/pages/settings/general.vue | 23 ++---------- .../pages/settings/preferences-backups.vue | 1 + packages/client/src/store.ts | 4 ++ packages/client/src/stream.ts | 37 ++++++++++++++----- 24 files changed, 145 insertions(+), 86 deletions(-) create mode 100644 packages/backend/migration/1684206886988-remove-showTimelineReplies.js diff --git a/packages/backend/migration/1684206886988-remove-showTimelineReplies.js b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js new file mode 100644 index 000000000..e5f8483c7 --- /dev/null +++ b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js @@ -0,0 +1,15 @@ +export class RemoveShowTimelineReplies1684206886988 { + name = "RemoveShowTimelineReplies1684206886988"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`, + ); + } +} diff --git a/packages/backend/native-utils/src/model/entity/user.rs b/packages/backend/native-utils/src/model/entity/user.rs index f30fd8ace..e76ae08c7 100644 --- a/packages/backend/native-utils/src/model/entity/user.rs +++ b/packages/backend/native-utils/src/model/entity/user.rs @@ -63,8 +63,6 @@ pub struct Model { pub hide_online_status: bool, #[sea_orm(column_name = "isDeleted")] pub is_deleted: bool, - #[sea_orm(column_name = "showTimelineReplies")] - pub show_timeline_replies: bool, #[sea_orm(column_name = "driveCapacityOverrideMb")] pub drive_capacity_override_mb: Option, #[sea_orm(column_name = "movedToUri")] diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 53dc7e60b..ddad9f3b2 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -249,12 +249,6 @@ export class User { }) public followersUri: string | null; - @Column("boolean", { - default: false, - comment: "Whether to show users replying to other users in the timeline.", - }) - public showTimelineReplies: boolean; - @Index({ unique: true }) @Column("char", { length: 16, diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 1ca9b3289..48c8d75b3 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -567,7 +567,6 @@ export const UserRepository = db.getRepository(User).extend({ mutedInstances: profile!.mutedInstances, mutingNotificationTypes: profile!.mutingNotificationTypes, emailNotificationTypes: profile!.emailNotificationTypes, - showTimelineReplies: user.showTimelineReplies || falsy, } : {}), diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 9e21fa9ff..f8208e6d7 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -279,7 +279,6 @@ export async function createPerson( tags, isBot, isCat: (person as any).isCat === true, - showTimelineReplies: false, }), )) as IRemoteUser; 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 140c1d74a..845fef130 100644 --- a/packages/backend/src/server/api/common/generate-replies-query.ts +++ b/packages/backend/src/server/api/common/generate-replies-query.ts @@ -4,7 +4,8 @@ import { Brackets } from "typeorm"; export function generateRepliesQuery( q: SelectQueryBuilder, - me?: Pick | null, + withReplies: boolean, + me?: Pick | null, ) { if (me == null) { q.andWhere( @@ -20,25 +21,21 @@ export function generateRepliesQuery( ); }), ); - } else if (!me.showTimelineReplies) { + } else if (!withReplies) { q.andWhere( new Brackets((qb) => { qb.where("note.replyId IS NULL") // 返信ではない .orWhere("note.replyUserId = :meId", { meId: me.id }) // 返信だけど自分のノートへの返信 .orWhere( new Brackets((qb) => { - qb.where( - // 返信だけど自分の行った返信 - "note.replyId IS NOT NULL", - ).andWhere("note.userId = :meId", { meId: me.id }); + qb.where("note.replyId IS NOT NULL") // 返信だけど自分の行った返信 + .andWhere("note.userId = :meId", { meId: me.id }); }), ) .orWhere( new Brackets((qb) => { - qb.where( - // 返信だけど投稿者自身への返信 - "note.replyId IS NOT NULL", - ).andWhere("note.replyUserId = note.userId"); + qb.where("note.replyId IS NOT NULL") // 返信だけど投稿者自身への返信 + .andWhere("note.replyUserId = note.userId"); }), ); }), diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index f3ff704ce..0637251a6 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -106,7 +106,6 @@ export const paramDef = { isBot: { type: "boolean" }, isCat: { type: "boolean" }, speakAsCat: { type: "boolean" }, - showTimelineReplies: { type: "boolean" }, injectFeaturedNote: { type: "boolean" }, receiveAnnouncementEmail: { type: "boolean" }, alwaysMarkNsfw: { type: "boolean" }, @@ -185,8 +184,6 @@ export default define(meta, paramDef, async (ps, _user, token) => { if (typeof ps.publicReactions === "boolean") profileUpdates.publicReactions = ps.publicReactions; if (typeof ps.isBot === "boolean") updates.isBot = ps.isBot; - if (typeof ps.showTimelineReplies === "boolean") - updates.showTimelineReplies = ps.showTimelineReplies; if (typeof ps.carefulBot === "boolean") profileUpdates.carefulBot = ps.carefulBot; if (typeof ps.autoAcceptFollowed === "boolean") 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 78a193283..0a365a6df 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -53,6 +53,11 @@ export const paramDef = { untilId: { type: "string", format: "misskey:id" }, sinceDate: { type: "integer" }, untilDate: { type: "integer" }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -87,7 +92,7 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar") .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); 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 508b268cc..4e32b0ab2 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -60,6 +60,11 @@ export const paramDef = { default: false, description: "Only show notes that have attached files.", }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -104,7 +109,7 @@ export default define(meta, paramDef, async (ps, user) => { .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); 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 797c6d77c..82e93e371 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -63,6 +63,11 @@ export const paramDef = { untilId: { type: "string", format: "misskey:id" }, sinceDate: { type: "integer" }, untilDate: { type: "integer" }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -97,7 +102,7 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); if (user) generateMutedNoteQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts index 321ab4ad7..d3b5cbff5 100644 --- a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts @@ -63,6 +63,11 @@ export const paramDef = { untilId: { type: "string", format: "misskey:id" }, sinceDate: { type: "integer" }, untilDate: { type: "integer" }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -100,7 +105,7 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); 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 62996efdd..d629deebb 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -54,6 +54,11 @@ export const paramDef = { default: false, description: "Only show notes that have attached files.", }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -100,7 +105,7 @@ export default define(meta, paramDef, async (ps, user) => { .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 4e8263bbe..2257be2b8 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -9,6 +9,7 @@ export default class extends Channel { public readonly chName = "globalTimeline"; public static shouldShare = true; public static requireCredential = false; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -22,6 +23,8 @@ export default class extends Channel { return; } + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } @@ -31,7 +34,7 @@ export default class extends Channel { if (note.channelId != null) return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index bdcd8a283..47875aeda 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -8,6 +8,7 @@ export default class extends Channel { public readonly chName = "homeTimeline"; public static shouldShare = true; public static requireCredential = true; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -15,6 +16,8 @@ export default class extends Channel { } public async init(params: any) { + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } @@ -39,7 +42,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( 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 9205d609d..1f1a9b831 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -9,6 +9,7 @@ export default class extends Channel { public readonly chName = "hybridTimeline"; public static shouldShare = true; public static requireCredential = true; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -24,6 +25,8 @@ export default class extends Channel { ) return; + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } @@ -56,7 +59,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index df0845b5b..bd488bdd7 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -8,6 +8,7 @@ export default class extends Channel { public readonly chName = "localTimeline"; public static shouldShare = true; public static requireCredential = false; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -21,6 +22,8 @@ export default class extends Channel { return; } + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } @@ -32,7 +35,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( diff --git a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts index d030c1e7e..0b78d8b66 100644 --- a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts @@ -9,6 +9,7 @@ export default class extends Channel { public readonly chName = "recommendedTimeline"; public static shouldShare = true; public static requireCredential = true; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -24,6 +25,8 @@ export default class extends Channel { ) return; + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } @@ -54,7 +57,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index 1eb304df6..672080d9a 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -44,12 +44,7 @@ describe("ユーザー", () => { }; type MeDetailed = UserDetailedNotMe & - misskey.entities.MeDetailed & { - showTimelineReplies: boolean; - achievements: object[]; - loggedInDays: number; - policies: object; - }; + misskey.entities.MeDetailed type User = MeDetailed & { token: string }; @@ -172,9 +167,6 @@ describe("ユーザー", () => { mutedInstances: user.mutedInstances, mutingNotificationTypes: user.mutingNotificationTypes, emailNotificationTypes: user.emailNotificationTypes, - showTimelineReplies: user.showTimelineReplies, - achievements: user.achievements, - loggedInDays: user.loggedInDays, policies: user.policies, ...(security ? { @@ -479,13 +471,6 @@ describe("ユーザー", () => { "follow", "receiveFollowRequest", ]); - assert.strictEqual(response.showTimelineReplies, false); - assert.deepStrictEqual(response.achievements, []); - assert.deepStrictEqual(response.loggedInDays, 0); - assert.deepStrictEqual(response.policies, DEFAULT_POLICIES); - assert.notStrictEqual(response.email, undefined); - assert.strictEqual(response.emailVerified, false); - assert.deepStrictEqual(response.securityKeysList, []); }); //#endregion @@ -551,8 +536,6 @@ describe("ユーザー", () => { { parameters: (): object => ({ isBot: false }) }, { parameters: (): object => ({ isCat: true }) }, { parameters: (): object => ({ isCat: false }) }, - { parameters: (): object => ({ showTimelineReplies: true }) }, - { parameters: (): object => ({ showTimelineReplies: false }) }, { parameters: (): object => ({ injectFeaturedNote: true }) }, { parameters: (): object => ({ injectFeaturedNote: false }) }, { parameters: (): object => ({ receiveAnnouncementEmail: true }) }, diff --git a/packages/client/src/components/MkFollowButton.vue b/packages/client/src/components/MkFollowButton.vue index 04f5e3311..bff393fdd 100644 --- a/packages/client/src/components/MkFollowButton.vue +++ b/packages/client/src/components/MkFollowButton.vue @@ -1,5 +1,10 @@