diff --git a/CHANGELOG.md b/CHANGELOG.md index b8974a23f5..c7e3841743 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### General - Feat: コンテンツの表示にログインを必須にできるように - Feat: 過去のノートを非公開化/フォロワーのみ表示可能にできるように +- Fix: お知らせ作成時に画像URL入力欄を空欄に変更できないのを修正 ( #14976 ) - Enhance: 依存関係の更新 - Enhance: l10nの更新 @@ -28,6 +29,7 @@ - Enhance: 過去に送信したフォローリクエストを確認できるように (Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/663) - Enhance: サイドバーを簡単に展開・折りたたみできるように ( #14981 ) +- Enhance: リノートメニューに「リノートの詳細」を追加 - Enhance: Blueskyの投稿埋め込みプレビューに対応 - Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正 - Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正 @@ -40,6 +42,8 @@ - Fix: メールアドレス登録有効化時の「完了」ダイアログボックスの表示条件を修正 - Fix: 画面幅が狭い環境でデザインが崩れる問題を修正 (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/815) +- Fix: TypeScriptの型チェック対象ファイルを限定してビルドを高速化するように + (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/725) ### Server - Enhance: DockerのNode.jsを22.11.0に更新 diff --git a/locales/index.d.ts b/locales/index.d.ts index 98253c5d6d..34c3f2c819 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2362,6 +2362,10 @@ export interface Locale extends ILocale { * 詳細 */ "details": string; + /** + * リノートの詳細 + */ + "renoteDetails": string; /** * 絵文字を選択 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 2d48e4d61e..f4672a5ea6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -586,6 +586,7 @@ masterVolume: "マスター音量" notUseSound: "サウンドを出力しない" useSoundOnlyWhenActive: "Misskeyがアクティブな時のみサウンドを出力する" details: "詳細" +renoteDetails: "リノートの詳細" chooseEmoji: "絵文字を選択" unableToProcess: "操作を完了できません" recentUsed: "最近使用" diff --git a/packages/backend/src/core/AbuseReportNotificationService.ts b/packages/backend/src/core/AbuseReportNotificationService.ts index 25e265f2b1..742e2621fd 100644 --- a/packages/backend/src/core/AbuseReportNotificationService.ts +++ b/packages/backend/src/core/AbuseReportNotificationService.ts @@ -154,9 +154,9 @@ export class AbuseReportNotificationService implements OnApplicationShutdown { const convertedReports = abuseReports.map(it => { return { ...it, - reporter: usersMap.get(it.reporterId), - targetUser: usersMap.get(it.targetUserId), - assignee: it.assigneeId ? usersMap.get(it.assigneeId) : null, + reporter: usersMap.get(it.reporterId) ?? null, + targetUser: usersMap.get(it.targetUserId) ?? null, + assignee: it.assigneeId ? (usersMap.get(it.assigneeId) ?? null) : null, }; }); diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts index d4fcf19439..a9f6731977 100644 --- a/packages/backend/src/core/AnnouncementService.ts +++ b/packages/backend/src/core/AnnouncementService.ts @@ -72,7 +72,7 @@ export class AnnouncementService { updatedAt: null, title: values.title, text: values.text, - imageUrl: values.imageUrl, + imageUrl: values.imageUrl || null, icon: values.icon, display: values.display, forExistingUsers: values.forExistingUsers, diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index 50f08da241..da76dd1284 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -7,13 +7,15 @@ import { randomUUID } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import type { IActivity } from '@/core/activitypub/type.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; -import type { MiWebhook, WebhookEventTypes, webhookEventTypes } from '@/models/Webhook.js'; +import type { MiWebhook, WebhookEventTypes } from '@/models/Webhook.js'; import type { MiSystemWebhook, SystemWebhookEventType } from '@/models/SystemWebhook.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js'; +import { type SystemWebhookPayload } from '@/core/SystemWebhookService.js'; +import { type UserWebhookPayload } from './UserWebhookService.js'; import type { DbJobData, DeliverJobData, @@ -30,12 +32,11 @@ import type { ObjectStorageQueue, RelationshipQueue, SystemQueue, - UserWebhookDeliverQueue, SystemWebhookDeliverQueue, + UserWebhookDeliverQueue, } from './QueueModule.js'; import type httpSignature from '@peertube/http-signature'; import type * as Bull from 'bullmq'; -import { type UserWebhookPayload } from './UserWebhookService.js'; @Injectable() export class QueueService { @@ -501,10 +502,10 @@ export class QueueService { * @see SystemWebhookDeliverProcessorService */ @bindThis - public systemWebhookDeliver( + public systemWebhookDeliver( webhook: MiSystemWebhook, - type: SystemWebhookEventType, - content: unknown, + type: T, + content: SystemWebhookPayload, opts?: { attempts?: number }, ) { const data: SystemWebhookDeliverJobData = { diff --git a/packages/backend/src/core/SystemWebhookService.ts b/packages/backend/src/core/SystemWebhookService.ts index db6407dcb3..de00169612 100644 --- a/packages/backend/src/core/SystemWebhookService.ts +++ b/packages/backend/src/core/SystemWebhookService.ts @@ -15,8 +15,39 @@ import { QueueService } from '@/core/QueueService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { LoggerService } from '@/core/LoggerService.js'; import Logger from '@/logger.js'; +import { Packed } from '@/misc/json-schema.js'; +import { AbuseReportResolveType } from '@/models/AbuseUserReport.js'; +import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModeratorsActivityProcessorService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +export type AbuseReportPayload = { + id: string; + targetUserId: string; + targetUser: Packed<'UserLite'> | null; + targetUserHost: string | null; + reporterId: string; + reporter: Packed<'UserLite'> | null; + reporterHost: string | null; + assigneeId: string | null; + assignee: Packed<'UserLite'> | null; + resolved: boolean; + forwarded: boolean; + comment: string; + moderationNote: string; + resolvedAs: AbuseReportResolveType | null; +}; + +export type InactiveModeratorsWarningPayload = { + remainingTime: ModeratorInactivityRemainingTime; +}; + +export type SystemWebhookPayload = + T extends 'abuseReport' | 'abuseReportResolved' ? AbuseReportPayload : + T extends 'userCreated' ? Packed<'UserLite'> : + T extends 'inactiveModeratorsWarning' ? InactiveModeratorsWarningPayload : + T extends 'inactiveModeratorsInvitationOnlyChanged' ? Record : + never; + @Injectable() export class SystemWebhookService implements OnApplicationShutdown { private logger: Logger; @@ -168,7 +199,7 @@ export class SystemWebhookService implements OnApplicationShutdown { public async enqueueSystemWebhook( webhook: MiSystemWebhook | MiSystemWebhook['id'], type: T, - content: unknown, + content: SystemWebhookPayload, ) { const webhookEntity = typeof webhook === 'string' ? (await this.fetchActiveSystemWebhooks()).find(a => a.id === webhook) diff --git a/packages/backend/src/core/WebhookTestService.ts b/packages/backend/src/core/WebhookTestService.ts index b1ea7974fb..555a39f71c 100644 --- a/packages/backend/src/core/WebhookTestService.ts +++ b/packages/backend/src/core/WebhookTestService.ts @@ -7,7 +7,7 @@ import { Injectable } from '@nestjs/common'; import { MiAbuseUserReport, MiNote, MiUser, MiWebhook } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWebhook.js'; -import { SystemWebhookService } from '@/core/SystemWebhookService.js'; +import { AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js'; import { Packed } from '@/misc/json-schema.js'; import { type WebhookEventTypes } from '@/models/Webhook.js'; import { type UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js'; @@ -16,13 +16,7 @@ import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModera const oneDayMillis = 24 * 60 * 60 * 1000; -type AbuseUserReportDto = Omit & { - targetUser: Packed<'UserLite'> | null, - reporter: Packed<'UserLite'> | null, - assignee: Packed<'UserLite'> | null, -}; - -function generateAbuseReport(override?: Partial): AbuseUserReportDto { +function generateAbuseReport(override?: Partial): AbuseReportPayload { const result: MiAbuseUserReport = { id: 'dummy-abuse-report1', targetUserId: 'dummy-target-user', @@ -389,7 +383,8 @@ export class WebhookTestService { break; } // まだ実装されていない (#9485) - case 'reaction': return; + case 'reaction': + return; default: { // eslint-disable-next-line @typescript-eslint/no-unused-vars const _exhaustiveAssertion: never = params.type; @@ -407,10 +402,10 @@ export class WebhookTestService { * - 送信対象イベント(on)に関する設定 */ @bindThis - public async testSystemWebhook( + public async testSystemWebhook( params: { webhookId: MiSystemWebhook['id'], - type: SystemWebhookEventType, + type: T, override?: Partial>, }, ) { @@ -420,7 +415,7 @@ export class WebhookTestService { } const webhook = webhooks[0]; - const send = (contents: unknown) => { + const send = (type: U, contents: SystemWebhookPayload) => { const merged = { ...webhook, ...params.override, @@ -428,12 +423,12 @@ export class WebhookTestService { // テスト目的なのでSystemWebhookServiceの機能を経由せず直接キューに追加する(チェック処理などをスキップする意図). // また、Jobの試行回数も1回だけ. - this.queueService.systemWebhookDeliver(merged, params.type, contents, { attempts: 1 }); + this.queueService.systemWebhookDeliver(merged, type, contents, { attempts: 1 }); }; switch (params.type) { case 'abuseReport': { - send(generateAbuseReport({ + send('abuseReport', generateAbuseReport({ targetUserId: dummyUser1.id, targetUser: dummyUser1, reporterId: dummyUser2.id, @@ -442,7 +437,7 @@ export class WebhookTestService { break; } case 'abuseReportResolved': { - send(generateAbuseReport({ + send('abuseReportResolved', generateAbuseReport({ targetUserId: dummyUser1.id, targetUser: dummyUser1, reporterId: dummyUser2.id, @@ -454,7 +449,7 @@ export class WebhookTestService { break; } case 'userCreated': { - send(toPackedUserLite(dummyUser1)); + send('userCreated', toPackedUserLite(dummyUser1)); break; } case 'inactiveModeratorsWarning': { @@ -464,15 +459,20 @@ export class WebhookTestService { asHours: 24, }; - send({ + send('inactiveModeratorsWarning', { remainingTime: dummyTime, }); break; } case 'inactiveModeratorsInvitationOnlyChanged': { - send({}); + send('inactiveModeratorsInvitationOnlyChanged', {}); break; } + default: { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _exhaustiveAssertion: never = params.type; + return; + } } } } diff --git a/packages/backend/src/models/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts index cb5672e4ac..d43ebf9342 100644 --- a/packages/backend/src/models/AbuseUserReport.ts +++ b/packages/backend/src/models/AbuseUserReport.ts @@ -7,6 +7,8 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ import { id } from './util/id.js'; import { MiUser } from './User.js'; +export type AbuseReportResolveType = 'accept' | 'reject'; + @Entity('abuse_user_report') export class MiAbuseUserReport { @PrimaryColumn(id()) @@ -76,7 +78,7 @@ export class MiAbuseUserReport { @Column('varchar', { length: 128, nullable: true, }) - public resolvedAs: 'accept' | 'reject' | null; + public resolvedAs: AbuseReportResolveType | null; //#region Denormalized fields @Index() diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 2dae1df87d..b8bfda73a4 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -55,7 +55,7 @@ export const paramDef = { properties: { title: { type: 'string', minLength: 1 }, text: { type: 'string', minLength: 1 }, - imageUrl: { type: 'string', nullable: true, minLength: 1 }, + imageUrl: { type: 'string', nullable: true, minLength: 0 }, icon: { type: 'string', enum: ['info', 'warning', 'error', 'success'], default: 'info' }, display: { type: 'string', enum: ['normal', 'banner', 'dialog'], default: 'normal' }, forExistingUsers: { type: 'boolean', default: false }, @@ -76,7 +76,8 @@ export default class extends Endpoint { // eslint- updatedAt: null, title: ps.title, text: ps.text, - imageUrl: ps.imageUrl, + /* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */ + imageUrl: ps.imageUrl || null, icon: ps.icon, display: ps.display, forExistingUsers: ps.forExistingUsers, diff --git a/packages/frontend-embed/tsconfig.json b/packages/frontend-embed/tsconfig.json index 3701343623..45f933dc28 100644 --- a/packages/frontend-embed/tsconfig.json +++ b/packages/frontend-embed/tsconfig.json @@ -33,7 +33,7 @@ "./node_modules" ], "types": [ - "vite/client", + "vite/client" ], "lib": [ "esnext", @@ -44,8 +44,9 @@ }, "compileOnSave": false, "include": [ - "./**/*.ts", - "./**/*.vue" + "./src/**/*.ts", + "./src/**/*.vue", + "./@types/**/*.ts" ], "exclude": [ ".storybook/**/*" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index cf0d0787b1..1a8814b7cb 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -187,6 +187,7 @@ import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkInstanceTicker from '@/components/MkInstanceTicker.vue'; import { pleaseLogin, type OpenOnRemoteOptions } from '@/scripts/please-login.js'; import { checkWordMute } from '@/scripts/check-word-mute.js'; +import { notePage } from '@/filters/note.js'; import { userPage } from '@/filters/user.js'; import number from '@/filters/number.js'; import * as os from '@/os.js'; @@ -566,15 +567,24 @@ function showRenoteMenu(): void { }; } + const renoteDetailsMenu: MenuItem = { + type: 'link', + text: i18n.ts.renoteDetails, + icon: 'ti ti-info-circle', + to: notePage(note.value), + }; + if (isMyRenote) { pleaseLogin({ openOnRemote: pleaseLoginContext.value }); os.popupMenu([ + renoteDetailsMenu, getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), { type: 'divider' }, getUnrenote(), ], renoteTime.value); } else { os.popupMenu([ + renoteDetailsMenu, getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), { type: 'divider' }, getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote), diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json index b88773b598..4e5ca7f559 100644 --- a/packages/frontend/tsconfig.json +++ b/packages/frontend/tsconfig.json @@ -45,8 +45,11 @@ }, "compileOnSave": false, "include": [ - "./**/*.ts", - "./**/*.vue" + "./src/**/*.ts", + "./src/**/*.vue", + "./test/**/*.ts", + "./test/**/*.vue", + "./@types/**/*.ts" ], "exclude": [ ".storybook/**/*"