From 7f3b9b171c37ce37b36ccb3b5324f3eb97e95fbc Mon Sep 17 00:00:00 2001 From: ThatOneCalculator <kainoa@t1c.dev> 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 <syuilotan@yahoo.co.jp> --- ...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<i32>, #[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<any>, - me?: Pick<User, "id" | "showTimelineReplies"> | null, + withReplies: boolean, + me?: Pick<User, "id"> | 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 @@ <template> - <button v-if="!hideMenu" class="menu _button" @click.stop="menu" v-tooltip="i18n.ts.menu"> + <button + v-if="!hideMenu" + class="menu _button" + @click.stop="menu" + v-tooltip="i18n.ts.menu" + > <i class="ph-dots-three-outline ph-bold ph-lg"></i> </button> <button diff --git a/packages/client/src/components/MkTimeline.vue b/packages/client/src/components/MkTimeline.vue index 11fe175d9..c3366c17a 100644 --- a/packages/client/src/components/MkTimeline.vue +++ b/packages/client/src/components/MkTimeline.vue @@ -91,7 +91,12 @@ if (props.src === "antenna") { connection.on("note", prepend); } else if (props.src === "home") { endpoint = "notes/timeline"; - connection = stream.useChannel("homeTimeline"); + query = { + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel("homeTimeline", { + withReplies: defaultStore.state.showTimelineReplies, + }); connection.on("note", prepend); connection2 = stream.useChannel("main"); @@ -102,28 +107,48 @@ if (props.src === "antenna") { tlHintClosed = defaultStore.state.tlHomeHintClosed; } else if (props.src === "local") { endpoint = "notes/local-timeline"; - connection = stream.useChannel("localTimeline"); + query = { + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel("localTimeline", { + withReplies: defaultStore.state.showTimelineReplies, + }); connection.on("note", prepend); tlHint = i18n.ts._tutorial.step5_4; tlHintClosed = defaultStore.state.tlLocalHintClosed; } else if (props.src === "recommended") { endpoint = "notes/recommended-timeline"; - connection = stream.useChannel("recommendedTimeline"); + query = { + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel("recommendedTimeline", { + withReplies: defaultStore.state.showTimelineReplies, + }); connection.on("note", prepend); tlHint = i18n.ts._tutorial.step5_6; tlHintClosed = defaultStore.state.tlRecommendedHintClosed; } else if (props.src === "social") { endpoint = "notes/hybrid-timeline"; - connection = stream.useChannel("hybridTimeline"); + query = { + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel("hybridTimeline", { + withReplies: defaultStore.state.showTimelineReplies, + }); connection.on("note", prepend); tlHint = i18n.ts._tutorial.step5_5; tlHintClosed = defaultStore.state.tlSocialHintClosed; } else if (props.src === "global") { endpoint = "notes/global-timeline"; - connection = stream.useChannel("globalTimeline"); + query = { + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel("globalTimeline", { + withReplies: defaultStore.state.showTimelineReplies, + }); connection.on("note", prepend); tlHint = i18n.ts._tutorial.step5_7; diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue index cf75d4fe6..36a4e2a13 100644 --- a/packages/client/src/pages/settings/general.vue +++ b/packages/client/src/pages/settings/general.vue @@ -54,7 +54,7 @@ <FormSwitch v-model="disablePagesScript" class="_formBlock">{{ i18n.ts.disablePagesScript }}</FormSwitch> - <FormSwitch v-model="profile.showTimelineReplies" class="_formBlock" + <FormSwitch v-model="showTimelineReplies" class="_formBlock" >{{ i18n.ts.flagShowTimelineReplies }}<template #caption >{{ i18n.ts.flagShowTimelineRepliesDescription }} @@ -258,24 +258,6 @@ const lang = ref(localStorage.getItem("lang")); const fontSize = ref(localStorage.getItem("fontSize")); const useSystemFont = ref(localStorage.getItem("useSystemFont") != null); -const profile = reactive({ - showTimelineReplies: $i?.showTimelineReplies, -}); -watch( - () => profile, - () => { - save(); - }, - { - deep: true, - } -); -function save() { - os.apiWithDialog("i/update", { - showTimelineReplies: !!profile.showTimelineReplies, - }); -} - async function reloadAsk() { const { canceled } = await os.confirm({ type: "info", @@ -360,6 +342,9 @@ const swipeOnDesktop = computed( const showAdminUpdates = computed( defaultStore.makeGetterSetter("showAdminUpdates") ); +const showTimelineReplies = computed( + defaultStore.makeGetterSetter("showTimelineReplies") +); watch(lang, () => { localStorage.setItem("lang", lang.value as string); diff --git a/packages/client/src/pages/settings/preferences-backups.vue b/packages/client/src/pages/settings/preferences-backups.vue index 14bb27f91..313024d86 100644 --- a/packages/client/src/pages/settings/preferences-backups.vue +++ b/packages/client/src/pages/settings/preferences-backups.vue @@ -115,6 +115,7 @@ const defaultStoreSaveKeys: (keyof (typeof defaultStore)["state"])[] = [ "enableCustomKaTeXMacro", "enableEmojiReactions", "showEmojisInReactionNotifications", + "showTimelineReplies", ]; const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [ "lightTheme", diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index 5199c336a..c8ce96b0e 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -330,6 +330,10 @@ export const defaultStore = markRaw( where: "account", default: true, }, + showTimelineReplies: { + where: "device", + default: true, + } }), ); diff --git a/packages/client/src/stream.ts b/packages/client/src/stream.ts index e96b04565..0bc6198da 100644 --- a/packages/client/src/stream.ts +++ b/packages/client/src/stream.ts @@ -3,13 +3,30 @@ import { markRaw } from "vue"; import { $i } from "@/account"; import { url } from "@/config"; -export const stream = markRaw( - new Misskey.Stream( - url, - $i - ? { - token: $i.token, - } - : null, - ), -); +let stream: Misskey.Stream | null = null; + +export function useStream(): Misskey.Stream { + if (stream) return stream; + + stream = markRaw( + new Misskey.Stream( + url, + $i + ? { + token: $i.token, + } + : null + ) + ); + + window.setTimeout(heartbeat, 1000 * 60); + + return stream; +} + +function heartbeat(): void { + if (stream != null && document.visibilityState === "visible") { + stream.send("ping"); + } + window.setTimeout(heartbeat, 1000 * 60); +}