diff --git a/CHANGELOG.md b/CHANGELOG.md index a77a95c02..9bfb12d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,12 @@ - Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正 - Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正 - Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正 +- Fix: Renoteのキーボードショートカットが機能していなかった問題を修正 +- Fix: 投稿フォームでアンケートの日時指定をした状態で再読み込みをすると期日が復元されない問題を修正 +- Fix: アンケートを設定したノートを「削除して編集」をするとアンケートの期日が引き継がれず、リセットされてしまう問題を修正 +- Fix: デッキのプロファイル作成時に名前を空にできる問題を修正 +- Fix: テーマ作成時に名称が空欄でも作成できてしまう問題を修正 +- Fix: プラグインで`Plugin:register_note_post_interruptor`を使用すると、ノートが投稿できなくなる問題を修正 - Enhance: ページ遷移時にPlayerを閉じるように - Fix: iOSで大きな画像を変換してアップロードできない問題を修正 diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 5b4c8cb44..6a7267166 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -54,9 +54,9 @@ export interface MainEventTypes { reply: Packed<'Note'>; renote: Packed<'Note'>; follow: Packed<'UserDetailedNotMe'>; - followed: Packed<'User'>; - unfollow: Packed<'User'>; - meUpdated: Packed<'User'>; + followed: Packed<'UserDetailed' | 'UserLite'>; + unfollow: Packed<'UserDetailed'>; + meUpdated: Packed<'UserDetailed'>; pageEvent: { pageId: MiPage['id']; event: string; diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index 8d0a89f2d..b1cde2f6e 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -94,6 +94,29 @@ type ToJsonSchema = { }; export function getJsonSchema(schema: S): ToJsonSchema>> { + const unflatten = (str: string, parent: Record) => { + const keys = str.split('.'); + const key = keys.shift(); + const nextKey = keys[0]; + + if (key == null) return; + + if (parent.properties[key] == null) { + parent.properties[key] = nextKey ? { + type: 'object', + properties: {}, + required: [], + } : { + type: 'array', + items: { + type: 'number', + }, + }; + } + + if (nextKey) unflatten(keys.join('.'), parent.properties[key] as Record); + }; + const jsonSchema = { type: 'object', properties: {} as Record, @@ -101,10 +124,7 @@ export function getJsonSchema(schema: S): ToJsonSchema>>; diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 1777e2cf5..03cd58917 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -164,7 +164,7 @@ export class NoteEntityService implements OnModuleInit { return { multiple: poll.multiple, - expiresAt: poll.expiresAt, + expiresAt: poll.expiresAt?.toISOString() ?? null, choices, }; } diff --git a/packages/backend/src/misc/clone.ts b/packages/backend/src/misc/clone.ts index 9d20deac3..52e6c825f 100644 --- a/packages/backend/src/misc/clone.ts +++ b/packages/backend/src/misc/clone.ts @@ -6,7 +6,7 @@ // structredCloneが遅いため // SEE: http://var.blog.jp/archives/86038606.html -type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[]; +type Cloneable = string | number | boolean | null | undefined | { [key: string]: Cloneable } | Cloneable[]; export function deepClone(x: T): T { if (typeof x === 'object') { @@ -14,7 +14,7 @@ export function deepClone(x: T): T { if (Array.isArray(x)) return x.map(deepClone) as T; const obj = {} as Record; for (const [k, v] of Object.entries(x)) { - obj[k] = deepClone(v); + obj[k] = v === undefined ? undefined : deepClone(v); } return obj as T; } else { diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index b4f054171..0dd8f15d9 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -25,7 +25,7 @@ import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; import { packedHashtagSchema } from '@/models/json-schema/hashtag.js'; import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js'; -import { packedPageSchema } from '@/models/json-schema/page.js'; +import { packedPageSchema, packedPageBlockSchema } from '@/models/json-schema/page.js'; import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js'; import { packedChannelSchema } from '@/models/json-schema/channel.js'; import { packedAntennaSchema } from '@/models/json-schema/antenna.js'; @@ -37,7 +37,7 @@ import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/jso import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; import { packedSigninSchema } from '@/models/json-schema/signin.js'; -import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js'; +import { packedRoleLiteSchema, packedRoleSchema, packedRolePoliciesSchema } from '@/models/json-schema/role.js'; import { packedAdSchema } from '@/models/json-schema/ad.js'; import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js'; @@ -67,6 +67,7 @@ export const refs = { Hashtag: packedHashtagSchema, InviteCode: packedInviteCodeSchema, Page: packedPageSchema, + PageBlock: packedPageBlockSchema, Channel: packedChannelSchema, QueueCount: packedQueueCountSchema, Antenna: packedAntennaSchema, @@ -79,12 +80,16 @@ export const refs = { Signin: packedSigninSchema, RoleLite: packedRoleLiteSchema, Role: packedRoleSchema, + RolePolicies: packedRolePoliciesSchema, ReversiGameLite: packedReversiGameLiteSchema, ReversiGameDetailed: packedReversiGameDetailedSchema, }; export type Packed = SchemaType; +export type KeyOf = PropertiesToUnion; +type PropertiesToUnion

= p['properties'] extends NonNullable ? keyof p['properties'] : never; + type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any'; type StringDefToType = T extends 'null' ? null : diff --git a/packages/backend/src/models/Announcement.ts b/packages/backend/src/models/Announcement.ts index 8f8be88fe..c2d9e9878 100644 --- a/packages/backend/src/models/Announcement.ts +++ b/packages/backend/src/models/Announcement.ts @@ -38,7 +38,7 @@ export class MiAnnouncement { length: 256, nullable: false, default: 'info', }) - public icon: string; + public icon: 'info' | 'warning' | 'error' | 'success'; // normal ... お知らせページ掲載 // banner ... お知らせページ掲載 + バナー表示 @@ -47,7 +47,7 @@ export class MiAnnouncement { length: 256, nullable: false, default: 'normal', }) - public display: string; + public display: 'normal' | 'banner' | 'dialog'; @Column('boolean', { default: false, diff --git a/packages/backend/src/models/json-schema/announcement.ts b/packages/backend/src/models/json-schema/announcement.ts index 78a98872b..57fd7d605 100644 --- a/packages/backend/src/models/json-schema/announcement.ts +++ b/packages/backend/src/models/json-schema/announcement.ts @@ -37,10 +37,12 @@ export const packedAnnouncementSchema = { icon: { type: 'string', optional: false, nullable: false, + enum: ['info', 'warning', 'error', 'success'], }, display: { type: 'string', optional: false, nullable: false, + enum: ['dialog', 'normal', 'banner'], }, needConfirmationToRead: { type: 'boolean', diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 2b7722129..929f697e8 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -69,6 +69,7 @@ export const packedNoteSchema = { visibility: { type: 'string', optional: false, nullable: false, + enum: ['public', 'home', 'followers', 'specified'], }, mentions: { type: 'array', @@ -117,6 +118,48 @@ export const packedNoteSchema = { poll: { type: 'object', optional: true, nullable: true, + properties: { + expiresAt: { + type: 'string', + optional: true, nullable: true, + format: 'date-time', + }, + multiple: { + type: 'boolean', + optional: false, nullable: false, + }, + choices: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + isVoted: { + type: 'boolean', + optional: false, nullable: false, + }, + text: { + type: 'string', + optional: false, nullable: false, + }, + votes: { + type: 'number', + optional: false, nullable: false, + }, + }, + }, + }, + }, + }, + emojis: { + type: 'object', + optional: true, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'string', + }], + }, }, channelId: { type: 'string', @@ -162,9 +205,23 @@ export const packedNoteSchema = { type: 'string', optional: false, nullable: true, }, + reactionEmojis: { + type: 'object', + optional: false, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'string', + }], + }, + }, reactions: { type: 'object', optional: false, nullable: false, + additionalProperties: { + anyOf: [{ + type: 'number', + }], + }, }, renoteCount: { type: 'number', @@ -196,7 +253,7 @@ export const packedNoteSchema = { }, myReaction: { - type: 'object', + type: 'string', optional: true, nullable: true, }, }, diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index c6d6e8431..6286950de 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -5,7 +5,7 @@ import { notificationTypes } from '@/types.js'; -export const packedNotificationSchema = { +const baseSchema = { type: 'object', properties: { id: { @@ -23,68 +23,368 @@ export const packedNotificationSchema = { optional: false, nullable: false, enum: [...notificationTypes, 'reaction:grouped', 'renote:grouped'], }, - user: { - type: 'object', - ref: 'UserLite', - optional: true, nullable: true, - }, - userId: { - type: 'string', - optional: true, nullable: true, - format: 'id', - }, - note: { - type: 'object', - ref: 'Note', - optional: true, nullable: true, - }, - reaction: { - type: 'string', - optional: true, nullable: true, - }, - achievement: { - type: 'string', - optional: true, nullable: false, - }, - body: { - type: 'string', - optional: true, nullable: true, - }, - header: { - type: 'string', - optional: true, nullable: true, - }, - icon: { - type: 'string', - optional: true, nullable: true, - }, - reactions: { - type: 'array', - optional: true, nullable: true, - items: { - type: 'object', - properties: { - user: { - type: 'object', - ref: 'UserLite', - optional: false, nullable: false, - }, - reaction: { - type: 'string', - optional: false, nullable: false, - }, - }, - required: ['user', 'reaction'], + }, +} as const; + +export const packedNotificationSchema = { + type: 'object', + oneOf: [{ + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['note'], }, - }, - users: { - type: 'array', - optional: true, nullable: true, - items: { + user: { type: 'object', ref: 'UserLite', optional: false, nullable: false, }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, }, - }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['mention'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reply'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['renote'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['quote'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reaction'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + reaction: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['pollEnded'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['follow'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['receiveFollowRequest'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['followRequestAccepted'], + }, + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['roleAssigned'], + }, + role: { + type: 'object', + ref: 'Role', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['achievementEarned'], + }, + achievement: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['app'], + }, + body: { + type: 'string', + optional: false, nullable: false, + }, + header: { + type: 'string', + optional: false, nullable: false, + }, + icon: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['reaction:grouped'], + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + reactions: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + properties: { + user: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + reaction: { + type: 'string', + optional: false, nullable: false, + }, + }, + required: ['user', 'reaction'], + }, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['renote:grouped'], + }, + note: { + type: 'object', + ref: 'Note', + optional: false, nullable: false, + }, + users: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + ref: 'UserLite', + optional: false, nullable: false, + }, + }, + }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['test'], + }, + }, + }], } as const; diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts index 9baacd688..402db76e5 100644 --- a/packages/backend/src/models/json-schema/page.ts +++ b/packages/backend/src/models/json-schema/page.ts @@ -3,6 +3,107 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +const blockBaseSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + }, + type: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; + +const textBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['text'], + }, + text: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; + +const sectionBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['section'], + }, + title: { + type: 'string', + optional: false, nullable: false, + }, + children: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'PageBlock', + }, + }, + }, +} as const; + +const imageBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['image'], + }, + fileId: { + type: 'string', + optional: false, nullable: true, + }, + }, +} as const; + +const noteBlockSchema = { + type: 'object', + properties: { + ...blockBaseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['note'], + }, + detailed: { + type: 'boolean', + optional: false, nullable: false, + }, + note: { + type: 'string', + optional: false, nullable: true, + }, + }, +} as const; + +export const packedPageBlockSchema = { + type: 'object', + oneOf: [ + textBlockSchema, + sectionBlockSchema, + imageBlockSchema, + noteBlockSchema, + ], +} as const; + export const packedPageSchema = { type: 'object', properties: { @@ -38,6 +139,7 @@ export const packedPageSchema = { items: { type: 'object', optional: false, nullable: false, + ref: 'PageBlock', }, }, variables: { diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts index b0c6804bb..55348d4f3 100644 --- a/packages/backend/src/models/json-schema/role.ts +++ b/packages/backend/src/models/json-schema/role.ts @@ -1,26 +1,103 @@ -const rolePolicyValue = { +export const packedRolePoliciesSchema = { type: 'object', + optional: false, nullable: false, properties: { - value: { - oneOf: [ - { - type: 'integer', - optional: false, nullable: false, - }, - { - type: 'boolean', - optional: false, nullable: false, - }, - ], + gtlAvailable: { + type: 'boolean', + optional: false, nullable: false, }, - priority: { + ltlAvailable: { + type: 'boolean', + optional: false, nullable: false, + }, + canPublicNote: { + type: 'boolean', + optional: false, nullable: false, + }, + canInvite: { + type: 'boolean', + optional: false, nullable: false, + }, + inviteLimit: { type: 'integer', optional: false, nullable: false, }, - useDefault: { + inviteLimitCycle: { + type: 'integer', + optional: false, nullable: false, + }, + inviteExpirationTime: { + type: 'integer', + optional: false, nullable: false, + }, + canManageCustomEmojis: { type: 'boolean', optional: false, nullable: false, }, + canManageAvatarDecorations: { + type: 'boolean', + optional: false, nullable: false, + }, + canSearchNotes: { + type: 'boolean', + optional: false, nullable: false, + }, + canUseTranslator: { + type: 'boolean', + optional: false, nullable: false, + }, + canHideAds: { + type: 'boolean', + optional: false, nullable: false, + }, + driveCapacityMb: { + type: 'integer', + optional: false, nullable: false, + }, + alwaysMarkNsfw: { + type: 'boolean', + optional: false, nullable: false, + }, + pinLimit: { + type: 'integer', + optional: false, nullable: false, + }, + antennaLimit: { + type: 'integer', + optional: false, nullable: false, + }, + wordMuteLimit: { + type: 'integer', + optional: false, nullable: false, + }, + webhookLimit: { + type: 'integer', + optional: false, nullable: false, + }, + clipLimit: { + type: 'integer', + optional: false, nullable: false, + }, + noteEachClipsLimit: { + type: 'integer', + optional: false, nullable: false, + }, + userListLimit: { + type: 'integer', + optional: false, nullable: false, + }, + userEachUserListsLimit: { + type: 'integer', + optional: false, nullable: false, + }, + rateLimitFactor: { + type: 'integer', + optional: false, nullable: false, + }, + avatarDecorationLimit: { + type: 'integer', + optional: false, nullable: false, + }, }, } as const; @@ -121,31 +198,28 @@ export const packedRoleSchema = { policies: { type: 'object', optional: false, nullable: false, - properties: { - pinLimit: rolePolicyValue, - canInvite: rolePolicyValue, - clipLimit: rolePolicyValue, - canHideAds: rolePolicyValue, - inviteLimit: rolePolicyValue, - antennaLimit: rolePolicyValue, - gtlAvailable: rolePolicyValue, - ltlAvailable: rolePolicyValue, - webhookLimit: rolePolicyValue, - canPublicNote: rolePolicyValue, - userListLimit: rolePolicyValue, - wordMuteLimit: rolePolicyValue, - alwaysMarkNsfw: rolePolicyValue, - canSearchNotes: rolePolicyValue, - driveCapacityMb: rolePolicyValue, - rateLimitFactor: rolePolicyValue, - inviteLimitCycle: rolePolicyValue, - noteEachClipsLimit: rolePolicyValue, - inviteExpirationTime: rolePolicyValue, - canManageCustomEmojis: rolePolicyValue, - userEachUserListsLimit: rolePolicyValue, - canManageAvatarDecorations: rolePolicyValue, - canUseTranslator: rolePolicyValue, - avatarDecorationLimit: rolePolicyValue, + additionalProperties: { + anyOf: [{ + type: 'object', + properties: { + value: { + oneOf: [ + { + type: 'integer', + }, + { + type: 'boolean', + }, + ], + }, + priority: { + type: 'integer', + }, + useDefault: { + type: 'boolean', + }, + }, + }], }, }, usersCount: { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 6a0d43b1a..7447513a9 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -590,104 +590,7 @@ export const packedMeDetailedOnlySchema = { 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, - }, - avatarDecorationLimit: { - type: 'number', - nullable: false, optional: false, - }, - }, + ref: 'RolePolicies', }, //#region secrets email: { diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 4a88216d0..75e6395f1 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -4,8 +4,7 @@ */ import { permissions } from 'misskey-js'; -import type { Schema } from '@/misc/json-schema.js'; -import { RolePolicies } from '@/core/RoleService.js'; +import type { KeyOf, Schema } from '@/misc/json-schema.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; @@ -776,7 +775,7 @@ interface IEndpointMetaBase { */ readonly requireAdmin?: boolean; - readonly requireRolePolicy?: keyof RolePolicies; + readonly requireRolePolicy?: KeyOf<'RolePolicies'>; /** * 引っ越し済みのユーザーによるリクエストを禁止するか diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 529e82678..e1d347348 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -303,6 +303,11 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + policies: { + type: 'object', + optional: false, nullable: false, + ref: 'RolePolicies', + }, }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/retention.ts b/packages/backend/src/server/api/endpoints/retention.ts index dac6d6540..263169313 100644 --- a/packages/backend/src/server/api/endpoints/retention.ts +++ b/packages/backend/src/server/api/endpoints/retention.ts @@ -14,6 +14,32 @@ export const meta = { requireCredential: false, res: { + type: 'array', + items: { + type: 'object', + properties: { + createdAt: { + type: 'string', + format: 'date-time', + }, + users: { + type: 'number', + }, + data: { + type: 'object', + additionalProperties: { + anyOf: [{ + type: 'number', + }], + }, + }, + }, + required: [ + 'createdAt', + 'users', + 'data', + ], + }, }, allowGet: true, diff --git a/packages/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts index 7d9335cc5..936e74dec 100644 --- a/packages/frontend/@types/global.d.ts +++ b/packages/frontend/@types/global.d.ts @@ -16,3 +16,8 @@ declare const _DATA_TRANSFER_DECK_COLUMN_: string; // for dev-mode declare const _LANGS_FULL_: string[][]; + +// TagCanvas +interface Window { + TagCanvas: any; +} diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index 7814681ea..39745a97c 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -39,7 +39,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - user: Misskey.entities.User; + user: Misskey.entities.UserDetailed; initialComment?: string; }>(); diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index 1137eaf97..ff8a9fa1a 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only - {{ c.text }} - + {{ c.text }} + {{ c.text }}

{{ button.text }} @@ -20,19 +20,19 @@ SPDX-License-Identifier: AGPL-3.0-only - + - + - + - + @@ -42,8 +42,8 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkSelect from '@/components/MkSelect.vue'; -import { AsUiComponent } from '@/scripts/aiscript/ui.js'; +import { AsUiComponent, AsUiRoot, AsUiPostFormButton } from '@/scripts/aiscript/ui.js'; import MkFolder from '@/components/MkFolder.vue'; import MkPostForm from '@/components/MkPostForm.vue'; @@ -85,20 +85,32 @@ const props = withDefaults(defineProps<{ const c = props.component; function g(id) { - return props.components.find(x => x.value.id === id).value; + const v = props.components.find(x => x.value.id === id)?.value; + if (v) return v; + + return { + id: 'dummy', + type: 'root', + children: [], + } as AsUiRoot; } -const valueForSwitch = ref(c.default ?? false); +const valueForSwitch = ref('default' in c && typeof c.default === 'boolean' ? c.default : false); function onSwitchUpdate(v) { valueForSwitch.value = v; - if (c.onChange) c.onChange(v); + if ('onChange' in c && c.onChange) { + c.onChange(v as never); + } } function openPostForm() { + const form = (c as AsUiPostFormButton).form; + if (!form) return; + os.post({ - initialText: c.form.text, - initialCw: c.form.cw, + initialText: form.text, + initialCw: form.cw, instant: true, }); } diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 8d4631968..70de6a851 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index f60c721ea..7aa08cf51 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only