From b941c896aa5512240de9121a1850d55aa5f8b68b 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: Wed, 14 Jan 2026 14:02:50 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor(frontend):=20MkRadios=E3=81=AE?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E3=82=92props=E3=81=8B=E3=82=89=E8=A1=8C?= =?UTF-8?q?=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=20(#16597)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(frontend): MkRadiosの指定をpropsから行うように * spdx * fix lint * fix: mkradiosを動的slotsに対応させる * fix: remove comment [ci skip] * fix lint * fix lint * migrate * rename * fix * fix * fix types * remove unused imports * fix * wip --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- .../core/entities/ReversiGameEntityService.ts | 8 +- .../src/models/json-schema/reversi-game.ts | 2 + packages/frontend/src/components/MkDialog.vue | 3 +- packages/frontend/src/components/MkForm.vue | 14 +- .../src/components/MkImageEffectorFxForm.vue | 6 +- packages/frontend/src/components/MkMenu.vue | 15 +- packages/frontend/src/components/MkRadio.vue | 6 +- packages/frontend/src/components/MkRadios.vue | 197 ++++++++++-------- packages/frontend/src/components/MkSelect.vue | 2 +- .../src/components/MkServerSetupWizard.vue | 52 +++-- .../MkUserAnnouncementEditDialog.vue | 26 ++- .../frontend/src/composables/use-mkselect.ts | 3 +- packages/frontend/src/os.ts | 3 +- packages/frontend/src/pages/admin/ads.vue | 24 ++- .../src/pages/admin/announcements.vue | 26 ++- .../src/pages/admin/bot-protection.vue | 18 +- .../frontend/src/pages/admin/branding.vue | 10 +- .../frontend/src/pages/admin/security.vue | 14 +- .../frontend/src/pages/admin/settings.vue | 12 +- packages/frontend/src/pages/channels.vue | 17 +- .../src/pages/reversi/game.setting.vue | 45 ++-- packages/frontend/src/pages/search.note.vue | 28 ++- packages/frontend/src/pages/search.user.vue | 14 +- packages/frontend/src/pages/settings/deck.vue | 32 ++- .../src/pages/settings/emoji-palette.vue | 53 +++-- .../frontend/src/pages/settings/navbar.vue | 10 +- .../src/pages/settings/preferences.vue | 102 +++++---- .../pages/settings/statusbar.statusbar.vue | 16 +- packages/frontend/src/preferences/def.ts | 4 +- packages/frontend/src/store.ts | 4 +- packages/frontend/src/types/menu.ts | 3 +- packages/frontend/src/types/option-value.ts | 6 + packages/frontend/src/utility/form.ts | 8 +- packages/misskey-js/src/autogen/types.ts | 6 +- 34 files changed, 505 insertions(+), 284 deletions(-) create mode 100644 packages/frontend/src/types/option-value.ts diff --git a/packages/backend/src/core/entities/ReversiGameEntityService.ts b/packages/backend/src/core/entities/ReversiGameEntityService.ts index df042e75c1..21099bad3e 100644 --- a/packages/backend/src/core/entities/ReversiGameEntityService.ts +++ b/packages/backend/src/core/entities/ReversiGameEntityService.ts @@ -14,6 +14,10 @@ import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; import { UserEntityService } from './UserEntityService.js'; +function assertBw(bw: string): bw is Packed<'ReversiGameDetailed'>['bw'] { + return ['random', '1', '2'].includes(bw); +} + @Injectable() export class ReversiGameEntityService { constructor( @@ -58,7 +62,7 @@ export class ReversiGameEntityService { surrenderedUserId: game.surrenderedUserId, timeoutUserId: game.timeoutUserId, black: game.black, - bw: game.bw, + bw: assertBw(game.bw) ? game.bw : 'random', isLlotheo: game.isLlotheo, canPutEverywhere: game.canPutEverywhere, loopedBoard: game.loopedBoard, @@ -116,7 +120,7 @@ export class ReversiGameEntityService { surrenderedUserId: game.surrenderedUserId, timeoutUserId: game.timeoutUserId, black: game.black, - bw: game.bw, + bw: assertBw(game.bw) ? game.bw : 'random', isLlotheo: game.isLlotheo, canPutEverywhere: game.canPutEverywhere, loopedBoard: game.loopedBoard, diff --git a/packages/backend/src/models/json-schema/reversi-game.ts b/packages/backend/src/models/json-schema/reversi-game.ts index cb37200384..378ae41cb5 100644 --- a/packages/backend/src/models/json-schema/reversi-game.ts +++ b/packages/backend/src/models/json-schema/reversi-game.ts @@ -81,6 +81,7 @@ export const packedReversiGameLiteSchema = { bw: { type: 'string', optional: false, nullable: false, + enum: ['random', '1', '2'], }, noIrregularRules: { type: 'boolean', @@ -199,6 +200,7 @@ export const packedReversiGameDetailedSchema = { bw: { type: 'string', optional: false, nullable: false, + enum: ['random', '1', '2'], }, noIrregularRules: { type: 'boolean', diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index bea0392d2d..4801b412f8 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -52,7 +52,8 @@ import MkModal from '@/components/MkModal.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; -import type { MkSelectItem, OptionValue } from '@/components/MkSelect.vue'; +import type { MkSelectItem } from '@/components/MkSelect.vue'; +import type { OptionValue } from '@/types/option-value.js'; import { useMkSelect } from '@/composables/use-mkselect.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/components/MkForm.vue b/packages/frontend/src/components/MkForm.vue index 3d4724e6b7..1ece0ad4c3 100644 --- a/packages/frontend/src/components/MkForm.vue +++ b/packages/frontend/src/components/MkForm.vue @@ -26,9 +26,8 @@ SPDX-License-Identifier: AGPL-3.0-only - + - @@ -60,6 +59,7 @@ import MkButton from '@/components/MkButton.vue'; import MkRadios from '@/components/MkRadios.vue'; import { i18n } from '@/i18n.js'; import type { MkSelectItem } from '@/components/MkSelect.vue'; +import type { MkRadiosOption } from '@/components/MkRadios.vue'; import type { Form, EnumFormItem, RadioFormItem } from '@/utility/form.js'; const props = defineProps<{ @@ -113,7 +113,13 @@ function getMkSelectDef(def: EnumFormItem): MkSelectItem[] { }); } -function getRadioKey(e: RadioFormItem['options'][number]) { - return typeof e.value === 'string' ? e.value : JSON.stringify(e.value); +function getRadioOptionsDef(def: RadioFormItem): MkRadiosOption[] { + return def.options.map((v) => { + if (typeof v === 'string') { + return { value: v, label: v }; + } else { + return { value: v.value, label: v.label }; + } + }); } diff --git a/packages/frontend/src/components/MkImageEffectorFxForm.vue b/packages/frontend/src/components/MkImageEffectorFxForm.vue index 51485977a9..723b5f093e 100644 --- a/packages/frontend/src/components/MkImageEffectorFxForm.vue +++ b/packages/frontend/src/components/MkImageEffectorFxForm.vue @@ -28,13 +28,9 @@ SPDX-License-Identifier: AGPL-3.0-only - + -
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 22d5802596..b618dab6b2 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -323,9 +323,20 @@ async function showRadioOptions(item: MenuRadio, ev: MouseEvent | PointerEvent | type: 'radioOption', text: key, action: () => { - item.ref = value; + if ('value' in item.ref) { + item.ref.value = value; + } else { + // @ts-expect-error リアクティビティは保たれる + item.ref = value; + } }, - active: computed(() => item.ref === value), + active: computed(() => { + if ('value' in item.ref) { + return item.ref.value === value; + } else { + return item.ref === value; + } + }), }; }); diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index a7d77dd118..19ba90052c 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -24,8 +24,9 @@ SPDX-License-Identifier: AGPL-3.0-only
- - diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue index f130145e36..6f6957d504 100644 --- a/packages/frontend/src/components/MkSelect.vue +++ b/packages/frontend/src/components/MkSelect.vue @@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only - - diff --git a/packages/frontend/src/components/MkRadios.vue b/packages/frontend/src/components/MkRadios.vue index 43957a0673..e2210e858e 100644 --- a/packages/frontend/src/components/MkRadios.vue +++ b/packages/frontend/src/components/MkRadios.vue @@ -8,15 +8,27 @@ SPDX-License-Identifier: AGPL-3.0-only
+
- -
+ + + + +
@@ -26,8 +38,9 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
+
@@ -51,8 +64,6 @@ export type MkRadiosOption = { diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index e06ea50453..0efd1a2e28 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -33,15 +33,6 @@ SPDX-License-Identifier: AGPL-3.0-only
- - From bd81a6c8adb45067bee9582f84855a60a962e92b 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: Wed, 14 Jan 2026 14:45:45 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor(frontend):=20any=E3=82=92=E9=99=A4?= =?UTF-8?q?=E5=8E=BB2=20(#17092)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * fix types * fix --- packages/backend/src/misc/json-schema.ts | 2 + packages/backend/src/models/Meta.ts | 6 ++- .../backend/src/models/json-schema/meta.ts | 23 +++++++++- .../src/server/api/endpoints/admin/meta.ts | 3 +- .../server/api/endpoints/admin/update-meta.ts | 20 +++++++-- .../src/components/MkAchievements.vue | 6 +-- .../src/components/MkAutocomplete.vue | 17 +++---- .../src/components/MkExtensionInstaller.vue | 9 ++-- .../src/components/MkNotification.vue | 2 +- .../MkPushNotificationAllowButton.vue | 2 +- .../src/components/MkRetentionLineChart.vue | 17 ++++--- .../src/components/MkServerSetupWizard.vue | 2 +- .../src/components/MkWidgetSettingsDialog.vue | 5 ++- .../src/components/global/MkCondensedLine.vue | 2 +- packages/frontend/src/instance.ts | 6 --- .../frontend/src/pages/admin/branding.vue | 12 ++--- packages/frontend/src/pages/auth.form.vue | 8 +++- packages/frontend/src/pages/flash/flash.vue | 2 +- .../frontend/src/pages/settings/plugin.vue | 2 +- .../src/pages/settings/preferences.vue | 4 +- packages/frontend/src/plugin.ts | 6 +-- packages/frontend/src/pref-migrate.ts | 9 ++-- packages/frontend/src/store.ts | 6 +-- packages/frontend/src/utility/autocomplete.ts | 45 +++++++++++-------- .../frontend/src/widgets/WidgetAichan.vue | 2 +- .../frontend/src/widgets/WidgetTimeline.vue | 10 ++--- .../frontend/src/widgets/WidgetTrends.vue | 2 +- packages/frontend/src/widgets/index.ts | 2 + packages/frontend/src/widgets/widget.ts | 3 +- packages/misskey-js/etc/misskey-js.api.md | 4 ++ packages/misskey-js/src/autogen/models.ts | 1 + packages/misskey-js/src/autogen/types.ts | 17 +++++-- 32 files changed, 164 insertions(+), 93 deletions(-) diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index 3fa49e3cd1..cf233defd9 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -64,6 +64,7 @@ import { packedMetaDetailedOnlySchema, packedMetaDetailedSchema, packedMetaLiteSchema, + packedMetaClientOptionsSchema, } from '@/models/json-schema/meta.js'; import { packedUserWebhookSchema } from '@/models/json-schema/user-webhook.js'; import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js'; @@ -135,6 +136,7 @@ export const refs = { MetaLite: packedMetaLiteSchema, MetaDetailedOnly: packedMetaDetailedOnlySchema, MetaDetailed: packedMetaDetailedSchema, + MetaClientOptions: packedMetaClientOptionsSchema, UserWebhook: packedUserWebhookSchema, SystemWebhook: packedSystemWebhookSchema, AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema, diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index a6f68194c5..620853450c 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -725,7 +725,11 @@ export class MiMeta { @Column('jsonb', { default: { }, }) - public clientOptions: Record; + public clientOptions: { + entrancePageStyle: 'classic' | 'simple'; + showTimelineForVisitor: boolean; + showActivitiesForVisitor: boolean; + }; } export type SoftwareSuspension = { diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts index a0e7d490b3..0c3ec141bc 100644 --- a/packages/backend/src/models/json-schema/meta.ts +++ b/packages/backend/src/models/json-schema/meta.ts @@ -72,8 +72,7 @@ export const packedMetaLiteSchema = { optional: false, nullable: true, }, clientOptions: { - type: 'object', - optional: false, nullable: false, + ref: 'MetaClientOptions', }, disableRegistration: { type: 'boolean', @@ -397,3 +396,23 @@ export const packedMetaDetailedSchema = { }, ], } as const; + +export const packedMetaClientOptionsSchema = { + type: 'object', + optional: false, nullable: false, + properties: { + entrancePageStyle: { + type: 'string', + enum: ['classic', 'simple'], + optional: false, nullable: false, + }, + showTimelineForVisitor: { + type: 'boolean', + optional: false, nullable: false, + }, + showActivitiesForVisitor: { + type: 'boolean', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 2c7f793584..5beed3a7e8 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -428,8 +428,7 @@ export const meta = { optional: false, nullable: true, }, clientOptions: { - type: 'object', - optional: false, nullable: false, + ref: 'MetaClientOptions', }, description: { type: 'string', diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index b3c2cecc67..7a8dfc4555 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -3,7 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Injectable, Inject } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; import type { MiMeta } from '@/models/Meta.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -67,7 +68,14 @@ export const paramDef = { description: { type: 'string', nullable: true }, defaultLightTheme: { type: 'string', nullable: true }, defaultDarkTheme: { type: 'string', nullable: true }, - clientOptions: { type: 'object', nullable: false }, + clientOptions: { + type: 'object', nullable: false, + properties: { + entrancePageStyle: { type: 'string', nullable: false, enum: ['classic', 'simple'] }, + showTimelineForVisitor: { type: 'boolean', nullable: false }, + showActivitiesForVisitor: { type: 'boolean', nullable: false }, + }, + }, cacheRemoteFiles: { type: 'boolean' }, cacheRemoteSensitiveFiles: { type: 'boolean' }, emailRequiredForSignup: { type: 'boolean' }, @@ -217,6 +225,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( + @Inject(DI.meta) + private serverSettings: MiMeta, + private metaService: MetaService, private moderationLogService: ModerationLogService, ) { @@ -329,7 +340,10 @@ export default class extends Endpoint { // eslint- } if (ps.clientOptions !== undefined) { - set.clientOptions = ps.clientOptions; + set.clientOptions = { + ...serverSettings.clientOptions, + ...ps.clientOptions, + }; } if (ps.cacheRemoteFiles !== undefined) { diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index d0e138c229..fe6415eabb 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -23,13 +23,13 @@ SPDX-License-Identifier: AGPL-3.0-only
- {{ (i18n.ts._achievements._types as any)['_' + achievement.name].title }} + {{ i18n.ts._achievements._types[`_${achievement.name}`].title }}
-
{{ withDescription ? (i18n.ts._achievements._types as any)['_' + achievement.name].description : '???' }}
-
{{ (i18n.ts._achievements._types as any)['_' + achievement.name].flavor }}
+
{{ withDescription ? i18n.ts._achievements._types[`_${achievement.name}`].description : '???' }}
+
{{ (i18n.ts._achievements._types[`_${achievement.name}`] as { flavor: string; }).flavor }}