From ed0cc443ea8122d01488f427bcced457d3d65d4f Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 21 Nov 2023 09:55:49 +0900 Subject: [PATCH 01/41] =?UTF-8?q?fix(backend):=20=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=81=8C=E4=BF=9D=E5=AD=98=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- packages/backend/src/core/RoleService.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2c226aec0..aff2ed283e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ - ### Server -- +- Fix: ロールタイムラインが保存されない問題を修正 ## 2023.11.1 diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index d6a414694a..432887b3b7 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -87,6 +87,9 @@ export class RoleService implements OnApplicationShutdown { @Inject(DI.redis) private redisClient: Redis.Redis, + @Inject(DI.redisForTimelines) + private redisForTimelines: Redis.Redis, + @Inject(DI.redisForSub) private redisForSub: Redis.Redis, @@ -476,7 +479,7 @@ export class RoleService implements OnApplicationShutdown { public async addNoteToRoleTimeline(note: Packed<'Note'>): Promise { const roles = await this.getUserRoles(note.userId); - const redisPipeline = this.redisClient.pipeline(); + const redisPipeline = this.redisForTimelines.pipeline(); for (const role of roles) { this.funoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline); From 2ec3227012eecb4358feed6d16bf2b700b3ac9c7 Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Tue, 21 Nov 2023 10:48:01 +0900 Subject: [PATCH 02/41] update api.md (#12379) for API changes in b65fd349812fe3c89b5face6ec5c12823459d7df --- packages/misskey-js/etc/misskey-js.api.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 85907de665..63c3cb71a5 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2685,10 +2685,16 @@ type ModerationLog = { } | { type: 'resolveAbuseReport'; info: ModerationLogPayloads['resolveAbuseReport']; +} | { + type: 'unsetUserAvatar'; + info: ModerationLogPayloads['unsetUserAvatar']; +} | { + type: 'unsetUserBanner'; + info: ModerationLogPayloads['unsetUserBanner']; }); // @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", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration"]; +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", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; @@ -3046,8 +3052,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // Warnings were encountered during analysis: // // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts -// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts -// src/api.types.ts:632:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/api.types.ts:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts +// src/api.types.ts:634:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts // src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts From 8bd9077f77eaebaa4dff403e072ba49e4cab1326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Tue, 21 Nov 2023 11:13:56 +0900 Subject: [PATCH 03/41] =?UTF-8?q?json-schema=E9=85=8D=E4=B8=8B=E3=81=AE?= =?UTF-8?q?=E6=9C=80=E6=96=B0=E5=8C=96=20(#12312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * user.ts、page.ts、drive-folder.tsを各EntityServiceの戻り値をもとに最新化 * 再確認 * fix error * note以外の残りのファイルを対応 * fix CHANGELOG.md * fix CHANGELOG.md * fix user.ts * fix user.ts * コメント対応 * fix note.ts --------- Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com> --- CHANGELOG.md | 1 + .../entities/NotificationEntityService.ts | 14 +- .../src/models/json-schema/announcement.ts | 8 +- .../backend/src/models/json-schema/channel.ts | 63 ++-- .../backend/src/models/json-schema/clip.ts | 8 +- .../src/models/json-schema/drive-file.ts | 2 +- .../src/models/json-schema/drive-folder.ts | 12 +- .../models/json-schema/federation-instance.ts | 9 +- .../backend/src/models/json-schema/flash.ts | 20 +- .../src/models/json-schema/following.ts | 10 +- .../src/models/json-schema/gallery-post.ts | 24 +- .../backend/src/models/json-schema/note.ts | 28 +- .../src/models/json-schema/notification.ts | 26 +- .../backend/src/models/json-schema/page.ts | 68 +++- .../backend/src/models/json-schema/user.ts | 313 ++++++++++++++++-- 15 files changed, 457 insertions(+), 149 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc38356284..2efa1b230d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - Feat: 管理者がコントロールパネルからメールアドレスの照会を行えるようになりました - Enhance: ローカリゼーションの更新 - Enhance: 依存関係の更新 +- Enhance: json-schema(OpenAPIの戻り値として使用されるスキーマ定義)を出来る限り最新化 #12311 ### Client - Enhance: MFMでルビを振れるように diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index f74594ff0c..e723ea5a55 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -198,12 +198,14 @@ export class NotificationEntityService implements OnModuleInit { }); } else if (notification.type === 'renote:grouped') { const users = await Promise.all(notification.userIds.map(userId => { - const user = hint?.packedUsers != null - ? hint.packedUsers.get(userId) - : this.userEntityService.pack(userId!, { id: meId }, { - detail: false, - }); - return user; + const packedUser = hint?.packedUsers != null ? hint.packedUsers.get(userId) : null; + if (packedUser) { + return packedUser; + } + + return this.userEntityService.pack(userId, { id: meId }, { + detail: false, + }); })); return await awaitAll({ id: notification.id, diff --git a/packages/backend/src/models/json-schema/announcement.ts b/packages/backend/src/models/json-schema/announcement.ts index c7e24c7f29..78a98872b2 100644 --- a/packages/backend/src/models/json-schema/announcement.ts +++ b/packages/backend/src/models/json-schema/announcement.ts @@ -42,11 +42,15 @@ export const packedAnnouncementSchema = { type: 'string', optional: false, nullable: false, }, - forYou: { + needConfirmationToRead: { type: 'boolean', optional: false, nullable: false, }, - needConfirmationToRead: { + silence: { + type: 'boolean', + optional: false, nullable: false, + }, + forYou: { type: 'boolean', optional: false, nullable: false, }, diff --git a/packages/backend/src/models/json-schema/channel.ts b/packages/backend/src/models/json-schema/channel.ts index 8f9770cdc5..5b0fa0f15d 100644 --- a/packages/backend/src/models/json-schema/channel.ts +++ b/packages/backend/src/models/json-schema/channel.ts @@ -19,7 +19,7 @@ export const packedChannelSchema = { }, lastNotedAt: { type: 'string', - optional: false, nullable: true, + nullable: true, optional: false, format: 'date-time', }, name: { @@ -28,38 +28,18 @@ export const packedChannelSchema = { }, description: { type: 'string', - nullable: true, optional: false, - }, - bannerUrl: { - type: 'string', - format: 'url', - nullable: true, optional: false, - }, - isArchived: { - type: 'boolean', - optional: false, nullable: false, - }, - notesCount: { - type: 'number', - nullable: false, optional: false, - }, - usersCount: { - type: 'number', - nullable: false, optional: false, - }, - isFollowing: { - type: 'boolean', - optional: true, nullable: false, - }, - isFavorited: { - type: 'boolean', - optional: true, nullable: false, + optional: false, nullable: true, }, userId: { type: 'string', nullable: true, optional: false, format: 'id', }, + bannerUrl: { + type: 'string', + format: 'url', + nullable: true, optional: false, + }, pinnedNoteIds: { type: 'array', nullable: false, optional: false, @@ -72,6 +52,18 @@ export const packedChannelSchema = { type: 'string', optional: false, nullable: false, }, + isArchived: { + type: 'boolean', + optional: false, nullable: false, + }, + usersCount: { + type: 'number', + nullable: false, optional: false, + }, + notesCount: { + type: 'number', + nullable: false, optional: false, + }, isSensitive: { type: 'boolean', optional: false, nullable: false, @@ -80,5 +72,22 @@ export const packedChannelSchema = { type: 'boolean', optional: false, nullable: false, }, + isFollowing: { + type: 'boolean', + optional: true, nullable: false, + }, + isFavorited: { + type: 'boolean', + optional: true, nullable: false, + }, + pinnedNotes: { + type: 'array', + optional: true, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/clip.ts b/packages/backend/src/models/json-schema/clip.ts index 64f7a2ad9c..1ab96c2b3b 100644 --- a/packages/backend/src/models/json-schema/clip.ts +++ b/packages/backend/src/models/json-schema/clip.ts @@ -44,13 +44,13 @@ export const packedClipSchema = { type: 'boolean', optional: false, nullable: false, }, - isFavorited: { - type: 'boolean', - optional: true, nullable: false, - }, favoritedCount: { type: 'number', optional: false, nullable: false, }, + isFavorited: { + type: 'boolean', + optional: true, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/drive-file.ts b/packages/backend/src/models/json-schema/drive-file.ts index 87f1340812..79f242a711 100644 --- a/packages/backend/src/models/json-schema/drive-file.ts +++ b/packages/backend/src/models/json-schema/drive-file.ts @@ -74,7 +74,7 @@ export const packedDriveFileSchema = { }, url: { type: 'string', - optional: false, nullable: true, + optional: false, nullable: false, format: 'url', }, thumbnailUrl: { diff --git a/packages/backend/src/models/json-schema/drive-folder.ts b/packages/backend/src/models/json-schema/drive-folder.ts index 51107d423f..aaad301303 100644 --- a/packages/backend/src/models/json-schema/drive-folder.ts +++ b/packages/backend/src/models/json-schema/drive-folder.ts @@ -21,6 +21,12 @@ export const packedDriveFolderSchema = { type: 'string', optional: false, nullable: false, }, + parentId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + example: 'xxxxxxxxxx', + }, foldersCount: { type: 'number', optional: true, nullable: false, @@ -29,12 +35,6 @@ export const packedDriveFolderSchema = { type: 'number', optional: true, nullable: false, }, - parentId: { - type: 'string', - optional: false, nullable: true, - format: 'id', - example: 'xxxxxxxxxx', - }, parent: { type: 'object', optional: true, nullable: true, diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index 442e1076f2..3417314272 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -79,6 +79,10 @@ export const packedFederationInstanceSchema = { type: 'string', optional: false, nullable: true, }, + isSilenced: { + type: 'boolean', + optional: false, nullable: false, + }, iconUrl: { type: 'string', optional: false, nullable: true, @@ -93,11 +97,6 @@ export const packedFederationInstanceSchema = { type: 'string', optional: false, nullable: true, }, - isSilenced: { - type: "boolean", - optional: false, - nullable: false, - }, infoUpdatedAt: { type: 'string', optional: false, nullable: true, diff --git a/packages/backend/src/models/json-schema/flash.ts b/packages/backend/src/models/json-schema/flash.ts index 9453ba1dce..f08fa7a279 100644 --- a/packages/backend/src/models/json-schema/flash.ts +++ b/packages/backend/src/models/json-schema/flash.ts @@ -22,6 +22,16 @@ export const packedFlashSchema = { optional: false, nullable: false, format: 'date-time', }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, title: { type: 'string', optional: false, nullable: false, @@ -34,16 +44,6 @@ export const packedFlashSchema = { type: 'string', optional: false, nullable: false, }, - userId: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - user: { - type: 'object', - ref: 'UserLite', - optional: false, nullable: false, - }, likedCount: { type: 'number', optional: false, nullable: true, diff --git a/packages/backend/src/models/json-schema/following.ts b/packages/backend/src/models/json-schema/following.ts index 3a24ebb619..e92cff20a1 100644 --- a/packages/backend/src/models/json-schema/following.ts +++ b/packages/backend/src/models/json-schema/following.ts @@ -22,16 +22,16 @@ export const packedFollowingSchema = { optional: false, nullable: false, format: 'id', }, - followee: { - type: 'object', - optional: true, nullable: false, - ref: 'UserDetailed', - }, followerId: { type: 'string', optional: false, nullable: false, format: 'id', }, + followee: { + type: 'object', + optional: true, nullable: false, + ref: 'UserDetailed', + }, follower: { type: 'object', optional: true, nullable: false, diff --git a/packages/backend/src/models/json-schema/gallery-post.ts b/packages/backend/src/models/json-schema/gallery-post.ts index cf260c0bf5..df7038950c 100644 --- a/packages/backend/src/models/json-schema/gallery-post.ts +++ b/packages/backend/src/models/json-schema/gallery-post.ts @@ -22,14 +22,6 @@ export const packedGalleryPostSchema = { optional: false, nullable: false, format: 'date-time', }, - title: { - type: 'string', - optional: false, nullable: false, - }, - description: { - type: 'string', - optional: false, nullable: true, - }, userId: { type: 'string', optional: false, nullable: false, @@ -40,6 +32,14 @@ export const packedGalleryPostSchema = { ref: 'UserLite', optional: false, nullable: false, }, + title: { + type: 'string', + optional: false, nullable: false, + }, + description: { + type: 'string', + optional: false, nullable: true, + }, fileIds: { type: 'array', optional: true, nullable: false, @@ -70,5 +70,13 @@ export const packedGalleryPostSchema = { type: 'boolean', optional: false, nullable: false, }, + likedCount: { + type: 'number', + optional: false, nullable: false, + }, + isLiked: { + type: 'boolean', + optional: true, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 38c0054b55..9d5d558f51 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -127,22 +127,18 @@ export const packedNoteSchema = { channel: { type: 'object', optional: true, nullable: true, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - }, - name: { - type: 'string', - optional: false, nullable: true, - }, - isSensitive: { - type: 'boolean', - optional: true, nullable: false, - }, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + }, + name: { + type: 'string', + optional: false, nullable: true, + }, + isSensitive: { + type: 'boolean', + optional: true, nullable: false, }, }, }, diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index 27db3bb62c..c6d6e84317 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -42,13 +42,9 @@ export const packedNotificationSchema = { type: 'string', optional: true, nullable: true, }, - choice: { - type: 'number', - optional: true, nullable: true, - }, - invitation: { - type: 'object', - optional: true, nullable: true, + achievement: { + type: 'string', + optional: true, nullable: false, }, body: { type: 'string', @@ -81,14 +77,14 @@ export const packedNotificationSchema = { required: ['user', 'reaction'], }, }, - }, - users: { - type: 'array', - optional: true, nullable: true, - items: { - type: 'object', - ref: 'UserLite', - optional: false, nullable: false, + users: { + type: 'array', + optional: true, nullable: true, + items: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, }, }, } as const; diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts index 3f20a4b802..9baacd6884 100644 --- a/packages/backend/src/models/json-schema/page.ts +++ b/packages/backend/src/models/json-schema/page.ts @@ -22,6 +22,32 @@ export const packedPageSchema = { optional: false, nullable: false, format: 'date-time', }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + content: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + }, + }, + variables: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + }, + }, title: { type: 'string', optional: false, nullable: false, @@ -34,23 +60,47 @@ export const packedPageSchema = { type: 'string', optional: false, nullable: true, }, - content: { - type: 'array', + hideTitleWhenPinned: { + type: 'boolean', optional: false, nullable: false, }, - variables: { - type: 'array', + alignCenter: { + type: 'boolean', optional: false, nullable: false, }, - userId: { + font: { type: 'string', optional: false, nullable: false, - format: 'id', }, - user: { - type: 'object', - ref: 'UserLite', + script: { + type: 'string', optional: false, nullable: false, }, + eyeCatchingImageId: { + type: 'string', + optional: false, nullable: true, + }, + eyeCatchingImage: { + type: 'object', + optional: false, nullable: true, + ref: 'DriveFile', + }, + attachedFiles: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'DriveFile', + }, + }, + likedCount: { + type: 'number', + optional: false, nullable: false, + }, + isLiked: { + type: 'boolean', + optional: true, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 37bdcbe281..b0e18db01a 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -49,11 +49,6 @@ export const packedUserLiteSchema = { nullable: false, optional: false, format: 'id', }, - url: { - type: 'string', - format: 'url', - nullable: false, optional: false, - }, angle: { type: 'number', nullable: false, optional: true, @@ -62,19 +57,14 @@ export const packedUserLiteSchema = { type: 'boolean', nullable: false, optional: true, }, + url: { + type: 'string', + format: 'url', + nullable: false, optional: false, + }, }, }, }, - isAdmin: { - type: 'boolean', - nullable: false, optional: true, - default: false, - }, - isModerator: { - type: 'boolean', - nullable: false, optional: true, - default: false, - }, isBot: { type: 'boolean', nullable: false, optional: true, @@ -83,12 +73,67 @@ export const packedUserLiteSchema = { type: 'boolean', nullable: false, optional: true, }, + instance: { + type: 'object', + nullable: false, optional: true, + properties: { + name: { + type: 'string', + nullable: true, optional: false, + }, + softwareName: { + type: 'string', + nullable: true, optional: false, + }, + softwareVersion: { + type: 'string', + nullable: true, optional: false, + }, + iconUrl: { + type: 'string', + nullable: true, optional: false, + }, + faviconUrl: { + type: 'string', + nullable: true, optional: false, + }, + themeColor: { + type: 'string', + nullable: true, optional: false, + }, + }, + }, + emojis: { + type: 'object', + nullable: false, optional: false, + }, onlineStatus: { type: 'string', - format: 'url', - nullable: true, optional: false, + nullable: false, optional: false, enum: ['unknown', 'online', 'active', 'offline'], }, + badgeRoles: { + type: 'array', + nullable: false, optional: true, + items: { + type: 'object', + nullable: false, optional: false, + properties: { + name: { + type: 'string', + nullable: false, optional: false, + }, + iconUrl: { + type: 'string', + nullable: true, optional: false, + }, + displayOrder: { + type: 'number', + nullable: false, optional: false, + }, + }, + }, + }, }, } as const; @@ -105,21 +150,18 @@ export const packedUserDetailedNotMeOnlySchema = { format: 'uri', nullable: true, optional: false, }, - movedToUri: { + movedTo: { type: 'string', format: 'uri', - nullable: true, - optional: false, + nullable: true, optional: false, }, alsoKnownAs: { type: 'array', - nullable: true, - optional: false, + nullable: true, optional: false, items: { type: 'string', format: 'id', - nullable: false, - optional: false, + nullable: false, optional: false, }, }, createdAt: { @@ -249,6 +291,11 @@ export const packedUserDetailedNotMeOnlySchema = { type: 'boolean', nullable: false, optional: false, }, + ffVisibility: { + type: 'string', + nullable: false, optional: false, + enum: ['public', 'followers', 'private'], + }, twoFactorEnabled: { type: 'boolean', nullable: false, optional: false, @@ -264,6 +311,57 @@ export const packedUserDetailedNotMeOnlySchema = { nullable: false, optional: false, default: false, }, + roles: { + type: 'array', + nullable: false, optional: false, + items: { + type: 'object', + nullable: false, optional: false, + properties: { + id: { + type: 'string', + nullable: false, optional: false, + format: 'id', + }, + name: { + type: 'string', + nullable: false, optional: false, + }, + color: { + type: 'string', + nullable: true, optional: false, + }, + iconUrl: { + type: 'string', + nullable: true, optional: false, + }, + description: { + type: 'string', + nullable: false, optional: false, + }, + isModerator: { + type: 'boolean', + nullable: false, optional: false, + }, + isAdministrator: { + type: 'boolean', + nullable: false, optional: false, + }, + displayOrder: { + type: 'number', + nullable: false, optional: false, + }, + }, + }, + }, + memo: { + type: 'string', + nullable: true, optional: false, + }, + moderationNote: { + type: 'string', + nullable: false, optional: true, + }, //#region relations isFollowing: { type: 'boolean', @@ -297,10 +395,6 @@ export const packedUserDetailedNotMeOnlySchema = { type: 'boolean', nullable: false, optional: true, }, - memo: { - type: 'string', - nullable: false, optional: true, - }, notify: { type: 'string', nullable: false, optional: true, @@ -326,29 +420,37 @@ export const packedMeDetailedOnlySchema = { nullable: true, optional: false, format: 'id', }, - injectFeaturedNote: { + isModerator: { type: 'boolean', nullable: true, optional: false, }, + isAdmin: { + type: 'boolean', + nullable: true, optional: false, + }, + injectFeaturedNote: { + type: 'boolean', + nullable: false, optional: false, + }, receiveAnnouncementEmail: { type: 'boolean', - nullable: true, optional: false, + nullable: false, optional: false, }, alwaysMarkNsfw: { type: 'boolean', - nullable: true, optional: false, + nullable: false, optional: false, }, autoSensitive: { type: 'boolean', - nullable: true, optional: false, + nullable: false, optional: false, }, carefulBot: { type: 'boolean', - nullable: true, optional: false, + nullable: false, optional: false, }, autoAcceptFollowed: { type: 'boolean', - nullable: true, optional: false, + nullable: false, optional: false, }, noCrawle: { type: 'boolean', @@ -387,10 +489,23 @@ export const packedMeDetailedOnlySchema = { type: 'boolean', nullable: false, optional: false, }, + unreadAnnouncements: { + type: 'array', + nullable: false, optional: false, + items: { + type: 'object', + nullable: false, optional: false, + ref: 'Announcement', + }, + }, hasUnreadAntenna: { type: 'boolean', nullable: false, optional: false, }, + hasUnreadChannel: { + type: 'boolean', + nullable: false, optional: false, + }, hasUnreadNotification: { type: 'boolean', nullable: false, optional: false, @@ -429,12 +544,132 @@ export const packedMeDetailedOnlySchema = { }, emailNotificationTypes: { type: 'array', - nullable: true, optional: false, + nullable: false, optional: false, items: { type: 'string', nullable: false, optional: false, }, }, + achievements: { + type: 'array', + nullable: false, optional: false, + items: { + type: 'object', + nullable: false, optional: false, + properties: { + name: { + type: 'string', + nullable: false, optional: false, + }, + unlockedAt: { + type: 'number', + nullable: false, optional: false, + }, + }, + }, + }, + loggedInDays: { + type: 'number', + nullable: false, optional: false, + }, + policies: { + type: 'object', + nullable: false, optional: false, + properties: { + gtlAvailable: { + type: 'boolean', + nullable: false, optional: false, + }, + ltlAvailable: { + type: 'boolean', + nullable: false, optional: false, + }, + canPublicNote: { + type: 'boolean', + nullable: false, optional: false, + }, + canInvite: { + type: 'boolean', + nullable: false, optional: false, + }, + inviteLimit: { + type: 'number', + nullable: false, optional: false, + }, + inviteLimitCycle: { + type: 'number', + nullable: false, optional: false, + }, + inviteExpirationTime: { + type: 'number', + nullable: false, optional: false, + }, + canManageCustomEmojis: { + type: 'boolean', + nullable: false, optional: false, + }, + canManageAvatarDecorations: { + type: 'boolean', + nullable: false, optional: false, + }, + canSearchNotes: { + type: 'boolean', + nullable: false, optional: false, + }, + canUseTranslator: { + type: 'boolean', + nullable: false, optional: false, + }, + canHideAds: { + type: 'boolean', + nullable: false, optional: false, + }, + driveCapacityMb: { + type: 'number', + nullable: false, optional: false, + }, + alwaysMarkNsfw: { + type: 'boolean', + nullable: false, optional: false, + }, + pinLimit: { + type: 'number', + nullable: false, optional: false, + }, + antennaLimit: { + type: 'number', + nullable: false, optional: false, + }, + wordMuteLimit: { + type: 'number', + nullable: false, optional: false, + }, + webhookLimit: { + type: 'number', + nullable: false, optional: false, + }, + clipLimit: { + type: 'number', + nullable: false, optional: false, + }, + noteEachClipsLimit: { + type: 'number', + nullable: false, optional: false, + }, + userListLimit: { + type: 'number', + nullable: false, optional: false, + }, + userEachUserListsLimit: { + type: 'number', + nullable: false, optional: false, + }, + rateLimitFactor: { + type: 'number', + nullable: false, optional: false, + }, + }, + }, //#region secrets email: { type: 'string', @@ -511,5 +746,13 @@ export const packedUserSchema = { type: 'object', ref: 'UserDetailed', }, + { + type: 'object', + ref: 'UserDetailedNotMe', + }, + { + type: 'object', + ref: 'MeDetailed', + }, ], } as const; From 77ac51a680c352d5bed90a90379fcaf1454612a7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 21 Nov 2023 11:32:13 +0900 Subject: [PATCH 04/41] update typescript to 5.3 --- package.json | 2 +- packages/backend/package.json | 2 +- packages/frontend/package.json | 2 +- packages/misskey-js/package.json | 2 +- packages/sw/package.json | 2 +- pnpm-lock.yaml | 172 +++++++++++++++---------------- 6 files changed, 91 insertions(+), 91 deletions(-) diff --git a/package.json b/package.json index d0fc867b3c..f51861401e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "js-yaml": "4.1.0", "postcss": "8.4.31", "terser": "5.24.0", - "typescript": "5.2.2" + "typescript": "5.3.2" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "6.11.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index 496c79c9c0..3b029a49d2 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -165,7 +165,7 @@ "tsconfig-paths": "4.2.0", "twemoji-parser": "14.0.0", "typeorm": "0.3.17", - "typescript": "5.2.2", + "typescript": "5.3.2", "ulid": "2.3.0", "vary": "1.1.2", "web-push": "3.6.6", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 62192d0dab..c7736f7ac7 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -69,7 +69,7 @@ "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", "twemoji-parser": "14.0.0", - "typescript": "5.2.2", + "typescript": "5.3.2", "uuid": "9.0.1", "v-code-diff": "1.7.2", "vanilla-tilt": "1.8.1", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 0a4855874f..69ce173bf4 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -32,7 +32,7 @@ "jest-websocket-mock": "2.5.0", "mock-socket": "9.3.1", "tsd": "0.29.0", - "typescript": "5.2.2" + "typescript": "5.3.2" }, "files": [ "built" diff --git a/packages/sw/package.json b/packages/sw/package.json index 3259cae879..3c74ee8c78 100644 --- a/packages/sw/package.json +++ b/packages/sw/package.json @@ -18,7 +18,7 @@ "@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67", "eslint": "8.53.0", "eslint-plugin-import": "2.29.0", - "typescript": "5.2.2" + "typescript": "5.3.2" }, "type": "module" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 904150b075..7373d5f10b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,8 +28,8 @@ importers: specifier: 5.24.0 version: 5.24.0 typescript: - specifier: 5.2.2 - version: 5.2.2 + specifier: 5.3.2 + version: 5.3.2 optionalDependencies: '@tensorflow/tfjs-core': specifier: 4.4.0 @@ -37,10 +37,10 @@ importers: devDependencies: '@typescript-eslint/eslint-plugin': specifier: 6.11.0 - version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2) '@typescript-eslint/parser': specifier: 6.11.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(eslint@8.53.0)(typescript@5.3.2) cross-env: specifier: 7.0.3 version: 7.0.3 @@ -381,8 +381,8 @@ importers: specifier: 0.3.17 version: 0.3.17(ioredis@5.3.2)(pg@8.11.3) typescript: - specifier: 5.2.2 - version: 5.2.2 + specifier: 5.3.2 + version: 5.3.2 ulid: specifier: 2.3.0 version: 2.3.0 @@ -615,10 +615,10 @@ importers: version: 8.5.9 '@typescript-eslint/eslint-plugin': specifier: 6.11.0 - version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2) '@typescript-eslint/parser': specifier: 6.11.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(eslint@8.53.0)(typescript@5.3.2) aws-sdk-client-mock: specifier: 3.0.0 version: 3.0.0 @@ -806,8 +806,8 @@ importers: specifier: 14.0.0 version: 14.0.0 typescript: - specifier: 5.2.2 - version: 5.2.2 + specifier: 5.3.2 + version: 5.3.2 uuid: specifier: 9.0.1 version: 9.0.1 @@ -822,7 +822,7 @@ importers: version: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0) vue: specifier: 3.3.8 - version: 3.3.8(typescript@5.2.2) + version: 3.3.8(typescript@5.3.2) vuedraggable: specifier: next version: 4.1.0(vue@3.3.8) @@ -862,10 +862,10 @@ importers: version: 7.5.3 '@storybook/react': specifier: 7.5.3 - version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) + version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2) '@storybook/react-vite': specifier: 7.5.3 - version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.2.2)(vite@4.5.0) + version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.3.2)(vite@4.5.0) '@storybook/testing-library': specifier: 0.2.2 version: 0.2.2 @@ -880,7 +880,7 @@ importers: version: 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.8) '@storybook/vue3-vite': specifier: 7.5.3 - version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.8) + version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@4.5.0)(vue@3.3.8) '@testing-library/vue': specifier: 8.0.0 version: 8.0.0(@vue/compiler-sfc@3.3.8)(vue@3.3.8) @@ -922,10 +922,10 @@ importers: version: 8.5.9 '@typescript-eslint/eslint-plugin': specifier: 6.11.0 - version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2) '@typescript-eslint/parser': specifier: 6.11.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(eslint@8.53.0)(typescript@5.3.2) '@vitest/coverage-v8': specifier: 0.34.6 version: 0.34.6(vitest@0.34.6) @@ -961,7 +961,7 @@ importers: version: 4.0.5 msw: specifier: 1.3.2 - version: 1.3.2(typescript@5.2.2) + version: 1.3.2(typescript@5.3.2) msw-storybook-addon: specifier: 1.10.0 version: 1.10.0(msw@1.3.2) @@ -1003,7 +1003,7 @@ importers: version: 9.3.2(eslint@8.53.0) vue-tsc: specifier: 1.8.22 - version: 1.8.22(typescript@5.2.2) + version: 1.8.22(typescript@5.3.2) packages/misskey-js: dependencies: @@ -1034,10 +1034,10 @@ importers: version: 20.9.1 '@typescript-eslint/eslint-plugin': specifier: 6.11.0 - version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2) '@typescript-eslint/parser': specifier: 6.11.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(eslint@8.53.0)(typescript@5.3.2) eslint: specifier: 8.53.0 version: 8.53.0 @@ -1057,8 +1057,8 @@ importers: specifier: 0.29.0 version: 0.29.0 typescript: - specifier: 5.2.2 - version: 5.2.2 + specifier: 5.3.2 + version: 5.3.2 packages/sw: dependencies: @@ -1074,7 +1074,7 @@ importers: devDependencies: '@typescript-eslint/parser': specifier: 6.11.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) + version: 6.11.0(eslint@8.53.0)(typescript@5.3.2) '@typescript/lib-webworker': specifier: npm:@types/serviceworker@0.0.67 version: /@types/serviceworker@0.0.67 @@ -1085,8 +1085,8 @@ importers: specifier: 2.29.0 version: 2.29.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0) typescript: - specifier: 5.2.2 - version: 5.2.2 + specifier: 5.3.2 + version: 5.3.2 packages: @@ -4254,7 +4254,7 @@ packages: chalk: 4.1.2 dev: true - /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.2.2)(vite@4.5.0): + /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.2)(vite@4.5.0): resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==} peerDependencies: typescript: '>= 4.3.x' @@ -4266,8 +4266,8 @@ packages: glob: 7.2.3 glob-promise: 4.2.2(glob@7.2.3) magic-string: 0.27.0 - react-docgen-typescript: 2.2.2(typescript@5.2.2) - typescript: 5.2.2 + react-docgen-typescript: 2.2.2(typescript@5.3.2) + typescript: 5.3.2 vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0) dev: true @@ -6348,7 +6348,7 @@ packages: - supports-color dev: true - /@storybook/builder-vite@7.5.3(typescript@5.2.2)(vite@4.5.0): + /@storybook/builder-vite@7.5.3(typescript@5.3.2)(vite@4.5.0): resolution: {integrity: sha512-c104V3O75OCVnfZj0Jr70V09g0KSbPGvQK2Zh31omXGvakG8XrhWolYxkmjOcForJmAqsXnKs/nw3F75Gp853g==} peerDependencies: '@preact/preset-vite': '*' @@ -6379,7 +6379,7 @@ packages: fs-extra: 11.1.1 magic-string: 0.30.5 rollup: 3.29.4 - typescript: 5.2.2 + typescript: 5.3.2 vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0) transitivePeerDependencies: - encoding @@ -6750,7 +6750,7 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/react-vite@7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.2.2)(vite@4.5.0): + /@storybook/react-vite@7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.3.2)(vite@4.5.0): resolution: {integrity: sha512-ArPyHgiPbT5YvcyK4xK/DfqBOpn4R4/EP3kfIGhx8QKJyOtxPEYFdkLIZ5xu3KnPX7/z7GT+4a6Rb+8sk9gliA==} engines: {node: '>=16'} peerDependencies: @@ -6758,10 +6758,10 @@ packages: react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^3.0.0 || ^4.0.0 || ^5.0.0 dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.2.2)(vite@4.5.0) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.2)(vite@4.5.0) '@rollup/pluginutils': 5.0.5(rollup@4.4.1) - '@storybook/builder-vite': 7.5.3(typescript@5.2.2)(vite@4.5.0) - '@storybook/react': 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2) + '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@4.5.0) + '@storybook/react': 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2) '@vitejs/plugin-react': 3.1.0(vite@4.5.0) magic-string: 0.30.5 react: 18.2.0 @@ -6777,7 +6777,7 @@ packages: - vite-plugin-glimmerx dev: true - /@storybook/react@7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2): + /@storybook/react@7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2): resolution: {integrity: sha512-dZILdM36xMFDjdmmy421G5X+sOIncB2qF3IPTooniG1i1Z6v/dVNo57ovdID9lDTNa+AWr2fLB9hANiISMqmjQ==} engines: {node: '>=16.0.0'} peerDependencies: @@ -6810,7 +6810,7 @@ packages: react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0) ts-dedent: 2.2.0 type-fest: 2.19.0 - typescript: 5.2.2 + typescript: 5.3.2 util-deprecate: 1.0.2 transitivePeerDependencies: - encoding @@ -6892,7 +6892,7 @@ packages: file-system-cache: 2.3.0 dev: true - /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.8): + /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@4.5.0)(vue@3.3.8): resolution: {integrity: sha512-gkNwDDn2AKthAtaoPrHb0+2gi33UluxpfSq/M5COoMEVFphj6y/jyDa+OEYlceXgnD8g2xvX4/yv2TbTNDzmcQ==} engines: {node: ^14.18 || >=16} peerDependencies: @@ -6900,7 +6900,7 @@ packages: react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^3.0.0 || ^4.0.0 || ^5.0.0 dependencies: - '@storybook/builder-vite': 7.5.3(typescript@5.2.2)(vite@4.5.0) + '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@4.5.0) '@storybook/core-server': 7.5.3 '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.8) '@vitejs/plugin-vue': 4.5.0(vite@4.5.0)(vue@3.3.8) @@ -6937,7 +6937,7 @@ packages: lodash: 4.17.21 ts-dedent: 2.2.0 type-fest: 2.19.0 - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) vue-component-type-helpers: 1.8.22 transitivePeerDependencies: - encoding @@ -7432,7 +7432,7 @@ packages: '@testing-library/dom': 9.3.3 '@vue/compiler-sfc': 3.3.8 '@vue/test-utils': 2.4.1(vue@3.3.8) - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) transitivePeerDependencies: - '@vue/server-renderer' dev: true @@ -8073,7 +8073,7 @@ packages: dev: true optional: true - /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2): resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8085,10 +8085,10 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.6.2 - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2) '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2) + '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2) '@typescript-eslint/visitor-keys': 6.11.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 @@ -8096,13 +8096,13 @@ packages: ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@5.2.2) - typescript: 5.2.2 + ts-api-utils: 1.0.1(typescript@5.3.2) + typescript: 5.3.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.2): resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8114,11 +8114,11 @@ packages: dependencies: '@typescript-eslint/scope-manager': 6.11.0 '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2) '@typescript-eslint/visitor-keys': 6.11.0 debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 - typescript: 5.2.2 + typescript: 5.3.2 transitivePeerDependencies: - supports-color dev: true @@ -8131,7 +8131,7 @@ packages: '@typescript-eslint/visitor-keys': 6.11.0 dev: true - /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.2): resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8141,12 +8141,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2) + '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2) debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 - ts-api-utils: 1.0.1(typescript@5.2.2) - typescript: 5.2.2 + ts-api-utils: 1.0.1(typescript@5.3.2) + typescript: 5.3.2 transitivePeerDependencies: - supports-color dev: true @@ -8156,7 +8156,7 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.11.0(typescript@5.2.2): + /@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.2): resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8171,13 +8171,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.1(typescript@5.2.2) - typescript: 5.2.2 + ts-api-utils: 1.0.1(typescript@5.3.2) + typescript: 5.3.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.2): resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -8188,7 +8188,7 @@ packages: '@types/semver': 7.5.5 '@typescript-eslint/scope-manager': 6.11.0 '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2) eslint: 8.53.0 semver: 7.5.4 transitivePeerDependencies: @@ -8232,7 +8232,7 @@ packages: vue: ^3.2.25 dependencies: vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0) - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) /@vitest/coverage-v8@0.34.6(vitest@0.34.6): resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==} @@ -8327,7 +8327,7 @@ packages: ast-kit: 0.11.2(rollup@4.4.1) local-pkg: 0.5.0 magic-string-ast: 0.3.0 - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) transitivePeerDependencies: - rollup dev: false @@ -8344,7 +8344,7 @@ packages: '@vue/shared': 3.3.8 magic-string: 0.30.5 unplugin: 1.5.1 - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) transitivePeerDependencies: - rollup dev: false @@ -8415,7 +8415,7 @@ packages: '@vue/compiler-dom': 3.3.8 '@vue/shared': 3.3.8 - /@vue/language-core@1.8.22(typescript@5.2.2): + /@vue/language-core@1.8.22(typescript@5.3.2): resolution: {integrity: sha512-bsMoJzCrXZqGsxawtUea1cLjUT9dZnDsy5TuZ+l1fxRMzUGQUG9+Ypq4w//CqpWmrx7nIAJpw2JVF/t258miRw==} peerDependencies: typescript: '*' @@ -8430,7 +8430,7 @@ packages: computeds: 0.0.1 minimatch: 9.0.3 muggle-string: 0.3.1 - typescript: 5.2.2 + typescript: 5.3.2 vue-template-compiler: 2.7.14 dev: true @@ -8468,7 +8468,7 @@ packages: dependencies: '@vue/compiler-ssr': 3.3.8 '@vue/shared': 3.3.8 - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) /@vue/shared@3.3.6: resolution: {integrity: sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==} @@ -8491,7 +8491,7 @@ packages: optional: true dependencies: js-beautify: 1.14.9 - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) vue-component-type-helpers: 1.8.4 dev: true @@ -11158,7 +11158,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2) debug: 3.2.7(supports-color@5.5.0) eslint: 8.53.0 eslint-import-resolver-node: 0.3.9 @@ -11176,7 +11176,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 @@ -14852,10 +14852,10 @@ packages: msw: '>=0.35.0 <2.0.0' dependencies: is-node-process: 1.2.0 - msw: 1.3.2(typescript@5.2.2) + msw: 1.3.2(typescript@5.3.2) dev: true - /msw@1.3.2(typescript@5.2.2): + /msw@1.3.2(typescript@5.3.2): resolution: {integrity: sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==} engines: {node: '>=14'} hasBin: true @@ -14884,7 +14884,7 @@ packages: path-to-regexp: 6.2.1 strict-event-emitter: 0.4.6 type-fest: 2.19.0 - typescript: 5.2.2 + typescript: 5.3.2 yargs: 17.6.2 transitivePeerDependencies: - encoding @@ -16773,12 +16773,12 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /react-docgen-typescript@2.2.2(typescript@5.2.2): + /react-docgen-typescript@2.2.2(typescript@5.3.2): resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==} peerDependencies: typescript: '>= 4.3.x' dependencies: - typescript: 5.2.2 + typescript: 5.3.2 dev: true /react-docgen@6.0.4: @@ -18627,13 +18627,13 @@ packages: escape-string-regexp: 5.0.0 dev: false - /ts-api-utils@1.0.1(typescript@5.2.2): + /ts-api-utils@1.0.1(typescript@5.3.2): resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.2.2 + typescript: 5.3.2 dev: true /ts-dedent@2.2.0: @@ -18897,8 +18897,8 @@ packages: hasBin: true dev: true - /typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + /typescript@5.3.2: + resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==} engines: {node: '>=14.17'} hasBin: true @@ -19186,7 +19186,7 @@ packages: diff: 5.1.0 diff-match-patch: 1.0.5 highlight.js: 11.8.0 - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) vue-demi: 0.13.11(vue@3.3.8) dev: false @@ -19400,7 +19400,7 @@ packages: '@vue/composition-api': optional: true dependencies: - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) dev: false /vue-docgen-api@4.64.1(vue@3.3.8): @@ -19444,7 +19444,7 @@ packages: peerDependencies: vue: '>=2' dependencies: - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) dev: true /vue-template-compiler@2.7.14: @@ -19454,19 +19454,19 @@ packages: he: 1.2.0 dev: true - /vue-tsc@1.8.22(typescript@5.2.2): + /vue-tsc@1.8.22(typescript@5.3.2): resolution: {integrity: sha512-j9P4kHtW6eEE08aS5McFZE/ivmipXy0JzrnTgbomfABMaVKx37kNBw//irL3+LlE3kOo63XpnRigyPC3w7+z+A==} hasBin: true peerDependencies: typescript: '*' dependencies: '@volar/typescript': 1.10.7 - '@vue/language-core': 1.8.22(typescript@5.2.2) + '@vue/language-core': 1.8.22(typescript@5.3.2) semver: 7.5.4 - typescript: 5.2.2 + typescript: 5.3.2 dev: true - /vue@3.3.8(typescript@5.2.2): + /vue@3.3.8(typescript@5.3.2): resolution: {integrity: sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==} peerDependencies: typescript: '*' @@ -19479,7 +19479,7 @@ packages: '@vue/runtime-dom': 3.3.8 '@vue/server-renderer': 3.3.8(vue@3.3.8) '@vue/shared': 3.3.8 - typescript: 5.2.2 + typescript: 5.3.2 /vuedraggable@4.1.0(vue@3.3.8): resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} @@ -19487,7 +19487,7 @@ packages: vue: ^3.0.1 dependencies: sortablejs: 1.14.0 - vue: 3.3.8(typescript@5.2.2) + vue: 3.3.8(typescript@5.3.2) dev: false /w3c-xmlserializer@4.0.0: From b5be0e5780453653ca37ea809c36757057a21758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:12:05 +0900 Subject: [PATCH 05/41] =?UTF-8?q?note.ts=E3=81=AEchannel=E3=82=92=E6=AD=A3?= =?UTF-8?q?=E3=81=97=E3=81=84=E5=BD=A2=E3=81=AB=E3=81=97=E3=81=9F=E3=81=93?= =?UTF-8?q?=E3=81=A8=E3=81=AB=E3=82=88=E3=82=8A=E8=A1=A8=E5=87=BA=E5=8C=96?= =?UTF-8?q?=E3=81=97=E3=81=9F=E5=9E=8B=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1239?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com> --- packages/backend/src/models/json-schema/note.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 9d5d558f51..392fa7e1cb 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -134,11 +134,19 @@ export const packedNoteSchema = { }, name: { type: 'string', - optional: false, nullable: true, + optional: false, nullable: false, + }, + color: { + type: 'string', + optional: false, nullable: false, }, isSensitive: { type: 'boolean', - optional: true, nullable: false, + optional: false, nullable: false, + }, + allowRenoteToExternal: { + type: 'boolean', + optional: false, nullable: false, }, }, }, From b3d1cc9525b44e37b983c3a97af4e2aea80ea735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:32:34 +0900 Subject: [PATCH 06/41] =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E8=B5=B7?= =?UTF-8?q?=E5=8B=95=E6=99=82=E3=81=AB=E3=82=A2=E3=83=B3=E3=83=86=E3=83=8A?= =?UTF-8?q?=E3=81=8C=E9=9D=9E=E3=82=A2=E3=82=AF=E3=83=86=E3=82=A3=E3=83=96?= =?UTF-8?q?=E3=81=A0=E3=81=A3=E3=81=9F=E5=A0=B4=E5=90=88=E3=80=81=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=83=86=E3=82=A3=E3=83=96=E5=8C=96=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=82=82=E5=86=8D=E8=B5=B7=E5=8B=95=E3=81=99=E3=82=8B=E3=81=BE?= =?UTF-8?q?=E3=81=A7=E5=8F=8D=E6=98=A0=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=20(#12391)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * サーバ起動時にアンテナが非アクティブだった場合、アクティブ化しても再起動するまで反映されない * Fix CHANGELOG.md * lastUsedAtの更新に不備が出るので修正 --------- Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com> --- CHANGELOG.md | 1 + packages/backend/src/core/AntennaService.ts | 20 ++++++++++++++----- .../server/api/endpoints/antennas/notes.ts | 16 +++++++++++---- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2efa1b230d..900d042eaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正 ### Server +- Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303 - Fix: ロールタイムラインが保存されない問題を修正 ## 2023.11.1 diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 65be275548..2815d24734 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -60,11 +60,21 @@ export class AntennaService implements OnApplicationShutdown { lastUsedAt: new Date(body.lastUsedAt), }); break; - case 'antennaUpdated': - this.antennas[this.antennas.findIndex(a => a.id === body.id)] = { - ...body, - lastUsedAt: new Date(body.lastUsedAt), - }; + case 'antennaUpdated': { + const idx = this.antennas.findIndex(a => a.id === body.id); + if (idx >= 0) { + this.antennas[idx] = { + ...body, + lastUsedAt: new Date(body.lastUsedAt), + }; + } else { + // サーバ起動時にactiveじゃなかった場合、リストに持っていないので追加する必要あり + this.antennas.push({ + ...body, + lastUsedAt: new Date(body.lastUsedAt), + }); + } + } break; case 'antennaDeleted': this.antennas = this.antennas.filter(a => a.id !== body.id); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 9b5911800c..29e56b1085 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -13,6 +13,7 @@ import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -71,6 +72,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private noteReadService: NoteReadService, private funoutTimelineService: FunoutTimelineService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); @@ -85,10 +87,16 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchAntenna); } - this.antennasRepository.update(antenna.id, { - isActive: true, - lastUsedAt: new Date(), - }); + // falseだった場合はアンテナの配信先が増えたことを通知したい + const needPublishEvent = !antenna.isActive; + + antenna.isActive = true; + antenna.lastUsedAt = new Date(); + this.antennasRepository.update(antenna.id, antenna); + + if (needPublishEvent) { + this.globalEventService.publishInternalEvent('antennaUpdated', antenna); + } let noteIds = await this.funoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId); noteIds = noteIds.slice(0, ps.limit); From 481bca4cf2e8f13a46654770291b7ee31a1c361e Mon Sep 17 00:00:00 2001 From: nenohi Date: Tue, 21 Nov 2023 19:50:06 +0900 Subject: [PATCH 07/41] =?UTF-8?q?=E5=BA=83=E5=91=8A=E6=8E=B2=E8=BC=89?= =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AB=E3=81=A6filter=E3=82=92?= =?UTF-8?q?=E3=82=8F=E3=81=8B=E3=82=8A=E3=82=84=E3=81=99=E3=81=8F=20(#1238?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/admin/ad/list.ts | 6 ++- packages/frontend/src/pages/admin/ads.vue | 50 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 29eff89523..1366fbf76a 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -22,7 +22,7 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, - publishing: { type: 'boolean', default: false }, + publishing: { type: 'boolean', default: null, nullable: true}, }, required: [], } as const; @@ -37,8 +37,10 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId); - if (ps.publishing) { + if (ps.publishing === true) { query.andWhere('ad.expiresAt > :now', { now: new Date() }).andWhere('ad.startsAt <= :now', { now: new Date() }); + } else if (ps.publishing === false) { + query.andWhere('ad.expiresAt <= :now', { now: new Date() }).orWhere('ad.startsAt > :now', { now: new Date() }); } const ads = await query.limit(ps.limit).getMany(); diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index 6e07585fdc..1ce99d4ba5 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -9,12 +9,15 @@ SPDX-License-Identifier: AGPL-3.0-only - - {{ i18n.ts.publishing }} - + + + + + +
- + @@ -82,14 +85,14 @@ SPDX-License-Identifier: AGPL-3.0-only + + From c8b85a98b807be7d7b4032cb2b9703c25665b1c5 Mon Sep 17 00:00:00 2001 From: woxtu Date: Sun, 26 Nov 2023 09:54:24 +0900 Subject: [PATCH 31/41] Add mocks for Web Audio API (#12457) --- packages/frontend/test/init.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts index 986fa99c17..ab5e84b53c 100644 --- a/packages/frontend/test/init.ts +++ b/packages/frontend/test/init.ts @@ -25,3 +25,21 @@ vi.mock('@/store.js', () => { }, }; }); + +// Add mocks for Web Audio API +const AudioNodeMock = vi.fn(() => ({ + connect: vi.fn(() => ({ connect: vi.fn() })), + start: vi.fn(), +})); + +const GainNodeMock = vi.fn(() => ({ + gain: vi.fn(), +})); + +const AudioContextMock = vi.fn(() => ({ + createBufferSource: vi.fn(() => new AudioNodeMock()), + createGain: vi.fn(() => new GainNodeMock()), + decodeAudioData: vi.fn(), +})); + +vi.stubGlobal('AudioContext', AudioContextMock); From 3e0231d99501aba3b1f47431015c9b4a2f671e26 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 26 Nov 2023 10:01:06 +0900 Subject: [PATCH 32/41] =?UTF-8?q?fix(backend):=20=E4=BD=95=E3=82=82?= =?UTF-8?q?=E3=83=8E=E3=83=BC=E3=83=88=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E3=83=95?= =?UTF-8?q?=E3=82=A3=E3=83=BC=E3=83=89=E3=81=AB=E3=82=A2=E3=82=AF=E3=82=BB?= =?UTF-8?q?=E3=82=B9=E3=81=99=E3=82=8B=E3=81=A8=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20(#12455)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(backend): 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正 * Update CHANGELOG.md * add test * fix: incorrect bob's username --- CHANGELOG.md | 1 + packages/backend/src/server/web/FeedService.ts | 2 +- packages/backend/test/e2e/fetch-resource.ts | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0215e7e735..fdf6709117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Fix: api.jsonの生成ロジックを改善 #12402 - Fix: 招待コードが使い回せる問題を修正 - Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正 +- Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正 ## 2023.11.1 diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 3ba26ad34a..dd4304e6ef 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -58,7 +58,7 @@ export class FeedService { const feed = new Feed({ id: author.link, title: `${author.name} (@${user.username}@${this.config.host})`, - updated: this.idService.parse(notes[0].id).date, + updated: notes.length !== 0 ? this.idService.parse(notes[0].id).date : undefined, generator: 'Misskey', description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, link: author.link, diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts index 1cbfec3e5f..251d662760 100644 --- a/packages/backend/test/e2e/fetch-resource.ts +++ b/packages/backend/test/e2e/fetch-resource.ts @@ -93,7 +93,7 @@ describe('Webリソース', () => { }); aliceChannel = await channel(alice, {}); - bob = await signup({ username: 'alice' }); + bob = await signup({ username: 'bob' }); }, 1000 * 60 * 2); afterAll(async () => { @@ -152,6 +152,11 @@ describe('Webリソース', () => { type, })); + test('がGETできる。(ノートが存在しない場合でも。)', async () => await ok({ + path: path(bob.username), + type, + })); + test('は存在しないユーザーはGETできない。', async () => await notOk({ path: path('nonexisting'), status: 404, From 7a494b2aa7e0337a220b8988122839e9a7b75538 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 26 Nov 2023 10:02:22 +0900 Subject: [PATCH 33/41] fix(backend): rename FunoutTimelineService to FanoutTimelineService (#12453) --- packages/backend/src/core/AntennaService.ts | 6 ++-- packages/backend/src/core/CoreModule.ts | 12 +++---- ...ineService.ts => FanoutTimelineService.ts} | 2 +- .../backend/src/core/NoteCreateService.ts | 36 +++++++++---------- packages/backend/src/core/RoleService.ts | 6 ++-- .../backend/src/core/UserFollowingService.ts | 8 ++--- .../server/api/endpoints/antennas/notes.ts | 6 ++-- .../server/api/endpoints/channels/timeline.ts | 6 ++-- .../api/endpoints/notes/hybrid-timeline.ts | 10 +++--- .../api/endpoints/notes/local-timeline.ts | 8 ++--- .../server/api/endpoints/notes/timeline.ts | 6 ++-- .../api/endpoints/notes/user-list-timeline.ts | 6 ++-- .../src/server/api/endpoints/roles/notes.ts | 6 ++-- .../src/server/api/endpoints/users/notes.ts | 10 +++--- 14 files changed, 64 insertions(+), 64 deletions(-) rename packages/backend/src/core/{FunoutTimelineService.ts => FanoutTimelineService.ts} (98%) diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 2815d24734..2c27a02559 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -16,7 +16,7 @@ import type { AntennasRepository, UserListMembershipsRepository } from '@/models import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import type { GlobalEvents } from '@/core/GlobalEventService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -39,7 +39,7 @@ export class AntennaService implements OnApplicationShutdown { private utilityService: UtilityService, private globalEventService: GlobalEventService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, ) { this.antennasFetched = false; this.antennas = []; @@ -94,7 +94,7 @@ export class AntennaService implements OnApplicationShutdown { const redisPipeline = this.redisForTimelines.pipeline(); for (const antenna of matchedAntennas) { - this.funoutTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline); + this.fanoutTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline); this.globalEventService.publishAntennaStream(antenna.id, 'note', note); } diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 9fb29e0e68..bf6f0ef879 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -62,7 +62,7 @@ import { FileInfoService } from './FileInfoService.js'; import { SearchService } from './SearchService.js'; import { ClipService } from './ClipService.js'; import { FeaturedService } from './FeaturedService.js'; -import { FunoutTimelineService } from './FunoutTimelineService.js'; +import { FanoutTimelineService } from './FanoutTimelineService.js'; import { ChannelFollowingService } from './ChannelFollowingService.js'; import { RegistryApiService } from './RegistryApiService.js'; import { ChartLoggerService } from './chart/ChartLoggerService.js'; @@ -194,7 +194,7 @@ const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: Fi const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService }; const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService }; const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService }; -const $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService }; +const $FanoutTimelineService: Provider = { provide: 'FanoutTimelineService', useExisting: FanoutTimelineService }; const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService }; const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService }; @@ -330,7 +330,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SearchService, ClipService, FeaturedService, - FunoutTimelineService, + FanoutTimelineService, ChannelFollowingService, RegistryApiService, ChartLoggerService, @@ -459,7 +459,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SearchService, $ClipService, $FeaturedService, - $FunoutTimelineService, + $FanoutTimelineService, $ChannelFollowingService, $RegistryApiService, $ChartLoggerService, @@ -589,7 +589,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting SearchService, ClipService, FeaturedService, - FunoutTimelineService, + FanoutTimelineService, ChannelFollowingService, RegistryApiService, FederationChart, @@ -717,7 +717,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $SearchService, $ClipService, $FeaturedService, - $FunoutTimelineService, + $FanoutTimelineService, $ChannelFollowingService, $RegistryApiService, $FederationChart, diff --git a/packages/backend/src/core/FunoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts similarity index 98% rename from packages/backend/src/core/FunoutTimelineService.ts rename to packages/backend/src/core/FanoutTimelineService.ts index c633c329e5..6a1b0aa879 100644 --- a/packages/backend/src/core/FunoutTimelineService.ts +++ b/packages/backend/src/core/FanoutTimelineService.ts @@ -10,7 +10,7 @@ import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; @Injectable() -export class FunoutTimelineService { +export class FanoutTimelineService { constructor( @Inject(DI.redisForTimelines) private redisForTimelines: Redis.Redis, diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 86f220abd0..fd87edc28e 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -54,7 +54,7 @@ import { RoleService } from '@/core/RoleService.js'; import { MetaService } from '@/core/MetaService.js'; import { SearchService } from '@/core/SearchService.js'; import { FeaturedService } from '@/core/FeaturedService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; @@ -194,7 +194,7 @@ export class NoteCreateService implements OnApplicationShutdown { private idService: IdService, private globalEventService: GlobalEventService, private queueService: QueueService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private noteReadService: NoteReadService, private notificationService: NotificationService, private relayService: RelayService, @@ -843,9 +843,9 @@ export class NoteCreateService implements OnApplicationShutdown { const r = this.redisForTimelines.pipeline(); if (note.channelId) { - this.funoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r); + this.fanoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r); - this.funoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); + this.fanoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); const channelFollowings = await this.channelFollowingsRepository.find({ where: { @@ -855,9 +855,9 @@ export class NoteCreateService implements OnApplicationShutdown { }); for (const channelFollowing of channelFollowings) { - this.funoutTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); + this.fanoutTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - this.funoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); + this.fanoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } } else { @@ -895,9 +895,9 @@ export class NoteCreateService implements OnApplicationShutdown { if (!following.withReplies) continue; } - this.funoutTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); + this.fanoutTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - this.funoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); + this.fanoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } @@ -913,36 +913,36 @@ export class NoteCreateService implements OnApplicationShutdown { if (!userListMembership.withReplies) continue; } - this.funoutTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r); + this.fanoutTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r); if (note.fileIds.length > 0) { - this.funoutTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r); + this.fanoutTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r); } } if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL - this.funoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r); + this.fanoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r); if (note.fileIds.length > 0) { - this.funoutTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); + this.fanoutTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r); } } // 自分自身以外への返信 if (note.replyId && note.replyUserId !== note.userId) { - this.funoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); + this.fanoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); if (note.visibility === 'public' && note.userHost == null) { - this.funoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); + this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r); } } else { - this.funoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); + this.fanoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r); if (note.fileIds.length > 0) { - this.funoutTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r); + this.fanoutTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r); } if (note.visibility === 'public' && note.userHost == null) { - this.funoutTimelineService.push('localTimeline', note.id, 1000, r); + this.fanoutTimelineService.push('localTimeline', note.id, 1000, r); if (note.fileIds.length > 0) { - this.funoutTimelineService.push('localTimelineWithFiles', note.id, 500, r); + this.fanoutTimelineService.push('localTimelineWithFiles', note.id, 500, r); } } } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 432887b3b7..29e48aa8ca 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -20,7 +20,7 @@ import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import type { Packed } from '@/misc/json-schema.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; export type RolePolicies = { @@ -108,7 +108,7 @@ export class RoleService implements OnApplicationShutdown { private globalEventService: GlobalEventService, private idService: IdService, private moderationLogService: ModerationLogService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, ) { //this.onMessage = this.onMessage.bind(this); @@ -482,7 +482,7 @@ export class RoleService implements OnApplicationShutdown { const redisPipeline = this.redisForTimelines.pipeline(); for (const role of roles) { - this.funoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline); + this.fanoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline); this.globalEventService.publishRoleTimelineStream(role.id, 'note', note); } diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index bd7f298021..3062999c08 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -29,7 +29,7 @@ import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import Logger from '../logger.js'; const logger = new Logger('following/create'); @@ -84,7 +84,7 @@ export class UserFollowingService implements OnModuleInit { private webhookService: WebhookService, private apRendererService: ApRendererService, private accountMoveService: AccountMoveService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private perUserFollowingChart: PerUserFollowingChart, private instanceChart: InstanceChart, ) { @@ -305,7 +305,7 @@ export class UserFollowingService implements OnModuleInit { } }); - this.funoutTimelineService.purge(`homeTimeline:${follower.id}`); + this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`); } // Publish followed event @@ -374,7 +374,7 @@ export class UserFollowingService implements OnModuleInit { } }); - this.funoutTimelineService.purge(`homeTimeline:${follower.id}`); + this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`); } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 29e56b1085..0bf2688b4a 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -12,7 +12,7 @@ import { NoteReadService } from '@/core/NoteReadService.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ApiError } from '../../error.js'; @@ -71,7 +71,7 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private queryService: QueryService, private noteReadService: NoteReadService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { @@ -98,7 +98,7 @@ export default class extends Endpoint { // eslint- this.globalEventService.publishInternalEvent('antennaUpdated', antenna); } - let noteIds = await this.funoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId); + let noteIds = await this.fanoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId); noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 4ca7325f30..f9207199d6 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -12,7 +12,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { CacheService } from '@/core/CacheService.js'; import { MetaService } from '@/core/MetaService.js'; @@ -70,7 +70,7 @@ export default class extends Endpoint { // eslint- private idService: IdService, private noteEntityService: NoteEntityService, private queryService: QueryService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private cacheService: CacheService, private activeUsersChart: ActiveUsersChart, private metaService: MetaService, @@ -99,7 +99,7 @@ export default class extends Endpoint { // eslint- this.cacheService.userMutingsCache.fetch(me.id), ]) : [new Set()]; - let noteIds = await this.funoutTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId); + let noteIds = await this.fanoutTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId); noteIds = noteIds.slice(0, ps.limit); if (noteIds.length > 0) { 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 408c2fa371..372199844d 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -14,7 +14,7 @@ import { RoleService } from '@/core/RoleService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { CacheService } from '@/core/CacheService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { QueryService } from '@/core/QueryService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { MetaService } from '@/core/MetaService.js'; @@ -77,7 +77,7 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private queryService: QueryService, private userFollowingService: UserFollowingService, private metaService: MetaService, @@ -120,20 +120,20 @@ export default class extends Endpoint { // eslint- let shouldFallbackToDb = false; if (ps.withFiles) { - const [htlNoteIds, ltlNoteIds] = await this.funoutTimelineService.getMulti([ + const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([ `homeTimelineWithFiles:${me.id}`, 'localTimelineWithFiles', ], untilId, sinceId); noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); } else if (ps.withReplies) { - const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.funoutTimelineService.getMulti([ + const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.fanoutTimelineService.getMulti([ `homeTimeline:${me.id}`, 'localTimeline', 'localTimelineWithReplies', ], untilId, sinceId); noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds, ...ltlReplyNoteIds])); } else { - const [htlNoteIds, ltlNoteIds] = await this.funoutTimelineService.getMulti([ + const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([ `homeTimeline:${me.id}`, 'localTimeline', ], untilId, sinceId); 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 79baa6b285..7d8dec7b8f 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -14,7 +14,7 @@ import { RoleService } from '@/core/RoleService.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { QueryService } from '@/core/QueryService.js'; import { MetaService } from '@/core/MetaService.js'; import { MiLocalUser } from '@/models/User.js'; @@ -69,7 +69,7 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private queryService: QueryService, private metaService: MetaService, ) { @@ -107,9 +107,9 @@ export default class extends Endpoint { // eslint- let noteIds: string[]; if (ps.withFiles) { - noteIds = await this.funoutTimelineService.get('localTimelineWithFiles', untilId, sinceId); + noteIds = await this.fanoutTimelineService.get('localTimelineWithFiles', untilId, sinceId); } else { - const [nonReplyNoteIds, replyNoteIds] = await this.funoutTimelineService.getMulti([ + const [nonReplyNoteIds, replyNoteIds] = await this.fanoutTimelineService.getMulti([ 'localTimeline', 'localTimelineWithReplies', ], untilId, sinceId); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 8037d4862f..470abe0b14 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -14,7 +14,7 @@ import { DI } from '@/di-symbols.js'; import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { MiLocalUser } from '@/models/User.js'; import { MetaService } from '@/core/MetaService.js'; @@ -65,7 +65,7 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private userFollowingService: UserFollowingService, private queryService: QueryService, private metaService: MetaService, @@ -101,7 +101,7 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let noteIds = await this.funoutTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId); + let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId); noteIds = noteIds.slice(0, ps.limit); let redisTimeline: MiNote[] = []; 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 dbc3875597..1ac1d37f48 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 @@ -13,7 +13,7 @@ import { DI } from '@/di-symbols.js'; import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { QueryService } from '@/core/QueryService.js'; import { MiLocalUser } from '@/models/User.js'; import { MetaService } from '@/core/MetaService.js'; @@ -81,7 +81,7 @@ export default class extends Endpoint { // eslint- private activeUsersChart: ActiveUsersChart, private cacheService: CacheService, private idService: IdService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private queryService: QueryService, private metaService: MetaService, ) { @@ -123,7 +123,7 @@ export default class extends Endpoint { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let noteIds = await this.funoutTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId); + let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId); noteIds = noteIds.slice(0, ps.limit); let redisTimeline: MiNote[] = []; diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index daa9affc20..7010df22c9 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -11,7 +11,7 @@ import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -66,7 +66,7 @@ export default class extends Endpoint { // eslint- private idService: IdService, private noteEntityService: NoteEntityService, private queryService: QueryService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, ) { super(meta, paramDef, async (ps, me) => { const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); @@ -84,7 +84,7 @@ export default class extends Endpoint { // eslint- return []; } - let noteIds = await this.funoutTimelineService.get(`roleTimeline:${role.id}`, untilId, sinceId); + let noteIds = await this.fanoutTimelineService.get(`roleTimeline:${role.id}`, untilId, sinceId); noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 2e7d939b12..a775b58f03 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -14,7 +14,7 @@ import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { QueryService } from '@/core/QueryService.js'; -import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; +import { FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { MetaService } from '@/core/MetaService.js'; import { ApiError } from '../../error.js'; @@ -71,7 +71,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private cacheService: CacheService, private idService: IdService, - private funoutTimelineService: FunoutTimelineService, + private fanoutTimelineService: FanoutTimelineService, private metaService: MetaService, ) { super(meta, paramDef, async (ps, me) => { @@ -90,9 +90,9 @@ export default class extends Endpoint { // eslint- ]) : [new Set()]; const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([ - this.funoutTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId), - ps.withReplies ? this.funoutTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), - ps.withChannelNotes ? this.funoutTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), + this.fanoutTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId), + ps.withReplies ? this.fanoutTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), + ps.withChannelNotes ? this.fanoutTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]), ]); let noteIds = Array.from(new Set([ From 2ee48ae04da540384214ff0d7c8df2dfb18c88fc Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sun, 26 Nov 2023 10:05:56 +0900 Subject: [PATCH 34/41] =?UTF-8?q?fix(backend):=20=E3=82=AE=E3=83=A3?= =?UTF-8?q?=E3=83=A9=E3=83=AA=E3=83=BC=E3=81=AE=E4=BA=BA=E6=B0=97=E3=81=AE?= =?UTF-8?q?=E6=8A=95=E7=A8=BF=E3=81=AE=E9=81=B8=E5=87=BA=E3=81=ABid?= =?UTF-8?q?=E3=82=92=E7=94=A8=E3=81=84=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=20(#12448)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/FeaturedService.ts | 13 ++++++- .../server/api/endpoints/gallery/featured.ts | 37 ++++++++++++++++--- .../api/endpoints/gallery/posts/like.ts | 7 ++++ .../api/endpoints/gallery/posts/unlike.ts | 10 +++++ 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts index 9617f83880..507fc464ff 100644 --- a/packages/backend/src/core/FeaturedService.ts +++ b/packages/backend/src/core/FeaturedService.ts @@ -5,11 +5,12 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; -import type { MiNote, MiUser } from '@/models/_.js'; +import type { MiGalleryPost, MiNote, MiUser } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; const GLOBAL_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと +export const GALLERY_POSTS_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと const PER_USER_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 7; // 1週間ごと const HASHTAG_RANKING_WINDOW = 1000 * 60 * 60; // 1時間ごと @@ -79,6 +80,11 @@ export class FeaturedService { return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score); } + @bindThis + public updateGalleryPostsRanking(galleryPostId: MiGalleryPost['id'], score = 1): Promise { + return this.updateRankingOf('featuredGalleryPostsRanking', GALLERY_POSTS_RANKING_WINDOW, galleryPostId, score); + } + @bindThis public updateInChannelNotesRanking(channelId: MiNote['channelId'], noteId: MiNote['id'], score = 1): Promise { return this.updateRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, noteId, score); @@ -99,6 +105,11 @@ export class FeaturedService { return this.getRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, threshold); } + @bindThis + public getGalleryPostsRanking(threshold: number): Promise { + return this.getRankingOf('featuredGalleryPostsRanking', GALLERY_POSTS_RANKING_WINDOW, threshold); + } + @bindThis public getInChannelNotesRanking(channelId: MiNote['channelId'], threshold: number): Promise { return this.getRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, threshold); diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index cbab3a83a4..cea4234065 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPostsRepository } from '@/models/_.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; +import { FeaturedService } from '@/core/FeaturedService.js'; export const meta = { tags: ['gallery'], @@ -27,25 +28,49 @@ export const meta = { export const paramDef = { type: 'object', - properties: {}, + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + untilId: { type: 'string', format: 'misskey:id' }, + }, required: [], } as const; @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export + private galleryPostsRankingCache: string[] = []; + private galleryPostsRankingCacheLastFetchedAt = 0; + constructor( @Inject(DI.galleryPostsRepository) private galleryPostsRepository: GalleryPostsRepository, private galleryPostEntityService: GalleryPostEntityService, + private featuredService: FeaturedService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.galleryPostsRepository.createQueryBuilder('post') - .andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) }) - .andWhere('post.likedCount > 0') - .orderBy('post.likedCount', 'DESC'); + let postIds: string[]; + if (this.galleryPostsRankingCacheLastFetchedAt !== 0 && (Date.now() - this.galleryPostsRankingCacheLastFetchedAt < 1000 * 60 * 30)) { + postIds = this.galleryPostsRankingCache; + } else { + postIds = await this.featuredService.getGalleryPostsRanking(100); + this.galleryPostsRankingCache = postIds; + this.galleryPostsRankingCacheLastFetchedAt = Date.now(); + } - const posts = await query.limit(10).getMany(); + postIds.sort((a, b) => a > b ? -1 : 1); + if (ps.untilId) { + postIds = postIds.filter(id => id < ps.untilId!); + } + postIds = postIds.slice(0, ps.limit); + + if (postIds.length === 0) { + return []; + } + + const query = this.galleryPostsRepository.createQueryBuilder('post') + .where('post.id IN (:...postIds)', { postIds: postIds }); + + const posts = await query.getMany(); return await this.galleryPostEntityService.packMany(posts, me); }); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index 561b2492ab..cc424261b4 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -6,6 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/_.js'; +import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -57,6 +58,7 @@ export default class extends Endpoint { // eslint- @Inject(DI.galleryLikesRepository) private galleryLikesRepository: GalleryLikesRepository, + private featuredService: FeaturedService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { @@ -88,6 +90,11 @@ export default class extends Endpoint { // eslint- userId: me.id, }); + // ランキング更新 + if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) { + await this.featuredService.updateGalleryPostsRanking(post.id, 1); + } + this.galleryPostsRepository.increment({ id: post.id }, 'likedCount', 1); }); } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index 832b62282f..caa4d45553 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -6,6 +6,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/_.js'; +import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js'; +import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -49,6 +51,9 @@ export default class extends Endpoint { // eslint- @Inject(DI.galleryLikesRepository) private galleryLikesRepository: GalleryLikesRepository, + + private featuredService: FeaturedService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId }); @@ -68,6 +73,11 @@ export default class extends Endpoint { // eslint- // Delete like await this.galleryLikesRepository.delete(exist.id); + // ランキング更新 + if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) { + await this.featuredService.updateGalleryPostsRanking(post.id, -1); + } + this.galleryPostsRepository.decrement({ id: post.id }, 'likedCount', 1); }); } From d32631d1590ffe40f051e7abf75faab7bbf9c7da Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Sun, 26 Nov 2023 12:54:23 +0900 Subject: [PATCH 35/41] fix: query error in notes/featured (#12439) --- .../backend/src/server/api/endpoints/notes/featured.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index c456874309..bc6cbe242f 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -64,16 +64,16 @@ export default class extends Endpoint { // eslint- } } - if (noteIds.length === 0) { - return []; - } - noteIds.sort((a, b) => a > b ? -1 : 1); if (ps.untilId) { noteIds = noteIds.filter(id => id < ps.untilId!); } noteIds = noteIds.slice(0, ps.limit); + if (noteIds.length === 0) { + return []; + } + const query = this.notesRepository.createQueryBuilder('note') .where('note.id IN (:...noteIds)', { noteIds: noteIds }) .innerJoinAndSelect('note.user', 'user') From 5bdae9f6d03bbb1fc1cf341b2e6b3e5d15d03fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 26 Nov 2023 13:04:44 +0900 Subject: [PATCH 36/41] =?UTF-8?q?enhance(frontend):=20=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E9=81=B8=E6=8A=9E=E6=99=82?= =?UTF-8?q?=E3=81=AB=E9=9F=B3=E3=82=92=E6=B5=81=E3=81=9B=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=20(#12441)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (add) リアクション選択時に音を鳴らせるように * Update Changelog * tweak sound * tweak sound --------- Co-authored-by: syuilo --- CHANGELOG.md | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../frontend/assets/sounds/syuilo/bubble1.mp3 | Bin 0 -> 19328 bytes .../frontend/assets/sounds/syuilo/bubble2.mp3 | Bin 0 -> 19328 bytes packages/frontend/src/components/MkNote.vue | 5 +++++ .../frontend/src/components/MkNoteDetailed.vue | 5 +++++ .../components/MkReactionsViewer.reaction.vue | 7 +++++++ packages/frontend/src/pages/settings/sounds.vue | 3 ++- packages/frontend/src/scripts/sound.ts | 4 +++- packages/frontend/src/store.ts | 4 ++++ 11 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 packages/frontend/assets/sounds/syuilo/bubble1.mp3 create mode 100644 packages/frontend/assets/sounds/syuilo/bubble2.mp3 diff --git a/CHANGELOG.md b/CHANGELOG.md index fdf6709117..7f02d462b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ ### Client - Enhance: 絵文字のオートコンプリート機能強化 #12364 - Enhance: ユーザーのRawデータを表示するページが復活 +- Enhance: リアクション選択時に音を鳴らせるように - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367 - Fix: コードエディタが正しく表示されない問題を修正 diff --git a/locales/index.d.ts b/locales/index.d.ts index 042c7750e1..6e9fe311f1 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1943,6 +1943,7 @@ export interface Locale { "notification": string; "antenna": string; "channel": string; + "reaction": string; }; "_ago": { "future": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 58e0dd0b19..0b051b6190 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1848,6 +1848,7 @@ _sfx: notification: "通知" antenna: "アンテナ受信" channel: "チャンネル通知" + reaction: "リアクション選択時" _ago: future: "未来" diff --git a/packages/frontend/assets/sounds/syuilo/bubble1.mp3 b/packages/frontend/assets/sounds/syuilo/bubble1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..05b8ef8b10056c66a6e1a9b401d86530ecdc3296 GIT binary patch literal 19328 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 DcMQD( literal 0 HcmV?d00001 diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 6349df2e30..d047495dc9 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -163,6 +163,7 @@ import { focusPrev, focusNext } from '@/scripts/focus.js'; import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; +import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; @@ -336,6 +337,8 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.reactionAcceptance === 'likeOnly') { + sound.play('reaction'); + if (props.mock) { return; } @@ -354,6 +357,8 @@ function react(viaKeyboard = false): void { } else { blur(); reactionPicker.show(reactButton.value, reaction => { + sound.play('reaction'); + if (props.mock) { emit('reaction', reaction); return; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index d1bc3f676f..d8089ac36f 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -210,6 +210,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; @@ -369,6 +370,8 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.reactionAcceptance === 'likeOnly') { + sound.play('reaction'); + os.api('notes/reactions/create', { noteId: appearNote.id, reaction: '❤️', @@ -383,6 +386,8 @@ function react(viaKeyboard = false): void { } else { blur(); reactionPicker.show(reactButton.value, reaction => { + sound.play('reaction'); + os.api('notes/reactions/create', { noteId: appearNote.id, reaction: reaction, diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 9a107c3674..65a5c2374e 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -28,6 +28,7 @@ import MkReactionEffect from '@/components/MkReactionEffect.vue'; import { claimAchievement } from '@/scripts/achievements.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; +import * as sound from '@/scripts/sound.js'; const props = defineProps<{ reaction: string; @@ -59,6 +60,10 @@ async function toggleReaction() { }); if (confirm.canceled) return; + if (oldReaction !== props.reaction) { + sound.play('reaction'); + } + if (mock) { emit('reactionToggled', props.reaction, (props.count - 1)); return; @@ -75,6 +80,8 @@ async function toggleReaction() { } }); } else { + sound.play('reaction'); + if (mock) { emit('reactionToggled', props.reaction, (props.count + 1)); return; diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index cd1707a594..244bb1e0e2 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -38,7 +38,7 @@ import { defaultStore } from '@/store.js'; const masterVolume = computed(defaultStore.makeGetterSetter('sound_masterVolume')); -const soundsKeys = ['note', 'noteMy', 'notification', 'antenna', 'channel'] as const; +const soundsKeys = ['note', 'noteMy', 'notification', 'antenna', 'channel', 'reaction'] as const; const sounds = ref>>({ note: defaultStore.reactiveState.sound_note, @@ -46,6 +46,7 @@ const sounds = ref>>({ notification: defaultStore.reactiveState.sound_notification, antenna: defaultStore.reactiveState.sound_antenna, channel: defaultStore.reactiveState.sound_channel, + reaction: defaultStore.reactiveState.sound_reaction, }); async function updated(type: keyof typeof sounds.value, sound) { diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 2b604bd98a..4d7ef9bdee 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -38,6 +38,8 @@ export const soundsTypes = [ 'syuilo/waon', 'syuilo/popo', 'syuilo/triple', + 'syuilo/bubble1', + 'syuilo/bubble2', 'syuilo/poi1', 'syuilo/poi2', 'syuilo/pirori', @@ -77,7 +79,7 @@ export async function loadAudio(file: string, useCache = true) { return audioBuffer; } -export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification') { +export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification' | 'reaction') { const sound = defaultStore.state[`sound_${type}`]; if (_DEV_) console.log('play', type, sound); if (sound.type == null) return; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 12660e9e8d..f2ed4e7c0b 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -411,6 +411,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: { type: 'syuilo/square-pico', volume: 1 }, }, + sound_reaction: { + where: 'device', + default: { type: 'syuilo/bubble2', volume: 1 }, + }, })); // TODO: 他のタブと永続化されたstateを同期 From 755ca9785779eaa120c7e4372b61979362e7ceb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 26 Nov 2023 13:20:46 +0900 Subject: [PATCH 37/41] =?UTF-8?q?fix(frontend):=20=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E9=9F=B3=E3=81=8C=E3=81=BB=E3=81=BC=E5=90=8C=E6=99=82=E3=81=AB?= =?UTF-8?q?=E9=B3=B4=E3=81=A3=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AF=E5=86=8D?= =?UTF-8?q?=E7=94=9F=E3=82=92=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=EF=BC=88=E9=9F=B3=E5=89=B2?= =?UTF-8?q?=E3=82=8C=E9=98=B2=E6=AD=A2=EF=BC=89=20(#12433)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (fix) 通知音がダブって音割れしないように * Update Changelog --- CHANGELOG.md | 1 + packages/frontend/src/scripts/sound.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f02d462b2..7a1bdd233d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367 - Fix: コードエディタが正しく表示されない問題を修正 - Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正 +- Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正 ### Server - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 4d7ef9bdee..47ec4171af 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -7,6 +7,7 @@ import { defaultStore } from '@/store.js'; const ctx = new AudioContext(); const cache = new Map(); +let canPlay = true; export const soundsTypes = [ null, @@ -82,8 +83,15 @@ export async function loadAudio(file: string, useCache = true) { export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification' | 'reaction') { const sound = defaultStore.state[`sound_${type}`]; if (_DEV_) console.log('play', type, sound); - if (sound.type == null) return; - playFile(sound.type, sound.volume); + if (sound.type == null || !canPlay) return; + + canPlay = false; + playFile(sound.type, sound.volume).then(() => { + // ごく短時間に音が重複しないように + setTimeout(() => { + canPlay = true; + }, 25); + }); } export async function playFile(file: string, volume: number) { From ccb951f11e8cc3c884eef799bef82d09f138d28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Sun, 26 Nov 2023 14:38:34 +0900 Subject: [PATCH 38/41] chore: create AudioContext when it is needed (#12460) --- packages/frontend/src/scripts/sound.ts | 5 ++++- packages/frontend/src/widgets/WidgetJobQueue.vue | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts index 47ec4171af..d28d629227 100644 --- a/packages/frontend/src/scripts/sound.ts +++ b/packages/frontend/src/scripts/sound.ts @@ -5,7 +5,7 @@ import { defaultStore } from '@/store.js'; -const ctx = new AudioContext(); +let ctx: AudioContext; const cache = new Map(); let canPlay = true; @@ -65,6 +65,9 @@ export const soundsTypes = [ ] as const; export async function loadAudio(file: string, useCache = true) { + if (ctx == null) { + ctx = new AudioContext(); + } if (useCache && cache.has(file)) { return cache.get(file)!; } diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue index fa82997570..8c990e8e49 100644 --- a/packages/frontend/src/widgets/WidgetJobQueue.vue +++ b/packages/frontend/src/widgets/WidgetJobQueue.vue @@ -58,6 +58,7 @@ import { useStream } from '@/stream.js'; import number from '@/filters/number.js'; import * as sound from '@/scripts/sound.js'; import { deepClone } from '@/scripts/clone.js'; +import { defaultStore } from '@/store.js'; const name = 'jobQueue'; @@ -102,7 +103,9 @@ const prev = reactive({} as typeof current); let jammedAudioBuffer: AudioBuffer | null = $ref(null); let jammedSoundNodePlaying: boolean = $ref(false); -sound.loadAudio('syuilo/queue-jammed').then(buf => jammedAudioBuffer = buf); +if (defaultStore.state.sound_masterVolume) { + sound.loadAudio('syuilo/queue-jammed').then(buf => jammedAudioBuffer = buf); +} for (const domain of ['inbox', 'deliver']) { prev[domain] = deepClone(current[domain]); From c9503da8f8e4a67cbe7ba88722b3c3893f9ab4e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?= <46447427+samunohito@users.noreply.github.com> Date: Sun, 26 Nov 2023 16:12:02 +0900 Subject: [PATCH 39/41] =?UTF-8?q?=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89?= =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=AB=E3=80=8C=E3=82=B5=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=82=92=E5=87=BA=E5=8A=9B=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=80=8D=E3=81=A8=E3=80=8CMisskey=E3=81=8C=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=83=86=E3=82=A3=E3=83=96=E3=81=AA=E6=99=82=E3=81=AE=E3=81=BF?= =?UTF-8?q?=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89=E3=82=92=E5=87=BA=E5=8A=9B?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=80=8D=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1234?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com> --- CHANGELOG.md | 1 + locales/index.d.ts | 2 ++ locales/ja-JP.yml | 2 ++ packages/frontend/src/pages/settings/sounds.vue | 9 +++++++++ packages/frontend/src/scripts/sound.ts | 17 ++++++++++++++++- packages/frontend/src/store.ts | 8 ++++++++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a1bdd233d..bf3fecb5b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - 例: `$[unixtime 1701356400]` - Enhance: プラグインでエラーが発生した場合のハンドリングを強化 - Enhance: 細かなUIのブラッシュアップ +- Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加 - Fix: 効果音が再生されるとデバイスで再生している動画や音声が停止する問題を修正 #12339 - Fix: デッキに表示されたチャンネルの表示先チャンネルを切り替えた際、即座に反映されない問題を修正 #12236 - Fix: プラグインでノートの表示を書き換えられない問題を修正 diff --git a/locales/index.d.ts b/locales/index.d.ts index 6e9fe311f1..6097ae130e 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -547,6 +547,8 @@ export interface Locale { "popout": string; "volume": string; "masterVolume": string; + "notUseSound": string; + "useSoundOnlyWhenActive": string; "details": string; "chooseEmoji": string; "unableToProcess": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0b051b6190..1f6695b3e3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -544,6 +544,8 @@ showInPage: "ページで表示" popout: "ポップアウト" volume: "音量" masterVolume: "マスター音量" +notUseSound: "サウンドを出力しない" +useSoundOnlyWhenActive: "Misskeyがアクティブな時のみサウンドを出力する" details: "詳細" chooseEmoji: "絵文字を選択" unableToProcess: "操作を完了できません" diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index 244bb1e0e2..05e4b0d14c 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -5,6 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only