From 496cbb02ffa6c74949f62d77feaef4de6252ea58 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Fri, 29 Aug 2025 12:07:25 +0900 Subject: [PATCH] fix os.select --- packages/frontend/src/components/MkDialog.vue | 4 +- .../frontend/src/components/MkPostForm.vue | 10 ++--- .../src/components/MkRoleSelectDialog.vue | 8 ++-- packages/frontend/src/os.ts | 44 +++---------------- packages/frontend/src/pages/admin-user.vue | 14 +++--- .../frontend/src/pages/admin/roles.role.vue | 10 ++--- .../pages/avatar-decoration-edit-dialog.vue | 8 ++-- .../frontend/src/pages/emoji-edit-dialog.vue | 8 ++-- .../frontend/src/pages/page-editor/common.ts | 11 ++--- .../els/page-editor.el.section.vue | 2 +- .../src/pages/page-editor/page-editor.vue | 3 +- .../frontend/src/pages/settings/navbar.vue | 4 +- .../src/pages/settings/preferences.vue | 9 ++-- packages/frontend/src/preferences/manager.ts | 14 +++--- packages/frontend/src/preferences/utility.ts | 2 +- packages/frontend/src/ui/deck.vue | 2 +- .../frontend/src/ui/deck/channel-column.vue | 7 +-- .../src/ui/deck/role-timeline-column.vue | 7 +-- packages/frontend/src/ui/deck/tl-column.vue | 8 ++-- .../frontend/src/utility/get-user-menu.ts | 20 ++++----- .../frontend/src/widgets/WidgetUserList.vue | 8 ++-- 21 files changed, 85 insertions(+), 118 deletions(-) diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index f8a7be9b68..705301a6a6 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -47,7 +47,7 @@ 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 } from '@/components/MkSelect.vue'; +import type { MkSelectItem, OptionValue } from '@/components/MkSelect.vue'; import { useMkSelect } from '@/composables/use-mkselect.js'; import { i18n } from '@/i18n.js'; @@ -62,7 +62,7 @@ type Input = { type Select = { items: MkSelectItem[]; - default: string | null; + default: OptionValue | null; }; type Result = string | number | true | null; diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 56683b8f8c..c344496aaa 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -567,11 +567,11 @@ async function toggleReactionAcceptance() { const select = await os.select({ title: i18n.ts.reactionAcceptance, items: [ - { value: null, text: i18n.ts.all }, - { value: 'likeOnlyForRemote' as const, text: i18n.ts.likeOnlyForRemote }, - { value: 'nonSensitiveOnly' as const, text: i18n.ts.nonSensitiveOnly }, - { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, - { value: 'likeOnly' as const, text: i18n.ts.likeOnly }, + { value: null, label: i18n.ts.all }, + { value: 'likeOnlyForRemote' as const, label: i18n.ts.likeOnlyForRemote }, + { value: 'nonSensitiveOnly' as const, label: i18n.ts.nonSensitiveOnly }, + { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, label: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, + { value: 'likeOnly' as const, label: i18n.ts.likeOnly }, ], default: reactionAcceptance.value, }); diff --git a/packages/frontend/src/components/MkRoleSelectDialog.vue b/packages/frontend/src/components/MkRoleSelectDialog.vue index f1cc98def4..937804703d 100644 --- a/packages/frontend/src/components/MkRoleSelectDialog.vue +++ b/packages/frontend/src/components/MkRoleSelectDialog.vue @@ -102,12 +102,12 @@ async function addRole() { const items = roles.value .filter(r => r.isPublic) .filter(r => !selectedRoleIds.value.includes(r.id)) - .map(r => ({ text: r.name, value: r })); + .map(r => ({ label: r.name, value: r.id })); - const { canceled, result: role } = await os.select({ items }); - if (canceled || role == null) return; + const { canceled, result: roleId } = await os.select({ items }); + if (canceled || roleId == null) return; - selectedRoleIds.value.push(role.id); + selectedRoleIds.value.push(roleId); } async function removeRole(roleId: string) { diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 56a2b8d269..a38de4576e 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -14,6 +14,7 @@ import type { Form, GetFormResultType } from '@/utility/form.js'; import type { MenuItem } from '@/types/menu.js'; import type { PostFormProps } from '@/types/post-form.js'; import type { UploaderFeatures } from '@/composables/use-uploader.js'; +import type { MkSelectItem, OptionValue } from '@/components/MkSelect.vue'; import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue'; import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue'; import { misskeyApi } from '@/utility/misskey-api.js'; @@ -502,50 +503,15 @@ export function authenticateDialog(): Promise<{ }); } -type SelectItem = { - value: C; - text: string; -}; - -// default が指定されていたら result は null になり得ないことを保証する overload function -export function select(props: { +export function select(props: { title?: string; text?: string; - default: string; - items: (SelectItem | { - sectionTitle: string; - items: SelectItem[]; - } | undefined)[]; + default?: D; + items: (MkSelectItem | undefined)[]; }): Promise<{ canceled: true; result: undefined; } | { - canceled: false; result: C; -}>; -export function select(props: { - title?: string; - text?: string; - default?: string | null; - items: (SelectItem | { - sectionTitle: string; - items: SelectItem[]; - } | undefined)[]; -}): Promise<{ - canceled: true; result: undefined; -} | { - canceled: false; result: C | null; -}>; -export function select(props: { - title?: string; - text?: string; - default?: string | null; - items: (SelectItem | { - sectionTitle: string; - items: SelectItem[]; - } | undefined)[]; -}): Promise<{ - canceled: true; result: undefined; -} | { - canceled: false; result: C | null; + canceled: false; result: Exclude extends null ? C | null : C; }> { return new Promise(resolve => { const { dispose } = popup(MkDialog, { diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 8322c8377e..970b54f68d 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -442,22 +442,22 @@ async function assignRole() { const { canceled, result: roleId } = await os.select({ title: i18n.ts._role.chooseRoleToAssign, - items: roles.map(r => ({ text: r.name, value: r.id })), + items: roles.map(r => ({ label: r.name, value: r.id })), }); - if (canceled) return; + if (canceled || roleId == null) return; const { canceled: canceled2, result: period } = await os.select({ title: i18n.ts.period + ': ' + roles.find(r => r.id === roleId)!.name, items: [{ - value: 'indefinitely', text: i18n.ts.indefinitely, + value: 'indefinitely', label: i18n.ts.indefinitely, }, { - value: 'oneHour', text: i18n.ts.oneHour, + value: 'oneHour', label: i18n.ts.oneHour, }, { - value: 'oneDay', text: i18n.ts.oneDay, + value: 'oneDay', label: i18n.ts.oneDay, }, { - value: 'oneWeek', text: i18n.ts.oneWeek, + value: 'oneWeek', label: i18n.ts.oneWeek, }, { - value: 'oneMonth', text: i18n.ts.oneMonth, + value: 'oneMonth', label: i18n.ts.oneMonth, }], default: 'indefinitely', }); diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index c6c3165828..514277943a 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -115,15 +115,15 @@ async function assign() { const { canceled: canceled2, result: period } = await os.select({ title: i18n.ts.period + ': ' + role.name, items: [{ - value: 'indefinitely', text: i18n.ts.indefinitely, + value: 'indefinitely', label: i18n.ts.indefinitely, }, { - value: 'oneHour', text: i18n.ts.oneHour, + value: 'oneHour', label: i18n.ts.oneHour, }, { - value: 'oneDay', text: i18n.ts.oneDay, + value: 'oneDay', label: i18n.ts.oneDay, }, { - value: 'oneWeek', text: i18n.ts.oneWeek, + value: 'oneWeek', label: i18n.ts.oneWeek, }, { - value: 'oneMonth', text: i18n.ts.oneMonth, + value: 'oneMonth', label: i18n.ts.oneMonth, }], default: 'indefinitely', }); diff --git a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue index ddc4e89ef1..a8ce527523 100644 --- a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue +++ b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue @@ -101,12 +101,12 @@ async function addRole() { const roles = await misskeyApi('admin/roles/list'); const currentRoleIds = rolesThatCanBeUsedThisDecoration.value.map(x => x.id); - const { canceled, result: role } = await os.select({ - items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), + const { canceled, result: roleId } = await os.select({ + items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ label: r.name, value: r.id })), }); - if (canceled || role == null) return; + if (canceled || roleId == null) return; - rolesThatCanBeUsedThisDecoration.value.push(role); + rolesThatCanBeUsedThisDecoration.value.push(roles.find(r => r.id === roleId)!); } async function removeRole(role, ev) { diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 201ce003f0..23e234d069 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -135,12 +135,12 @@ async function addRole() { const roles = await misskeyApi('admin/roles/list'); const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id); - const { canceled, result: role } = await os.select({ - items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), + const { canceled, result: roleId } = await os.select({ + items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ label: r.name, value: r.id })), }); - if (canceled || role == null) return; + if (canceled || roleId == null) return; - rolesThatCanBeUsedThisEmojiAsReaction.value.push(role); + rolesThatCanBeUsedThisEmojiAsReaction.value.push(roles.find(r => r.id === roleId)!); } async function removeRole(role: Misskey.entities.RoleLite, ev: Event) { diff --git a/packages/frontend/src/pages/page-editor/common.ts b/packages/frontend/src/pages/page-editor/common.ts index 420c8fc967..64cd9cde7a 100644 --- a/packages/frontend/src/pages/page-editor/common.ts +++ b/packages/frontend/src/pages/page-editor/common.ts @@ -4,12 +4,13 @@ */ import { i18n } from '@/i18n.js'; +import type { MkSelectItem } from '@/components/MkSelect.vue'; export function getPageBlockList() { return [ - { value: 'section', text: i18n.ts._pages.blocks.section }, - { value: 'text', text: i18n.ts._pages.blocks.text }, - { value: 'image', text: i18n.ts._pages.blocks.image }, - { value: 'note', text: i18n.ts._pages.blocks.note }, - ]; + { value: 'section', label: i18n.ts._pages.blocks.section }, + { value: 'text', label: i18n.ts._pages.blocks.text }, + { value: 'image', label: i18n.ts._pages.blocks.image }, + { value: 'note', label: i18n.ts._pages.blocks.note }, + ] as const satisfies MkSelectItem[]; } diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue index 11f83b6ec6..86a5a3898d 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue @@ -71,7 +71,7 @@ async function add() { title: i18n.ts._pages.chooseBlock, items: getPageBlockList(), }); - if (canceled) return; + if (canceled || type == null) return; const id = genId(); children.value.push({ id, type }); diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index 6c7f3c3b0f..5893d08166 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -210,11 +210,10 @@ async function duplicate() { async function add() { const { canceled, result: type } = await os.select({ - type: null, title: i18n.ts._pages.chooseBlock, items: getPageBlockList(), }); - if (canceled) return; + if (canceled || type == null) return; const id = genId(); content.value.push({ id, type }); diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index f7c634b42e..c8cbc0977f 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -86,9 +86,9 @@ async function addItem() { const { canceled, result: item } = await os.select({ title: i18n.ts.addItem, items: [...menu.map(k => ({ - value: k, text: navbarItemDef[k].title, + value: k, label: navbarItemDef[k].title, })), { - value: '-', text: i18n.ts.divider, + value: '-', label: i18n.ts.divider, }], }); if (canceled || item == null) return; diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index fe730945a6..431cd67a7a 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -1010,16 +1010,15 @@ function removeEmojiIndex(lang: string) { async function setPinnedList() { const lists = await misskeyApi('users/lists/list'); - const { canceled, result: list } = await os.select({ + const { canceled, result: listId } = await os.select({ title: i18n.ts.selectList, items: lists.map(x => ({ - value: x, text: x.name, + value: x.id, label: x.name, })), }); - if (canceled) return; - if (list == null) return; + if (canceled || listId == null) return; - prefer.commit('pinnedUserLists', [list]); + prefer.commit('pinnedUserLists', [lists.find((x) => x.id === listId)!]); } function removePinnedList() { diff --git a/packages/frontend/src/preferences/manager.ts b/packages/frontend/src/preferences/manager.ts index d26d590851..b6d3d55a5f 100644 --- a/packages/frontend/src/preferences/manager.ts +++ b/packages/frontend/src/preferences/manager.ts @@ -447,16 +447,16 @@ export class PreferencesManager { title: i18n.ts.preferenceSyncConflictTitle, text: i18n.ts.preferenceSyncConflictText, items: [...(mergedValue !== undefined ? [{ - text: i18n.ts.preferenceSyncConflictChoiceMerge, - value: 'merge', + label: i18n.ts.preferenceSyncConflictChoiceMerge, + value: 'merge' as const, }] : []), { - text: i18n.ts.preferenceSyncConflictChoiceServer, - value: 'remote', + label: i18n.ts.preferenceSyncConflictChoiceServer, + value: 'remote' as const, }, { - text: i18n.ts.preferenceSyncConflictChoiceDevice, - value: 'local', + label: i18n.ts.preferenceSyncConflictChoiceDevice, + value: 'local' as const, }, { - text: i18n.ts.preferenceSyncConflictChoiceCancel, + label: i18n.ts.preferenceSyncConflictChoiceCancel, value: null, }], default: mergedValue !== undefined ? 'merge' : 'remote', diff --git a/packages/frontend/src/preferences/utility.ts b/packages/frontend/src/preferences/utility.ts index 80949f4971..33d379509a 100644 --- a/packages/frontend/src/preferences/utility.ts +++ b/packages/frontend/src/preferences/utility.ts @@ -187,7 +187,7 @@ export async function restoreFromCloudBackup() { const select = await os.select({ title: i18n.ts._preferencesBackup.selectBackupToRestore, items: backups.map(backup => ({ - text: backup.name, + label: backup.name, value: backup.name, })), }); diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 9f6d8267f7..e2ee4b658e 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -168,7 +168,7 @@ const addColumn = async (ev) => { const { canceled, result: column } = await os.select({ title: i18n.ts._deck.addColumn, items: columnTypes.map(column => ({ - value: column, text: i18n.ts._deck._columns[column], + value: column, label: i18n.ts._deck._columns[column], })), }); if (canceled || column == null) return; diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue index c02499e2d7..35ca9f5cc6 100644 --- a/packages/frontend/src/ui/deck/channel-column.vue +++ b/packages/frontend/src/ui/deck/channel-column.vue @@ -58,14 +58,15 @@ watch(soundSetting, v => { async function setChannel() { const channels = await favoritedChannelsCache.fetch(); - const { canceled, result: chosenChannel } = await os.select({ + const { canceled, result: chosenChannelId } = await os.select({ title: i18n.ts.selectChannel, items: channels.map(x => ({ - value: x, text: x.name, + value: x.id, label: x.name, })), default: props.column.channelId, }); - if (canceled || chosenChannel == null) return; + if (canceled || chosenChannelId == null) return; + const chosenChannel = channels.find(x => x.id === chosenChannelId)!; updateColumn(props.column.id, { channelId: chosenChannel.id, timelineNameCache: chosenChannel.name, diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue index 0aafeb56d7..beb679169c 100644 --- a/packages/frontend/src/ui/deck/role-timeline-column.vue +++ b/packages/frontend/src/ui/deck/role-timeline-column.vue @@ -49,14 +49,15 @@ watch(soundSetting, v => { async function setRole() { const roles = (await misskeyApi('roles/list')).filter(x => x.isExplorable); - const { canceled, result: role } = await os.select({ + const { canceled, result: roleId } = await os.select({ title: i18n.ts.role, items: roles.map(x => ({ - value: x, text: x.name, + value: x.id, label: x.name, })), default: props.column.roleId, }); - if (canceled || role == null) return; + if (canceled || roleId == null) return; + const role = roles.find(x => x.id === roleId)!; updateColumn(props.column.id, { roleId: role.id, timelineNameCache: role.name, diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 37814f0914..afaa08e6d0 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -96,13 +96,13 @@ async function setType() { const { canceled, result: src } = await os.select({ title: i18n.ts.timeline, items: [{ - value: 'home' as const, text: i18n.ts._timelines.home, + value: 'home', label: i18n.ts._timelines.home, }, { - value: 'local' as const, text: i18n.ts._timelines.local, + value: 'local', label: i18n.ts._timelines.local, }, { - value: 'social' as const, text: i18n.ts._timelines.social, + value: 'social', label: i18n.ts._timelines.social, }, { - value: 'global' as const, text: i18n.ts._timelines.global, + value: 'global', label: i18n.ts._timelines.global, }], }); if (canceled) { diff --git a/packages/frontend/src/utility/get-user-menu.ts b/packages/frontend/src/utility/get-user-menu.ts index d4407dadec..3c39bf789a 100644 --- a/packages/frontend/src/utility/get-user-menu.ts +++ b/packages/frontend/src/utility/get-user-menu.ts @@ -37,15 +37,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router const { canceled, result: period } = await os.select({ title: i18n.ts.mutePeriod, items: [{ - value: 'indefinitely', text: i18n.ts.indefinitely, + value: 'indefinitely', label: i18n.ts.indefinitely, }, { - value: 'tenMinutes', text: i18n.ts.tenMinutes, + value: 'tenMinutes', label: i18n.ts.tenMinutes, }, { - value: 'oneHour', text: i18n.ts.oneHour, + value: 'oneHour', label: i18n.ts.oneHour, }, { - value: 'oneDay', text: i18n.ts.oneDay, + value: 'oneDay', label: i18n.ts.oneDay, }, { - value: 'oneWeek', text: i18n.ts.oneWeek, + value: 'oneWeek', label: i18n.ts.oneWeek, }], default: 'indefinitely', }); @@ -313,15 +313,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router const { canceled, result: period } = await os.select({ title: i18n.ts.period + ': ' + r.name, items: [{ - value: 'indefinitely', text: i18n.ts.indefinitely, + value: 'indefinitely', label: i18n.ts.indefinitely, }, { - value: 'oneHour', text: i18n.ts.oneHour, + value: 'oneHour', label: i18n.ts.oneHour, }, { - value: 'oneDay', text: i18n.ts.oneDay, + value: 'oneDay', label: i18n.ts.oneDay, }, { - value: 'oneWeek', text: i18n.ts.oneWeek, + value: 'oneWeek', label: i18n.ts.oneWeek, }, { - value: 'oneMonth', text: i18n.ts.oneMonth, + value: 'oneMonth', label: i18n.ts.oneMonth, }], default: 'indefinitely', }); diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue index d87ea5ade2..9e914fa648 100644 --- a/packages/frontend/src/widgets/WidgetUserList.vue +++ b/packages/frontend/src/widgets/WidgetUserList.vue @@ -67,15 +67,15 @@ const fetching = ref(true); async function chooseList() { const lists = await misskeyApi('users/lists/list'); - const { canceled, result: list } = await os.select({ + const { canceled, result: listId } = await os.select({ title: i18n.ts.selectList, items: lists.map(x => ({ - value: x, text: x.name, + value: x.id, label: x.name, })), default: widgetProps.listId, }); - if (canceled || list == null) return; - + if (canceled || listId == null) return; + const list = lists.find(x => x.id === listId)!; widgetProps.listId = list.id; save(); fetch();