diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2024-10-31 13:46:42 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-31 13:46:42 +0900 |
| commit | 17d9aca5a7ec6149a8dbf0c1607c81ab188e7015 (patch) | |
| tree | 78752526e76f518be29ebe5bdbd0176cd2087796 /packages | |
| parent | fix(frontend): 一部のノート表示で設定にかかわらずセンシ... (diff) | |
| download | sharkey-17d9aca5a7ec6149a8dbf0c1607c81ab188e7015.tar.gz sharkey-17d9aca5a7ec6149a8dbf0c1607c81ab188e7015.tar.bz2 sharkey-17d9aca5a7ec6149a8dbf0c1607c81ab188e7015.zip | |
refactor(frontend): asとanyをすぐなおせる範囲で除去 (#14848)
* refactor(frontend): できるだけanyを除去
* refactor
* lint
* fix
* remove unused
* Update packages/frontend/src/components/MkReactionsViewer.details.vue
* Update packages/frontend/src/components/MkUsersTooltip.vue
---------
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages')
56 files changed, 250 insertions, 192 deletions
diff --git a/packages/frontend/src/components/MkAntennaEditor.vue b/packages/frontend/src/components/MkAntennaEditor.vue index 2386ba6fa7..e622d57f1e 100644 --- a/packages/frontend/src/components/MkAntennaEditor.vue +++ b/packages/frontend/src/components/MkAntennaEditor.vue @@ -160,7 +160,7 @@ async function deleteAntenna() { function addUser() { os.selectUser({ includeSelf: true }).then(user => { users.value = users.value.trim(); - users.value += '\n@' + Misskey.acct.toString(user as any); + users.value += '\n@' + Misskey.acct.toString(user); users.value = users.value.trim(); }); } diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue index 99580df5e2..c470042b79 100644 --- a/packages/frontend/src/components/MkChannelPreview.vue +++ b/packages/frontend/src/components/MkChannelPreview.vue @@ -47,11 +47,12 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, ref, watch } from 'vue'; +import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; const props = defineProps<{ - channel: Record<string, any>; + channel: Misskey.entities.Channel; }>(); const getLastReadedAt = (): number | null => { diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 22130d4fab..b095a1cd4a 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkSelect> <div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> - <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> + <MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason != null" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> <MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> </div> <div v-if="actions" :class="$style.buttons"> @@ -98,7 +98,7 @@ const props = withDefaults(defineProps<{ text: string; primary?: boolean, danger?: boolean, - callback: (...args: any[]) => void; + callback: (...args: unknown[]) => void; }[]; showOkButton?: boolean; showCancelButton?: boolean; diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 910b73c798..8be6d6f53d 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -157,7 +157,7 @@ const ilFilesObserver = new IntersectionObserver( (entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles(), ); -const sortModeSelect = ref('+createdAt'); +const sortModeSelect = ref<NonNullable<Misskey.entities.DriveFilesRequest['sort']>>('+createdAt'); watch(folder, () => emit('cd', folder.value)); watch(sortModeSelect, () => { @@ -198,7 +198,7 @@ function onStreamDriveFolderDeleted(folderId: string) { removeFolder(folderId); } -function onDragover(ev: DragEvent): any { +function onDragover(ev: DragEvent) { if (!ev.dataTransfer) return; // ドラッグ元が自分自身の所有するアイテムだったら @@ -243,7 +243,7 @@ function onDragleave() { draghover.value = false; } -function onDrop(ev: DragEvent): any { +function onDrop(ev: DragEvent) { draghover.value = false; if (!ev.dataTransfer) return; @@ -332,7 +332,7 @@ function createFolder() { title: i18n.ts.createFolder, placeholder: i18n.ts.folderName, }).then(({ canceled, result: name }) => { - if (canceled) return; + if (canceled || name == null) return; misskeyApi('drive/folders/create', { name: name, parentId: folder.value ? folder.value.id : undefined, diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index f4caa730bf..b418ed3ae6 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -90,7 +90,7 @@ function computeButtonTitle(ev: MouseEvent): void { elm.title = getEmojiName(emoji); } -function nestedChosen(emoji: any, ev: MouseEvent) { +function nestedChosen(emoji: string, ev: MouseEvent) { emit('chosen', emoji, ev); } </script> diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 219950f135..8187d991e7 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -409,7 +409,7 @@ function computeButtonTitle(ev: MouseEvent): void { elm.title = getEmojiName(emoji); } -function chosen(emoji: any, ev?: MouseEvent) { +function chosen(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef, ev?: MouseEvent) { const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; if (el) { const rect = el.getBoundingClientRect(); @@ -426,7 +426,7 @@ function chosen(emoji: any, ev?: MouseEvent) { // 最近使った絵文字更新 if (!pinned.value?.includes(key)) { let recents = defaultStore.state.recentlyUsedEmojis; - recents = recents.filter((emoji: any) => emoji !== key); + recents = recents.filter((emoji) => emoji !== key); recents.unshift(key); defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); } diff --git a/packages/frontend/src/components/MkExtensionInstaller.vue b/packages/frontend/src/components/MkExtensionInstaller.vue index b41604b2c3..d59b20435e 100644 --- a/packages/frontend/src/components/MkExtensionInstaller.vue +++ b/packages/frontend/src/components/MkExtensionInstaller.vue @@ -73,7 +73,7 @@ export type Extension = { author: string; description?: string; permissions?: string[]; - config?: Record<string, any>; + config?: Record<string, unknown>; }; } | { type: 'theme'; diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue index e01ff86c5a..08817fd6a8 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue'; +import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs, InputHTMLAttributes } from 'vue'; import { debounce } from 'throttle-debounce'; import MkButton from '@/components/MkButton.vue'; import { useInterval } from '@@/js/use-interval.js'; @@ -53,7 +53,7 @@ import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js'; const props = defineProps<{ modelValue: string | number | null; - type?: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; + type?: InputHTMLAttributes['type']; required?: boolean; readonly?: boolean; disabled?: boolean; @@ -64,8 +64,8 @@ const props = defineProps<{ mfmAutocomplete?: boolean | SuggestionType[], autocapitalize?: string; spellcheck?: boolean; - inputmode?: 'none' | 'text' | 'search' | 'email' | 'url' | 'numeric' | 'tel' | 'decimal'; - step?: any; + inputmode?: InputHTMLAttributes['inputmode']; + step?: InputHTMLAttributes['step']; datalist?: string[]; min?: number; max?: number; diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue index 47a9c79e45..d07827d11a 100644 --- a/packages/frontend/src/components/MkNotificationSelectWindow.vue +++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue @@ -53,7 +53,7 @@ const props = withDefaults(defineProps<{ const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); -const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any); +const typesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as TypesMap); function ok() { emit('done', { diff --git a/packages/frontend/src/components/MkObjectView.value.vue b/packages/frontend/src/components/MkObjectView.value.vue index dabdd324fd..7fa8c23c6c 100644 --- a/packages/frontend/src/components/MkObjectView.value.vue +++ b/packages/frontend/src/components/MkObjectView.value.vue @@ -39,7 +39,7 @@ import number from '@/filters/number.js'; import XValue from '@/components/MkObjectView.value.vue'; const props = defineProps<{ - value: any; + value: unknown; }>(); const collapsed = reactive({}); @@ -50,19 +50,19 @@ if (isObject(props.value)) { } } -function isObject(v): boolean { +function isObject(v: unknown): v is Record<PropertyKey, unknown> { return typeof v === 'object' && !Array.isArray(v) && v !== null; } -function isArray(v): boolean { +function isArray(v: unknown): v is unknown[] { return Array.isArray(v); } -function isEmpty(v): boolean { +function isEmpty(v: unknown): v is Record<PropertyKey, never> | never[] { return (isArray(v) && v.length === 0) || (isObject(v) && Object.keys(v).length === 0); } -function collapsable(v): boolean { +function collapsable(v: unknown): boolean { return (isObject(v) || isArray(v)) && !isEmpty(v); } </script> diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 4777da2848..02c84df447 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -58,7 +58,7 @@ const windowRouter = routerFactory(props.initialPath); const contents = shallowRef<HTMLElement | null>(null); const pageMetadata = ref<null | PageMetadata>(null); const windowEl = shallowRef<InstanceType<typeof MkWindow>>(); -const history = ref<{ path: string; key: any; }[]>([{ +const history = ref<{ path: string; key: string; }[]>([{ path: windowRouter.getCurrentPath(), key: windowRouter.getCurrentKey(), }]); diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue index 26c251a8d2..df664e49f7 100644 --- a/packages/frontend/src/components/MkPopupMenu.vue +++ b/packages/frontend/src/components/MkPopupMenu.vue @@ -19,7 +19,7 @@ defineProps<{ items: MenuItem[]; align?: 'center' | string; width?: number; - src?: any; + src?: HTMLElement | null; returnFocusTo?: HTMLElement | null; }>(); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index b6b80082d3..f2fe048449 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -129,25 +129,13 @@ import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js'; +import type { PostFormProps } from '@/types/post-form.js'; const $i = signinRequired(); const modal = inject('modal'); -const props = withDefaults(defineProps<{ - reply?: Misskey.entities.Note; - renote?: Misskey.entities.Note; - channel?: Misskey.entities.Channel; // TODO - mention?: Misskey.entities.User; - specified?: Misskey.entities.UserDetailed; - initialText?: string; - initialCw?: string; - initialVisibility?: (typeof Misskey.noteVisibilities)[number]; - initialFiles?: Misskey.entities.DriveFile[]; - initialLocalOnly?: boolean; - initialVisibleUsers?: Misskey.entities.UserDetailed[]; - initialNote?: Misskey.entities.Note; - instant?: boolean; +const props = withDefaults(defineProps<PostFormProps & { fixed?: boolean; autofocus?: boolean; freezeAfterPosted?: boolean; @@ -955,8 +943,8 @@ function showActions(ev: MouseEvent) { action.handler({ text: text.value, cw: cw.value, - }, (key, value: any) => { - if (typeof key !== 'string') return; + }, (key, value) => { + if (typeof key !== 'string' || typeof value !== 'string') return; if (key === 'text') { text.value = value; } if (key === 'cw') { useCw.value = value !== null; cw.value = value; } }); diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index ee7038df64..56e026aa3c 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div v-show="props.modelValue.length != 0" :class="$style.root"> <Sortable :modelValue="props.modelValue" :class="$style.files" itemKey="id" :animation="150" :delay="100" :delayOnTouchOnly="true" @update:modelValue="v => emit('update:modelValue', v)"> - <template #item="{element}"> + <template #item="{ element }"> <div :class="$style.file" role="button" @@ -38,14 +38,14 @@ import type { MenuItem } from '@/types/menu.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); const props = defineProps<{ - modelValue: any[]; + modelValue: Misskey.entities.DriveFile[]; detachMediaFn?: (id: string) => void; }>(); const mock = inject<boolean>('mock', false); const emit = defineEmits<{ - (ev: 'update:modelValue', value: any[]): void; + (ev: 'update:modelValue', value: Misskey.entities.DriveFile[]): void; (ev: 'detach', id: string): void; (ev: 'changeSensitive', file: Misskey.entities.DriveFile, isSensitive: boolean): void; (ev: 'changeName', file: Misskey.entities.DriveFile, newName: string): void; @@ -113,7 +113,7 @@ async function rename(file) { }); } -async function describe(file) { +async function describe(file: Misskey.entities.DriveFile) { if (mock) return; const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue index d6bca29050..32d8df1504 100644 --- a/packages/frontend/src/components/MkPostFormDialog.vue +++ b/packages/frontend/src/components/MkPostFormDialog.vue @@ -11,23 +11,11 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef } from 'vue'; -import * as Misskey from 'misskey-js'; import MkModal from '@/components/MkModal.vue'; import MkPostForm from '@/components/MkPostForm.vue'; +import type { PostFormProps } from '@/types/post-form.js'; -const props = withDefaults(defineProps<{ - reply?: Misskey.entities.Note; - renote?: Misskey.entities.Note; - channel?: any; // TODO - mention?: Misskey.entities.User; - specified?: Misskey.entities.UserDetailed; - initialText?: string; - initialCw?: string; - initialVisibility?: (typeof Misskey.noteVisibilities)[number]; - initialFiles?: Misskey.entities.DriveFile[]; - initialLocalOnly?: boolean; - initialVisibleUsers?: Misskey.entities.UserDetailed[]; - initialNote?: Misskey.entities.Note; +const props = withDefaults(defineProps<PostFormProps & { instant?: boolean; fixed?: boolean; autofocus?: boolean; diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index e735d9fff8..f16c8f6c2a 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -24,17 +24,17 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> -<script lang="ts" setup> +<script lang="ts" setup generic="T extends unknown"> import { computed } from 'vue'; const props = defineProps<{ - modelValue: any; - value: any; + modelValue: T; + value: T; disabled?: boolean; }>(); const emit = defineEmits<{ - (ev: 'update:modelValue', value: any): void; + (ev: 'update:modelValue', value: T): void; }>(); const checked = computed(() => props.modelValue === props.value); diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue index f4c3643ba8..d24e0b15bf 100644 --- a/packages/frontend/src/components/MkReactionsViewer.details.vue +++ b/packages/frontend/src/components/MkReactionsViewer.details.vue @@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { } from 'vue'; +import * as Misskey from 'misskey-js'; import { getEmojiName } from '@@/js/emojilist.js'; import MkTooltip from './MkTooltip.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; @@ -30,7 +31,7 @@ import MkReactionIcon from '@/components/MkReactionIcon.vue'; defineProps<{ showing: boolean; reaction: string; - users: any[]; // TODO + users: Misskey.entities.UserLite[]; count: number; targetElement: HTMLElement; }>(); diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index 6e7a875dec..0caaed6f39 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -28,11 +28,38 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> -<script lang="ts" setup> -import { } from 'vue'; +<script lang="ts"> +export type SuperMenuDef = { + title?: string; + items: ({ + type: 'a'; + href: string; + target?: string; + icon?: string; + text: string; + danger?: boolean; + active?: boolean; + } | { + type: 'button'; + icon?: string; + text: string; + danger?: boolean; + active?: boolean; + action: (ev: MouseEvent) => void; + } | { + type: 'link'; + to: string; + icon?: string; + text: string; + danger?: boolean; + active?: boolean; + })[]; +}; +</script> +<script lang="ts" setup> defineProps<{ - def: any[]; + def: SuperMenuDef[]; grid?: boolean; }>(); </script> diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index c287effadc..f0da8fd3f2 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -180,7 +180,7 @@ window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLa sensitive.value = info.sensitive ?? false; }); -function adjustTweetHeight(message: any) { +function adjustTweetHeight(message: MessageEvent) { if (message.origin !== 'https://platform.twitter.com') return; const embed = message.data?.['twttr.embed']; if (embed?.method !== 'twttr.private.resize') return; @@ -193,14 +193,16 @@ function openPlayer(): void { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkYouTubePlayer.vue')), { url: requestUrl.href, }, { - // TODO + closed: () => { + dispose(); + }, }); } -(window as any).addEventListener('message', adjustTweetHeight); +window.addEventListener('message', adjustTweetHeight); onUnmounted(() => { - (window as any).removeEventListener('message', adjustTweetHeight); + window.removeEventListener('message', adjustTweetHeight); }); </script> diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue index 7a2b5f5ddc..7f0266d1d3 100644 --- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue +++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue @@ -62,9 +62,11 @@ import MkTextarea from '@/components/MkTextarea.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkRadios from '@/components/MkRadios.vue'; +type AdminAnnouncementType = Misskey.entities.AdminAnnouncementsCreateRequest & { id: string; } + const props = defineProps<{ user: Misskey.entities.User, - announcement?: Misskey.entities.Announcement, + announcement?: Required<AdminAnnouncementType>, }>(); const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null); @@ -75,7 +77,7 @@ const display = ref(props.announcement ? props.announcement.display : 'dialog'); const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false); const emit = defineEmits<{ - (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, + (ev: 'done', v: { deleted?: boolean; updated?: AdminAnnouncementType; created?: AdminAnnouncementType; }): void, (ev: 'closed'): void }>(); @@ -88,7 +90,7 @@ async function done() { display: display.value, needConfirmationToRead: needConfirmationToRead.value, userId: props.user.id, - }; + } satisfies Misskey.entities.AdminAnnouncementsCreateRequest; if (props.announcement) { await os.apiWithDialog('admin/announcements/update', { diff --git a/packages/frontend/src/components/MkUsersTooltip.vue b/packages/frontend/src/components/MkUsersTooltip.vue index 054a503257..0cb7f22e93 100644 --- a/packages/frontend/src/components/MkUsersTooltip.vue +++ b/packages/frontend/src/components/MkUsersTooltip.vue @@ -16,12 +16,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import * as Misskey from 'misskey-js'; import MkTooltip from './MkTooltip.vue'; defineProps<{ showing: boolean; - users: any[]; // TODO + users: Misskey.entities.UserLite[]; count: number; targetElement: HTMLElement; }>(); diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index 056b6a37ed..ed7e3867ce 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -60,6 +60,13 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; +type WindowButton = { + title: string; + icon: string; + onClick: () => void; + highlighted?: boolean; +}; + const minHeight = 50; const minWidth = 250; @@ -87,8 +94,8 @@ const props = withDefaults(defineProps<{ mini?: boolean; front?: boolean; contextmenu?: MenuItem[] | null; - buttonsLeft?: any[]; - buttonsRight?: any[]; + buttonsLeft?: WindowButton[]; + buttonsRight?: WindowButton[]; }>(), { initialWidth: 400, initialHeight: null, diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue index 5226c61d68..821f07510b 100644 --- a/packages/frontend/src/components/form/suspense.vue +++ b/packages/frontend/src/components/form/suspense.vue @@ -18,19 +18,19 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> -<script lang="ts" setup> +<script lang="ts" setup generic="T extends unknown"> import { ref, watch } from 'vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - p: () => Promise<any>; + p: () => Promise<T>; }>(); const pending = ref(true); const resolved = ref(false); const rejected = ref(false); -const result = ref<any>(null); +const result = ref<T | null>(null); const process = () => { if (props.p == null) { diff --git a/packages/frontend/src/components/global/MkMfm.ts b/packages/frontend/src/components/global/MkMfm.ts index 0d4ae8cacb..0d138d1f1c 100644 --- a/packages/frontend/src/components/global/MkMfm.ts +++ b/packages/frontend/src/components/global/MkMfm.ts @@ -467,8 +467,8 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven } default: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - console.error('unrecognized ast type:', (token as any).type); + // @ts-expect-error 存在しないASTタイプ + console.error('unrecognized ast type:', token.type); return []; } diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index 25f853453a..965bd6f0bc 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -36,6 +36,8 @@ interface RouteDefWithRedirect extends RouteDefBase { export type RouteDef = RouteDefWithComponent | RouteDefWithRedirect; +export type RouterFlag = 'forcePage'; + type ParsedPath = (string | { name: string; startsWith?: string; @@ -107,7 +109,7 @@ export interface IRouter extends EventEmitter<RouterEvent> { current: Resolved; currentRef: ShallowRef<Resolved>; currentRoute: ShallowRef<RouteDef>; - navHook: ((path: string, flag?: any) => boolean) | null; + navHook: ((path: string, flag?: RouterFlag) => boolean) | null; /** * ルートの初期化(eventListenerの定義後に必ず呼び出すこと) @@ -116,11 +118,11 @@ export interface IRouter extends EventEmitter<RouterEvent> { resolve(path: string): Resolved | null; - getCurrentPath(): any; + getCurrentPath(): string; getCurrentKey(): string; - push(path: string, flag?: any): void; + push(path: string, flag?: RouterFlag): void; replace(path: string, key?: string | null): void; @@ -197,7 +199,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter { private currentKey = Date.now().toString(); private redirectCount = 0; - public navHook: ((path: string, flag?: any) => boolean) | null = null; + public navHook: ((path: string, flag?: RouterFlag) => boolean) | null = null; constructor(routes: Router['routes'], currentPath: Router['currentPath'], isLoggedIn: boolean, notFoundPageComponent: Component) { super(); @@ -404,7 +406,7 @@ export class Router extends EventEmitter<RouterEvent> implements IRouter { return this.currentKey; } - public push(path: string, flag?: any) { + public push(path: string, flag?: RouterFlag) { const beforePath = this.currentPath; if (path === beforePath) { this.emit('same'); diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 07d91a0644..ea1b673de9 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -28,12 +28,13 @@ import { pleaseLogin } from '@/scripts/please-login.js'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js'; import { focusParent } from '@/scripts/focus.js'; +import type { PostFormProps } from '@/types/post-form.js'; export const openingWindowsCount = ref(0); -export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req']>( +export const apiWithDialog = (<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req'] = Misskey.Endpoints[E]['req']>( endpoint: E, - data: P = {} as any, + data: P, token?: string | null | undefined, customErrors?: Record<string, { title?: string; text: string; }>, ) => { @@ -94,7 +95,7 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey export function promiseDialog<T extends Promise<any>>( promise: T, - onSuccess?: ((res: any) => void) | null, + onSuccess?: ((res: Awaited<T>) => void) | null, onFailure?: ((err: Misskey.api.APIError) => void) | null, text?: string, ): T { @@ -136,12 +137,12 @@ export function promiseDialog<T extends Promise<any>>( } let popupIdCount = 0; -export const popups = ref([]) as Ref<{ +export const popups = ref<{ id: number; component: Component; props: Record<string, any>; events: Record<string, any>; -}[]>; +}[]>([]); const zIndexes = { veryLow: 500000, @@ -458,7 +459,7 @@ type SelectItem<C> = { }; // default が指定されていたら result は null になり得ないことを保証する overload function -export function select<C = any>(props: { +export function select<C = unknown>(props: { title?: string; text?: string; default: string; @@ -471,7 +472,7 @@ export function select<C = any>(props: { } | { canceled: false; result: C; }>; -export function select<C = any>(props: { +export function select<C = unknown>(props: { title?: string; text?: string; default?: string | null; @@ -484,7 +485,7 @@ export function select<C = any>(props: { } | { canceled: false; result: C | null; }>; -export function select<C = any>(props: { +export function select<C = unknown>(props: { title?: string; text?: string; default?: string | null; @@ -687,13 +688,13 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> { })); } -export function post(props: Record<string, any> = {}): Promise<void> { +export function post(props: PostFormProps = {}): Promise<void> { pleaseLogin({ openOnRemote: (props.initialText || props.initialNote ? { type: 'share', params: { - text: props.initialText ?? props.initialNote.text, - visibility: props.initialVisibility ?? props.initialNote?.visibility, + text: props.initialText ?? props.initialNote?.text ?? '', + visibility: props.initialVisibility ?? props.initialNote?.visibility ?? 'public', localOnly: (props.initialLocalOnly || props.initialNote?.localOnly) ? '1' : '0', }, } : undefined), diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue index d1bbb5b734..870c3ce88b 100644 --- a/packages/frontend/src/pages/admin/users.vue +++ b/packages/frontend/src/pages/admin/users.vue @@ -99,19 +99,19 @@ async function addUser() { const { canceled: canceled1, result: username } = await os.inputText({ title: i18n.ts.username, }); - if (canceled1) return; + if (canceled1 || username == null) return; const { canceled: canceled2, result: password } = await os.inputText({ title: i18n.ts.password, type: 'password', }); - if (canceled2) return; + if (canceled2 || password == null) return; os.apiWithDialog('admin/accounts/create', { username: username, password: password, }).then(res => { - paginationComponent.value.reload(); + paginationComponent.value?.reload(); }); } diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 1e416e22d3..cae3f3ede9 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -116,7 +116,7 @@ const selectAll = () => { if (selectedEmojis.value.length > 0) { selectedEmojis.value = []; } else { - selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id); + selectedEmojis.value = Array.from(emojisPaginationComponent.value?.items.values(), item => item.id); } }; @@ -133,7 +133,7 @@ const add = async (ev: MouseEvent) => { }, { done: result => { if (result.created) { - emojisPaginationComponent.value.prepend(result.created); + emojisPaginationComponent.value?.prepend(result.created); } }, closed: () => dispose(), @@ -146,12 +146,12 @@ const edit = (emoji) => { }, { done: result => { if (result.updated) { - emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + emojisPaginationComponent.value?.updateItem(result.updated.id, (oldEmoji) => ({ ...oldEmoji, ...result.updated, })); } else if (result.deleted) { - emojisPaginationComponent.value.removeItem(emoji.id); + emojisPaginationComponent.value?.removeItem(emoji.id); } }, closed: () => dispose(), @@ -226,7 +226,7 @@ const setCategoryBulk = async () => { ids: selectedEmojis.value, category: result, }); - emojisPaginationComponent.value.reload(); + emojisPaginationComponent.value?.reload(); }; const setLicenseBulk = async () => { @@ -238,43 +238,43 @@ const setLicenseBulk = async () => { ids: selectedEmojis.value, license: result, }); - emojisPaginationComponent.value.reload(); + emojisPaginationComponent.value?.reload(); }; const addTagBulk = async () => { const { canceled, result } = await os.inputText({ title: 'Tag', }); - if (canceled) return; + if (canceled || result == null) return; await os.apiWithDialog('admin/emoji/add-aliases-bulk', { ids: selectedEmojis.value, aliases: result.split(' '), }); - emojisPaginationComponent.value.reload(); + emojisPaginationComponent.value?.reload(); }; const removeTagBulk = async () => { const { canceled, result } = await os.inputText({ title: 'Tag', }); - if (canceled) return; + if (canceled || result == null) return; await os.apiWithDialog('admin/emoji/remove-aliases-bulk', { ids: selectedEmojis.value, aliases: result.split(' '), }); - emojisPaginationComponent.value.reload(); + emojisPaginationComponent.value?.reload(); }; const setTagBulk = async () => { const { canceled, result } = await os.inputText({ title: 'Tag', }); - if (canceled) return; + if (canceled || result == null) return; await os.apiWithDialog('admin/emoji/set-aliases-bulk', { ids: selectedEmojis.value, aliases: result.split(' '), }); - emojisPaginationComponent.value.reload(); + emojisPaginationComponent.value?.reload(); }; const delBulk = async () => { @@ -286,7 +286,7 @@ const delBulk = async () => { await os.apiWithDialog('admin/emoji/delete-bulk', { ids: selectedEmojis.value, }); - emojisPaginationComponent.value.reload(); + emojisPaginationComponent.value?.reload(); }; const headerActions = computed(() => [{ diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 3b3f41d9b1..2caba03675 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -95,14 +95,14 @@ import { selectFile } from '@/scripts/select-file.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; const props = defineProps<{ - emoji?: any, + emoji?: Misskey.entities.EmojiDetailed, }>(); const windowEl = ref<InstanceType<typeof MkWindow> | null>(null); const name = ref<string>(props.emoji ? props.emoji.name : ''); -const category = ref<string>(props.emoji ? props.emoji.category : ''); +const category = ref<string>(props.emoji?.category ? props.emoji.category : ''); const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : ''); -const license = ref<string>(props.emoji ? (props.emoji.license ?? '') : ''); +const license = ref<string>(props.emoji?.license ? props.emoji.license : ''); const isSensitive = ref(props.emoji ? props.emoji.isSensitive : false); const localOnly = ref(props.emoji ? props.emoji.localOnly : false); const roleIdsThatCanBeUsedThisEmojiAsReaction = ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); @@ -116,11 +116,11 @@ watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => { const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); const emit = defineEmits<{ - (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, + (ev: 'done', v: { deleted?: boolean; updated?: Misskey.entities.AdminEmojiUpdateRequest; created?: Misskey.entities.AdminEmojiUpdateRequest }): void, (ev: 'closed'): void }>(); -async function changeImage(ev) { +async function changeImage(ev: Event) { file.value = await selectFile(ev.currentTarget ?? ev.target, null); const candidate = file.value.name.replace(/\.(.+)$/, ''); if (candidate.match(/^[a-z0-9_]+$/)) { @@ -140,7 +140,7 @@ async function addRole() { rolesThatCanBeUsedThisEmojiAsReaction.value.push(role); } -async function removeRole(role, ev) { +async function removeRole(role: Misskey.entities.RoleLite, ev: Event) { rolesThatCanBeUsedThisEmojiAsReaction.value = rolesThatCanBeUsedThisEmojiAsReaction.value.filter(x => x.id !== role.id); } @@ -172,7 +172,7 @@ async function done() { }, }); - windowEl.value.close(); + windowEl.value?.close(); } else { const created = await os.apiWithDialog('admin/emoji/add', params); @@ -180,11 +180,12 @@ async function done() { created: created, }); - windowEl.value.close(); + windowEl.value?.close(); } } async function del() { + if (!props.emoji) return; const { canceled } = await os.confirm({ type: 'warning', text: i18n.tsx.removeAreYouSure({ x: name.value }), @@ -197,7 +198,7 @@ async function del() { emit('done', { deleted: true, }); - windowEl.value.close(); + windowEl.value?.close(); }); } </script> diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index 8991af8086..a840d0d0b3 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -55,13 +55,13 @@ const pagination = { function accept(user) { misskeyApi('following/requests/accept', { userId: user.id }).then(() => { - paginationComponent.value.reload(); + paginationComponent.value?.reload(); }); } function reject(user) { misskeyApi('following/requests/reject', { userId: user.id }).then(() => { - paginationComponent.value.reload(); + paginationComponent.value?.reload(); }); } diff --git a/packages/frontend/src/pages/lookup.vue b/packages/frontend/src/pages/lookup.vue index 3233953942..6f10c69640 100644 --- a/packages/frontend/src/pages/lookup.vue +++ b/packages/frontend/src/pages/lookup.vue @@ -40,7 +40,7 @@ function fetch() { return; } - let promise: Promise<any>; + let promise: Promise<unknown>; if (uri.startsWith('https://')) { promise = misskeyApi('ap/show', { diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index ece998a7a5..acf37a9a2f 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -77,15 +77,15 @@ async function create() { clipsCache.delete(); - pagingComponent.value.reload(); + pagingComponent.value?.reload(); } function onClipCreated() { - pagingComponent.value.reload(); + pagingComponent.value?.reload(); } function onClipDeleted() { - pagingComponent.value.reload(); + pagingComponent.value?.reload(); } const headerActions = computed(() => []); diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 804a5ae8f8..69e404bd85 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -110,7 +110,7 @@ function addUser() { listId: list.value.id, userId: user.id, }).then(() => { - paginationEl.value.reload(); + paginationEl.value?.reload(); }); }); } @@ -126,7 +126,7 @@ async function removeUser(item, ev) { listId: list.value.id, userId: item.userId, }).then(() => { - paginationEl.value.removeItem(item.id); + paginationEl.value?.removeItem(item.id); }); }, }], ev.currentTarget ?? ev.target); diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue index 1cfe7a6d2d..247b8f61a1 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue @@ -30,11 +30,12 @@ import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - modelValue: any + modelValue: Misskey.entities.PageBlock & { type: 'image' }; }>(); const emit = defineEmits<{ - (ev: 'update:modelValue', value: any): void; + (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'image' }): void; + (ev: 'remove'): void; }>(); const file = ref<Misskey.entities.DriveFile | null>(null); diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue index 0a28386986..52b4f2aaa3 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue @@ -34,19 +34,24 @@ import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - modelValue: any + modelValue: Misskey.entities.PageBlock & { type: 'note' }; }>(); const emit = defineEmits<{ - (ev: 'update:modelValue', value: any): void; + (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'note' }): void; }>(); -const id = ref<any>(props.modelValue.note); +const id = ref(props.modelValue.note); const note = ref<Misskey.entities.Note | null>(null); watch(id, async () => { if (id.value && (id.value.startsWith('http://') || id.value.startsWith('https://'))) { - id.value = (id.value.endsWith('/') ? id.value.slice(0, -1) : id.value).split('/').pop(); + id.value = (id.value.endsWith('/') ? id.value.slice(0, -1) : id.value).split('/').pop() ?? null; + } + + if (!id.value) { + note.value = null; + return; } emit('update:modelValue', { 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 0f8dc33143..eea52255c7 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 @@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> /* eslint-disable vue/no-mutating-props */ import { defineAsyncComponent, inject, onMounted, watch, ref } from 'vue'; +import * as Misskey from 'misskey-js'; import { v4 as uuid } from 'uuid'; import XContainer from '../page-editor.container.vue'; import * as os from '@/os.js'; @@ -33,14 +34,13 @@ import { getPageBlockList } from '@/pages/page-editor/common.js'; const XBlocks = defineAsyncComponent(() => import('../page-editor.blocks.vue')); -const props = withDefaults(defineProps<{ - modelValue: any, -}>(), { - modelValue: {}, -}); +const props = defineProps<{ + modelValue: Misskey.entities.PageBlock & { type: 'section'; }, +}>(); const emit = defineEmits<{ - (ev: 'update:modelValue', value: any): void; + (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'section' }): void; + (ev: 'remove'): void; }>(); const children = ref(deepClone(props.modelValue.children ?? [])); diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue index f09f7e1acd..43a1e37f9e 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue @@ -17,16 +17,17 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> /* eslint-disable vue/no-mutating-props */ import { watch, ref, shallowRef, onMounted, onUnmounted } from 'vue'; +import * as Misskey from 'misskey-js'; import XContainer from '../page-editor.container.vue'; import { i18n } from '@/i18n.js'; import { Autocomplete } from '@/scripts/autocomplete.js'; const props = defineProps<{ - modelValue: any + modelValue: Misskey.entities.PageBlock & { type: 'text' } }>(); const emit = defineEmits<{ - (ev: 'update:modelValue', value: any): void; + (ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'text' }): void; }>(); let autocomplete: Autocomplete; diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index bac1d2bb70..4cacbd0906 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -52,7 +52,7 @@ const props = defineProps<{ const scope = computed(() => props.path ? props.path.split('/') : []); -const keys = ref<any>(null); +const keys = ref<[string, string][]>([]); function fetchKeys() { misskeyApi('i/registry/keys-with-type', { diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue index dfb6e3f53e..437a1a2294 100644 --- a/packages/frontend/src/pages/reversi/game.setting.vue +++ b/packages/frontend/src/pages/reversi/game.setting.vue @@ -132,7 +132,7 @@ const mapCategories = Array.from(new Set(Object.values(Reversi.maps).map(x => x. const props = defineProps<{ game: Misskey.entities.ReversiGameDetailed; - connection: Misskey.ChannelConnection; + connection: Misskey.ChannelConnection<Misskey.Channels['reversiGame']>; }>(); const shareWhenStart = defineModel<boolean>('shareWhenStart', { default: false }); @@ -217,14 +217,14 @@ function onChangeReadyStates(states) { game.value.user2Ready = states.user2; } -function updateSettings(key: keyof Misskey.entities.ReversiGameDetailed) { +function updateSettings(key: typeof Misskey.reversiUpdateKeys[number]) { props.connection.send('updateSettings', { key: key, value: game.value[key], }); } -function onUpdateSettings({ userId, key, value }: { userId: string; key: keyof Misskey.entities.ReversiGameDetailed; value: any; }) { +function onUpdateSettings<K extends typeof Misskey.reversiUpdateKeys[number]>({ userId, key, value }: { userId: string; key: K; value: Misskey.entities.ReversiGameDetailed[K]; }) { if (userId === $i.id) return; if (game.value[key] === value) return; game.value[key] = value; diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index 2250e1ce60..88171f7d70 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -76,7 +76,11 @@ import { claimAchievement } from '@/scripts/achievements.js'; const parser = new Parser(); let aiscript: Interpreter; const code = ref(''); -const logs = ref<any[]>([]); +const logs = ref<{ + id: number; + text: string; + print: boolean; +}[]>([]); const root = ref<AsUiRoot>(); const components = ref<Ref<AsUiComponent>[]>([]); const uiKey = ref(0); diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue index 2244047b31..18c82ffdf6 100644 --- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue +++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue @@ -138,12 +138,13 @@ const token = ref<string | number | null>(null); const backupCodes = ref<string[]>(); function cancel() { - dialog.value.close(); + dialog.value?.close(); } async function tokenDone() { + if (token.value == null) return; const res = await os.apiWithDialog('i/2fa/done', { - token: token.value.toString(), + token: typeof token.value === 'string' ? token.value : token.value.toString(), }); backupCodes.value = res.backupCodes; @@ -166,7 +167,7 @@ function downloadBackupCodes() { } function allDone() { - dialog.value.close(); + dialog.value?.close(); } </script> diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue index 16f0716a12..97e960675f 100644 --- a/packages/frontend/src/pages/settings/accounts.vue +++ b/packages/frontend/src/pages/settings/accounts.vue @@ -90,7 +90,7 @@ function createAccount() { }); } -async function switchAccount(account: any) { +async function switchAccount(account: Misskey.entities.UserDetailed) { const fetchedAccounts = await getAccounts(); const token = fetchedAccounts.find(x => x.id === account.id)!.token; switchAccountWithToken(token); diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue index 68e36ef1bb..6515503505 100644 --- a/packages/frontend/src/pages/settings/apps.vue +++ b/packages/frontend/src/pages/settings/apps.vue @@ -55,6 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, computed } from 'vue'; +import * as Misskey from 'misskey-js'; import FormPagination from '@/components/MkPagination.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; @@ -77,7 +78,7 @@ const pagination = { function revoke(token) { misskeyApi('i/revoke-token', { tokenId: token.id }).then(() => { - list.value.reload(); + list.value?.reload(); }); } diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue index aa899ac649..40542ad5b2 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue @@ -110,7 +110,7 @@ const decorationsForPreview = computed(() => { }); function cancel() { - dialog.value.close(); + dialog.value?.close(); } async function update() { @@ -120,7 +120,7 @@ async function update() { offsetX: offsetX.value, offsetY: offsetY.value, }); - dialog.value.close(); + dialog.value?.close(); } async function attach() { @@ -130,12 +130,12 @@ async function attach() { offsetX: offsetX.value, offsetY: offsetY.value, }); - dialog.value.close(); + dialog.value?.close(); } async function detach() { emit('detach'); - dialog.value.close(); + dialog.value?.close(); } </script> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 00b5740639..2794db2821 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -257,7 +257,7 @@ function parallaxLoop() { } function parallax() { - const banner = bannerEl.value as any; + const banner = bannerEl.value; if (banner == null) return; const top = getScrollPosition(rootEl.value); diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index b5fd6b6ec3..e98e0b59b1 100644 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -10,7 +10,7 @@ import { $i, iAmModerator } from '@/account.js'; import MkLoading from '@/pages/_loading_.vue'; import MkError from '@/pages/_error_.vue'; -export const page = (loader: AsyncComponentLoader<any>) => defineAsyncComponent({ +export const page = (loader: AsyncComponentLoader) => defineAsyncComponent({ loader: loader, loadingComponent: MkLoading, errorComponent: MkError, diff --git a/packages/frontend/src/router/main.ts b/packages/frontend/src/router/main.ts index 6ee967e6f4..3c25e80d12 100644 --- a/packages/frontend/src/router/main.ts +++ b/packages/frontend/src/router/main.ts @@ -4,7 +4,7 @@ */ import { EventEmitter } from 'eventemitter3'; -import { IRouter, Resolved, RouteDef, RouterEvent } from '@/nirax.js'; +import { IRouter, Resolved, RouteDef, RouterEvent, RouterFlag } from '@/nirax.js'; import type { App, ShallowRef } from 'vue'; @@ -79,7 +79,7 @@ class MainRouterProxy implements IRouter { return this.supplier().currentRoute; } - get navHook(): ((path: string, flag?: any) => boolean) | null { + get navHook(): ((path: string, flag?: RouterFlag) => boolean) | null { return this.supplier().navHook; } @@ -91,11 +91,11 @@ class MainRouterProxy implements IRouter { return this.supplier().getCurrentKey(); } - getCurrentPath(): any { + getCurrentPath(): string { return this.supplier().getCurrentPath(); } - push(path: string, flag?: any): void { + push(path: string, flag?: RouterFlag): void { this.supplier().push(path, flag); } diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts index 67e896b4b9..0a37a08bf0 100644 --- a/packages/frontend/src/scripts/check-word-mute.ts +++ b/packages/frontend/src/scripts/check-word-mute.ts @@ -2,8 +2,9 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ +import * as Misskey from 'misskey-js'; -export function checkWordMute(note: Record<string, any>, me: Record<string, any> | null | undefined, mutedWords: Array<string | string[]>): boolean { +export function checkWordMute(note: Misskey.entities.Note, me: Misskey.entities.UserLite | null | undefined, mutedWords: Array<string | string[]>): boolean { // 自分自身 if (me && (note.userId === me.id)) return false; diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts index 242a504c3b..1032e97ac9 100644 --- a/packages/frontend/src/scripts/form.ts +++ b/packages/frontend/src/scripts/form.ts @@ -15,7 +15,7 @@ type Hidden = boolean | ((v: any) => boolean); export type FormItem = { label?: string; type: 'string'; - default: string | null; + default?: string | null; description?: string; required?: boolean; hidden?: Hidden; @@ -24,7 +24,7 @@ export type FormItem = { } | { label?: string; type: 'number'; - default: number | null; + default?: number | null; description?: string; required?: boolean; hidden?: Hidden; @@ -32,20 +32,20 @@ export type FormItem = { } | { label?: string; type: 'boolean'; - default: boolean | null; + default?: boolean | null; description?: string; hidden?: Hidden; } | { label?: string; type: 'enum'; - default: string | null; + default?: string | null; required?: boolean; hidden?: Hidden; enum: EnumItem[]; } | { label?: string; type: 'radio'; - default: unknown | null; + default?: unknown | null; required?: boolean; hidden?: Hidden; options: { @@ -55,7 +55,7 @@ export type FormItem = { } | { label?: string; type: 'range'; - default: number | null; + default?: number | null; description?: string; required?: boolean; step?: number; @@ -66,12 +66,12 @@ export type FormItem = { } | { label?: string; type: 'object'; - default: Record<string, unknown> | null; + default?: Record<string, unknown> | null; hidden: Hidden; } | { label?: string; type: 'array'; - default: unknown[] | null; + default?: unknown[] | null; hidden: Hidden; } | { type: 'button'; diff --git a/packages/frontend/src/scripts/misskey-api.ts b/packages/frontend/src/scripts/misskey-api.ts index 1b1159fd01..e7a92e2d5c 100644 --- a/packages/frontend/src/scripts/misskey-api.ts +++ b/packages/frontend/src/scripts/misskey-api.ts @@ -17,7 +17,7 @@ export function misskeyApi< _ResT = ResT extends void ? Misskey.api.SwitchCaseResponseType<E, P> : ResT, >( endpoint: E, - data: P = {} as any, + data: P & { i?: string | null; } = {} as any, token?: string | null | undefined, signal?: AbortSignal, ): Promise<_ResT> { @@ -30,8 +30,8 @@ export function misskeyApi< const promise = new Promise<_ResT>((resolve, reject) => { // Append a credential - if ($i) (data as any).i = $i.token; - if (token !== undefined) (data as any).i = token; + if ($i) data.i = $i.token; + if (token !== undefined) data.i = token; // Send request window.fetch(`${apiUrl}/${endpoint}`, { diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts index 9aa38178b2..b037aa8acc 100644 --- a/packages/frontend/src/scripts/select-file.ts +++ b/packages/frontend/src/scripts/select-file.ts @@ -80,7 +80,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> { }); } -function select(src: any, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> { +function select(src: HTMLElement | EventTarget | null, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> { return new Promise((res, rej) => { const keepOriginal = ref(defaultStore.state.keepOriginalUploading); @@ -107,10 +107,10 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Miss }); } -export function selectFile(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile> { +export function selectFile(src: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile> { return select(src, label, false).then(files => files[0]); } -export function selectFiles(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile[]> { +export function selectFiles(src: HTMLElement | EventTarget | null, label: string | null = null): Promise<Misskey.entities.DriveFile[]> { return select(src, label, true); } diff --git a/packages/frontend/src/scripts/shuffle.ts b/packages/frontend/src/scripts/shuffle.ts index fed16bc71c..1f6ef1928c 100644 --- a/packages/frontend/src/scripts/shuffle.ts +++ b/packages/frontend/src/scripts/shuffle.ts @@ -6,8 +6,9 @@ /** * 配列をシャッフル (破壊的) */ -export function shuffle<T extends any[]>(array: T): T { - let currentIndex = array.length, randomIndex; +export function shuffle<T extends unknown[]>(array: T): T { + let currentIndex = array.length; + let randomIndex: number; // While there remain elements to shuffle. while (currentIndex !== 0) { diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts index 22dce609c6..713573a377 100644 --- a/packages/frontend/src/scripts/upload.ts +++ b/packages/frontend/src/scripts/upload.ts @@ -32,13 +32,13 @@ const mimeTypeMap = { export function uploadFile( file: File, - folder?: any, + folder?: string | Misskey.entities.DriveFolder, name?: string, keepOriginal: boolean = defaultStore.state.keepOriginalUploading, ): Promise<Misskey.entities.DriveFile> { if ($i == null) throw new Error('Not logged in'); - if (folder && typeof folder === 'object') folder = folder.id; + const _folder = typeof folder === 'string' ? folder : folder?.id; if (file.size > instance.maxFileSize) { alert({ @@ -89,11 +89,11 @@ export function uploadFile( } const formData = new FormData(); - formData.append('i', $i.token); + formData.append('i', $i!.token); formData.append('force', 'true'); formData.append('file', resizedImage ?? file); formData.append('name', ctx.name); - if (folder) formData.append('folderId', folder); + if (_folder) formData.append('folderId', _folder); const xhr = new XMLHttpRequest(); xhr.open('POST', apiUrl + '/drive/files/create', true); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index aab67e0b5c..911a463636 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -11,6 +11,7 @@ import darkTheme from '@@/themes/d-green-lime.json5'; import { miLocalStorage } from './local-storage.js'; import type { SoundType } from '@/scripts/sound.js'; import { Storage } from '@/pizzax.js'; +import type { Ast } from '@syuilo/aiscript'; interface PostFormAction { title: string, @@ -516,7 +517,7 @@ export type Plugin = { token: string; src: string | null; version: string; - ast: any[]; + ast: Ast.Node[]; author?: string; description?: string; permissions?: string[]; @@ -554,13 +555,13 @@ export class ColdDeviceStorage { } public static getAll(): Partial<typeof this.default> { - return (Object.keys(this.default) as (keyof typeof this.default)[]).reduce((acc, key) => { + return (Object.keys(this.default) as (keyof typeof this.default)[]).reduce<Partial<typeof this.default>>((acc, key) => { const value = localStorage.getItem(PREFIX + key); if (value != null) { acc[key] = JSON.parse(value); } return acc; - }, {} as any); + }, {}); } public static set<T extends keyof typeof ColdDeviceStorage.default>(key: T, value: typeof ColdDeviceStorage.default[T]): void { @@ -605,7 +606,7 @@ export class ColdDeviceStorage { get: () => { return valueRef.value; }, - set: (value: unknown) => { + set: (value: typeof ColdDeviceStorage.default[K]) => { const val = value; ColdDeviceStorage.set(key, val); }, diff --git a/packages/frontend/src/types/post-form.ts b/packages/frontend/src/types/post-form.ts new file mode 100644 index 0000000000..5bb04a95a0 --- /dev/null +++ b/packages/frontend/src/types/post-form.ts @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as Misskey from 'misskey-js'; + +export interface PostFormProps { + reply?: Misskey.entities.Note; + renote?: Misskey.entities.Note; + channel?: Misskey.entities.Channel; // TODO + mention?: Misskey.entities.User; + specified?: Misskey.entities.UserDetailed; + initialText?: string; + initialCw?: string; + initialVisibility?: (typeof Misskey.noteVisibilities)[number]; + initialFiles?: Misskey.entities.DriveFile[]; + initialLocalOnly?: boolean; + initialVisibleUsers?: Misskey.entities.UserDetailed[]; + initialNote?: Misskey.entities.Note; + instant?: boolean; +}; diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue index 34be8c5e57..40e2d8fbc7 100644 --- a/packages/frontend/src/widgets/WidgetPhotos.vue +++ b/packages/frontend/src/widgets/WidgetPhotos.vue @@ -68,10 +68,10 @@ const onDriveFileCreated = (file) => { } }; -const thumbnail = (image: any): string => { +const thumbnail = (image: Misskey.entities.DriveFile): string => { return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) - : image.thumbnailUrl; + : image.thumbnailUrl ?? image.url; }; misskeyApi('drive/stream', { |