From 5ad0906c89af3adfcbac3c1b1208aa1fb4b6130f Mon Sep 17 00:00:00 2001 From: CyberRex Date: Wed, 27 Sep 2023 09:32:36 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat(backend):=20Master=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=82=BB=E3=82=B9=E3=81=AEPID=E3=82=92=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E5=87=BA=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1190?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .config/example.yml | 25 ++++++++++++++----------- packages/backend/src/boot/master.ts | 1 + packages/backend/src/config.ts | 3 +++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index 086a6ca8fc..03864a3299 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -30,7 +30,7 @@ url: https://example.tld/ # The port that your Misskey server should listen on. port: 3000 -# You can also use UNIX domain socket. +# You can also use UNIX domain socket. # socket: /path/to/misskey.sock # chmodSocket: '777' @@ -60,17 +60,17 @@ dbReplications: false # You can configure any number of replicas here #dbSlaves: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # - -# host: -# port: -# db: -# user: -# pass: +# host: +# port: +# db: +# user: +# pass: # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -206,3 +206,6 @@ signToActivityPubGet: true # Upload or download file size limits (bytes) #maxFileSize: 262144000 + +# PID File of master process +#pidFile: /tmp/misskey.pid diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index a45ea2bb8f..623cc964ac 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -63,6 +63,7 @@ export async function masterMain() { showNodejsVersion(); config = loadConfigBoot(); //await connectDb(); + if (config.pidFile) fs.writeFileSync(config.pidFile, process.pid.toString()); } catch (e) { bootLogger.error('Fatal error occurred during initialization', null, true); process.exit(1); diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index abbfdfed8f..f89879d535 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -89,6 +89,7 @@ type Source = { perChannelMaxNoteCacheCount?: number; perUserNotificationsMaxCount?: number; deactivateAntennaThreshold?: number; + pidFile: string; }; export type Config = { @@ -163,6 +164,7 @@ export type Config = { perChannelMaxNoteCacheCount: number; perUserNotificationsMaxCount: number; deactivateAntennaThreshold: number; + pidFile: string; }; const _filename = fileURLToPath(import.meta.url); @@ -255,6 +257,7 @@ export function loadConfig(): Config { perChannelMaxNoteCacheCount: config.perChannelMaxNoteCacheCount ?? 1000, perUserNotificationsMaxCount: config.perUserNotificationsMaxCount ?? 300, deactivateAntennaThreshold: config.deactivateAntennaThreshold ?? (1000 * 60 * 60 * 24 * 7), + pidFile: config.pidFile, }; } From 440f3144ae46d7f22e4bd861b821d55a8709c860 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 27 Sep 2023 10:00:26 +0900 Subject: [PATCH 2/8] enhance(frontend): improve moderation log --- CHANGELOG.md | 10 ++++++++++ packages/frontend/src/pages/admin/modlog.ModLog.vue | 1 + 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26013c14df..b9b03bc266 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,16 @@ --> +## next + +### General + +### Client +- Enhance: モデレーションログ機能の強化 + +### Server +- Enhance: MasterプロセスのPIDを書き出せるように + ## 2023.9.1 ### General diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 14f94479f1..8d83b32fa1 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only : {{ log.info.role.name }} : {{ log.info.before.name }} : {{ log.info.role.name }} + : {{ log.info.emoji.name }} : {{ log.info.before.name }} : @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }} : @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }} From 9d0c0773114e851fc5d1206bd9202e5a3ab7097a Mon Sep 17 00:00:00 2001 From: Tassoman Date: Wed, 27 Sep 2023 06:48:21 +0200 Subject: [PATCH 3/8] fix: leverage join misskey multilingual behaviour (#11908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --- packages/frontend/src/components/MkVisitorDashboard.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 7a8d7e6109..e4520bbb2d 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -114,8 +114,7 @@ function showMenu(ev) { } function exploreOtherServers() { - // TODO: 言語をよしなに - window.open('https://join.misskey.page/ja-JP/instances', '_blank'); + window.open('https://join.misskey.page/instances', '_blank'); } From 055464a6241e10c8516ebee4ac9a01a2f753f8fb Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 10:02:05 +0900 Subject: [PATCH 4/8] enhance: improve moderation log --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../src/server/api/endpoints/admin/invite/create.ts | 7 +++++++ packages/backend/src/types.ts | 4 ++++ packages/misskey-js/etc/misskey-js.api.md | 5 ++++- packages/misskey-js/src/consts.ts | 4 ++++ packages/misskey-js/src/entities.ts | 3 +++ 7 files changed, 24 insertions(+), 1 deletion(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 4d8123eb5d..5473a26fca 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2277,6 +2277,7 @@ export interface Locale { "markSensitiveDriveFile": string; "unmarkSensitiveDriveFile": string; "resolveAbuseReport": string; + "createInvitation": string; }; } declare const locales: { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 647f5fb5f0..ffe1d20e10 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2190,3 +2190,4 @@ _moderationLogTypes: markSensitiveDriveFile: "ファイルをセンシティブ付与" unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" resolveAbuseReport: "通報を解決" + createInvitation: "招待コードを作成" diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts index 7112e06bdc..2cc5ab6e35 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts @@ -10,6 +10,7 @@ import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { generateInviteCode } from '@/misc/generate-invite-code.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -60,6 +61,7 @@ export default class extends Endpoint { // eslint- private inviteCodeEntityService: InviteCodeEntityService, private idService: IdService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { if (ps.expiresAt && isNaN(Date.parse(ps.expiresAt))) { @@ -78,6 +80,11 @@ export default class extends Endpoint { // eslint- } const tickets = await Promise.all(ticketsPromises); + + this.moderationLogService.log(me, 'createInvitation', { + invitations: tickets, + }); + return await this.inviteCodeEntityService.packMany(tickets, me); }); } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 35ea710f9e..7b928263ab 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -56,6 +56,7 @@ export const moderationLogTypes = [ 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', + 'createInvitation', ] as const; export type ModerationLogPayloads = { @@ -198,4 +199,7 @@ export type ModerationLogPayloads = { report: any; forwarded: boolean; }; + createInvitation: { + invitations: any[]; + }; }; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0686354ff4..7d4d4cc8b8 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2604,10 +2604,13 @@ type ModerationLog = { } | { type: 'unmarkSensitiveDriveFile'; info: ModerationLogPayloads['unmarkSensitiveDriveFile']; +} | { + type: 'createInvitation'; + info: ModerationLogPayloads['createInvitation']; }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index aedfb5570e..14a5b5643c 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -74,6 +74,7 @@ export const moderationLogTypes = [ 'markSensitiveDriveFile', 'unmarkSensitiveDriveFile', 'resolveAbuseReport', + 'createInvitation', ] as const; export type ModerationLogPayloads = { @@ -216,4 +217,7 @@ export type ModerationLogPayloads = { report: any; forwarded: boolean; }; + createInvitation: { + invitations: any[]; + }; }; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 41c9bdef6e..a089ef5a68 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -655,4 +655,7 @@ export type ModerationLog = { } | { type: 'unmarkSensitiveDriveFile'; info: ModerationLogPayloads['unmarkSensitiveDriveFile']; +} | { + type: 'createInvitation'; + info: ModerationLogPayloads['createInvitation']; }); From ce1218a2b27cb8208019ad3697f54b459fee63bd Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 11:02:01 +0900 Subject: [PATCH 5/8] =?UTF-8?q?enhance:=20=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E4=B8=80=E8=A6=A7=E3=81=A7Renote=E3=82=92=E9=99=A4?= =?UTF-8?q?=E5=A4=96=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../backend/src/server/api/endpoints/users/notes.ts | 11 +++++++++++ packages/frontend/src/pages/user/index.timeline.vue | 9 +++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9b03bc266..3e943714cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ## next ### General +- Enhance: ユーザーページのノート一覧でRenoteを除外できるように ### Client - Enhance: モデレーションログ機能の強化 diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 5934baef47..982fcc858a 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -42,6 +42,7 @@ export const paramDef = { properties: { userId: { type: 'string', format: 'misskey:id' }, includeReplies: { type: 'boolean', default: true }, + includeRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -118,6 +119,16 @@ export default class extends Endpoint { // eslint- query.andWhere('note.replyId IS NULL'); } + if (ps.includeRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { qb.orWhere('note.userId != :userId', { userId: user.id }); diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue index 3a2a2ade81..fcb2b41651 100644 --- a/packages/frontend/src/pages/user/index.timeline.vue +++ b/packages/frontend/src/pages/user/index.timeline.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -36,7 +36,8 @@ const pagination = { limit: 10, params: computed(() => ({ userId: props.user.id, - includeReplies: include.value === 'replies' || include.value === 'files', + includeRenotes: include.value === 'all', + includeReplies: include.value === 'all' || include.value === 'files', withFiles: include.value === 'files', })), }; @@ -51,7 +52,7 @@ const pagination = { .tl { background: var(--bg); - border-radius: var(--radius); - overflow: clip; + border-radius: var(--radius); + overflow: clip; } From d854942a1f4d28613f08daa63b086ca58522854d Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 11:04:14 +0900 Subject: [PATCH 6/8] .js --- packages/frontend/src/components/global/MkPageHeader.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index ef8bfbbbfc..580816abaa 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -45,7 +45,7 @@ import { onMounted, onUnmounted, ref, inject } from 'vue'; import tinycolor from 'tinycolor2'; import XTabs, { Tab } from './MkPageHeader.tabs.vue'; import { scrollToTop } from '@/scripts/scroll.js'; -import { globalEvents } from '@/events'; +import { globalEvents } from '@/events.js'; import { injectPageMetadata } from '@/scripts/page-metadata.js'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js'; From eb740e2c72ae6854b244ad099c927c069008720e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 11:41:41 +0900 Subject: [PATCH 7/8] =?UTF-8?q?enhance:=20=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=8B=E3=82=89Renote=E3=82=92?= =?UTF-8?q?=E9=99=A4=E5=A4=96=E3=81=99=E3=82=8B=E3=82=AA=E3=83=97=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../api/endpoints/notes/user-list-timeline.ts | 16 ++++++++ .../src/server/api/endpoints/users/notes.ts | 8 ++-- .../api/stream/channels/global-timeline.ts | 6 ++- .../api/stream/channels/home-timeline.ts | 6 ++- .../api/stream/channels/hybrid-timeline.ts | 6 ++- .../api/stream/channels/local-timeline.ts | 6 ++- .../frontend/src/components/MkTimeline.vue | 37 ++++++++++++++----- .../frontend/src/pages/settings/general.vue | 2 - packages/frontend/src/pages/timeline.vue | 24 +++++++++++- .../src/pages/user/index.timeline.vue | 4 +- packages/frontend/src/store.ts | 4 -- 14 files changed, 94 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e943714cc..33dfa28d02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ## next ### General +- Enhance: タイムラインからRenoteを除外するオプションを追加 - Enhance: ユーザーページのノート一覧でRenoteを除外できるように ### Client diff --git a/locales/index.d.ts b/locales/index.d.ts index 5473a26fca..eb2793c710 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1124,6 +1124,7 @@ export interface Locale { "authentication": string; "authenticationRequiredToContinue": string; "dateAndTime": string; + "showRenotes": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ffe1d20e10..637d580d6a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1121,6 +1121,7 @@ unnotifyNotes: "投稿の通知を解除" authentication: "認証" authenticationRequiredToContinue: "続けるには認証を行ってください" dateAndTime: "日時" +showRenotes: "リノートを表示" _announcement: forExistingUsers: "既存ユーザーのみ" diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 6932073791..c20274b2ba 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -49,6 +49,8 @@ export const paramDef = { includeMyRenotes: { type: 'boolean', default: true }, includeRenotedMyNotes: { type: 'boolean', default: true }, includeLocalRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false, @@ -130,6 +132,20 @@ export default class extends Endpoint { // eslint- })); } + if (!ps.withReplies) { + query.andWhere('note.replyId IS NULL'); + } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 982fcc858a..e660a0bb25 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -41,8 +41,8 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, - includeReplies: { type: 'boolean', default: true }, - includeRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -115,11 +115,11 @@ export default class extends Endpoint { // eslint- } } - if (!ps.includeReplies) { + if (!ps.withReplies) { query.andWhere('note.replyId IS NULL'); } - if (ps.includeRenotes === false) { + if (ps.withRenotes === false) { query.andWhere(new Brackets(qb => { qb.orWhere('note.renoteId IS NULL'); qb.orWhere(new Brackets(qb => { 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 a33f1a956a..fef52b6856 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -19,6 +19,7 @@ class GlobalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -37,7 +38,8 @@ class GlobalTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.gtlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -68,6 +70,8 @@ class GlobalTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; 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 bd8888f679..198c68e1c2 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -17,6 +17,7 @@ class HomeTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withRenotes: boolean; constructor( private noteEntityService: NoteEntityService, @@ -30,7 +31,8 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; this.subscriber.on('notesStream', this.onNote); } @@ -77,6 +79,8 @@ class HomeTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する 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 760fb8d19f..cde4297478 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -37,7 +38,8 @@ class HybridTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -89,6 +91,8 @@ class HybridTimelineChannel extends Channel { if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する 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 f32f8c5cec..ef708c4fee 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withRenotes: boolean; constructor( private metaService: MetaService, @@ -36,7 +37,8 @@ class LocalTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withReplies = params.withReplies as boolean; + this.withReplies = params.withReplies ?? false; + this.withRenotes = params.withRenotes ?? true; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -68,6 +70,8 @@ class LocalTimelineChannel extends Channel { if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return; } + if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return; + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.userIdsWhoMeMuting)) return; // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index d6712e7606..3e7c537512 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -15,14 +15,19 @@ import * as sound from '@/scripts/sound.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ src: string; list?: string; antenna?: string; channel?: string; role?: string; sound?: boolean; -}>(); + withRenotes?: boolean; + withReplies?: boolean; +}>(), { + withRenotes: true, + withReplies: false, +}); const emit = defineEmits<{ (ev: 'note'): void; @@ -62,10 +67,12 @@ if (props.src === 'antenna') { } else if (props.src === 'home') { endpoint = 'notes/timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('homeTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); @@ -73,28 +80,34 @@ if (props.src === 'antenna') { } else if (props.src === 'local') { endpoint = 'notes/local-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('localTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'social') { endpoint = 'notes/hybrid-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('hybridTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'global') { endpoint = 'notes/global-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('globalTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'mentions') { @@ -116,9 +129,13 @@ if (props.src === 'antenna') { } else if (props.src === 'list') { endpoint = 'notes/user-list-timeline'; query = { + withRenotes: props.withRenotes, + withReplies: props.withReplies, listId: props.list, }; connection = stream.useChannel('userList', { + withRenotes: props.withRenotes, + withReplies: props.withReplies, listId: props.list, }); connection.on('note', prepend); diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index a536bd1baa..55de53fb07 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.showFixedPostForm }} {{ i18n.ts.showFixedPostFormInChannel }} - {{ i18n.ts.flagShowTimelineReplies }} @@ -249,7 +248,6 @@ const squareAvatars = computed(defaultStore.makeGetterSetter('squareAvatars')); const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('mediaListWithOneImageAppearance')); const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition')); const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); -const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies')); const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); watch(lang, () => { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index cce7360b9b..5fc43dc650 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -15,9 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -58,6 +60,8 @@ const rootEl = $shallowRef(); let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); +const withRenotes = $ref(true); +const withReplies = $ref(false); watch($$(src), () => queue = 0); @@ -129,7 +133,23 @@ function focus(): void { tlComponent.focus(); } -const headerActions = $computed(() => []); +const headerActions = $computed(() => [{ + icon: 'ti ti-dots', + text: i18n.ts.options, + handler: (ev) => { + os.popupMenu([{ + type: 'switch', + text: i18n.ts.showRenotes, + icon: 'ti ti-repeat', + ref: $$(withRenotes), + }, { + type: 'switch', + text: i18n.ts.withReplies, + icon: 'ti ti-arrow-back-up', + ref: $$(withReplies), + }], ev.currentTarget ?? ev.target); + }, +}]); const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({ key: 'list:' + l.id, diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue index fcb2b41651..42040f5304 100644 --- a/packages/frontend/src/pages/user/index.timeline.vue +++ b/packages/frontend/src/pages/user/index.timeline.vue @@ -36,8 +36,8 @@ const pagination = { limit: 10, params: computed(() => ({ userId: props.user.id, - includeRenotes: include.value === 'all', - includeReplies: include.value === 'all' || include.value === 'files', + withRenotes: include.value === 'all', + withReplies: include.value === 'all' || include.value === 'files', withFiles: include.value === 'files', })), }; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 8a7ee62eff..e715088d03 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -109,10 +109,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: [] as string[], }, - showTimelineReplies: { - where: 'account', - default: false, - }, menu: { where: 'deviceAccount', From 772d2432b6e84a7a7c0fa8ad1852701cdc600f88 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 15:32:47 +0900 Subject: [PATCH 8/8] =?UTF-8?q?enhance:=20=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=8B=E3=82=89Renote=E3=82=92?= =?UTF-8?q?=E9=99=A4=E5=A4=96=E3=81=99=E3=82=8B=E3=82=AA=E3=83=97=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/endpoints/notes/global-timeline.ts | 12 +++++++ .../api/endpoints/notes/hybrid-timeline.ts | 11 +++++++ .../api/endpoints/notes/local-timeline.ts | 11 +++++++ .../server/api/endpoints/notes/timeline.ts | 11 +++++++ packages/frontend/src/ui/deck/deck-store.ts | 2 ++ packages/frontend/src/ui/deck/tl-column.vue | 33 +++++++++++++++++-- 6 files changed, 78 insertions(+), 2 deletions(-) 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 0b3b5c902e..8784e86153 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -4,6 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; import type { NotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; @@ -40,6 +41,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -88,6 +90,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); 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 e9ae5dc755..9bde5dee21 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -52,6 +52,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, }, required: [], } as const; @@ -137,6 +138,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); 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 af1e0398dc..0fefddc51b 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -42,6 +42,7 @@ export const paramDef = { properties: { withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, fileType: { type: 'array', items: { type: 'string', } }, @@ -110,6 +111,16 @@ export default class extends Endpoint { // eslint- query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); } } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 042115ab84..0d47cc1702 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -42,6 +42,7 @@ export const paramDef = { includeLocalRenotes: { type: 'boolean', default: true }, withFiles: { type: 'boolean', default: false }, withReplies: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, }, required: [], } as const; @@ -126,6 +127,16 @@ export default class extends Endpoint { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index f910c5181d..034d675b0e 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -30,6 +30,8 @@ export type Column = { roleId?: string; includingTypes?: typeof notificationTypes[number][]; tl?: 'home' | 'local' | 'social' | 'global'; + withRenotes?: boolean; + withReplies?: boolean; }; export const deckStore = markRaw(new Storage('deck', { diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 813b801d21..073898409c 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -20,12 +20,19 @@ SPDX-License-Identifier: AGPL-3.0-only

{{ i18n.ts._disabledTimeline.description }}

- +