diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cceb6e432..71273f705a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,9 @@ - Fix: 複数のメンションを1行に記述した場合に、サジェストが正しく表示されない問題を修正 - Fix: メンションとしての条件を満たしていても、特定の条件(`-`が含まれる場合など)で正しくサジェストされない問題を一部修正 - Fix: ユーザーの前後ノートを閲覧する機能が動作しない問題を修正 +- Fix: 照会ダイアログでap/showでローカルユーザーを解決した際@username@nullに飛ばされる問題を修正 +- Fix: アイコンのデコレーションを付ける際にデコレーションが表示されなくなる問題を修正 +- Fix: 管理中アカウント一覧で正しい表示が行われない問題を修正 ### Server - Feat: サーバー管理コマンド diff --git a/idea/MkAbuseReport.stories.impl.ts b/idea/MkAbuseReport.stories.impl.ts index 717bceb23d..138e5cd0cd 100644 --- a/idea/MkAbuseReport.stories.impl.ts +++ b/idea/MkAbuseReport.stories.impl.ts @@ -4,8 +4,8 @@ */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; -import { StoryObj } from '@storybook/vue3'; +import { action } from 'storybook/actions'; +import type { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; import { abuseUserReport } from '../packages/frontend/.storybook/fakes.js'; import { commonHandlers } from '../packages/frontend/.storybook/mocks.js'; diff --git a/locales/index.d.ts b/locales/index.d.ts index cefe4df0bd..05443ab7d6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -12039,6 +12039,10 @@ export interface Locale extends ILocale { * 連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったコンテンツを自動でサーバーから削除し、ストレージを節約できます。 */ "remoteContentsCleaning_description": string; + /** + * ハイパーリンクなど、一部の参照方法はシステム上で検知できません。 + */ + "remoteContentsCleaning_description2": string; /** * 管理者情報 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 31859cb2fa..1bc6df7103 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -3218,6 +3218,7 @@ _serverSetupWizard: youCanConfigureMoreFederationSettingsLater: "連合可能なサーバーの指定など、高度な設定も後ほど可能です。" remoteContentsCleaning: "受信コンテンツの自動クリーニング" remoteContentsCleaning_description: "連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったコンテンツを自動でサーバーから削除し、ストレージを節約できます。" + remoteContentsCleaning_description2: "ハイパーリンクなど、一部の参照方法はシステム上で検知できません。" adminInfo: "管理者情報" adminInfo_description: "問い合わせを受け付けるために使用される管理者情報を設定します。" adminInfo_mustBeFilled: "オープンサーバー、または連合がオンの場合は必ず入力が必要です。" diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index 46ec13704c..54ce4d472a 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -49,15 +49,12 @@ export class NoteReactionEntityService implements OnModuleInit { public async pack( src: MiNoteReaction['id'] | MiNoteReaction, me?: { id: MiUser['id'] } | null | undefined, - options?: { - withNote: boolean; - }, + options?: object, hints?: { packedUser?: Packed<'UserLite'> }, ): Promise> { const opts = Object.assign({ - withNote: false, }, options); const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); @@ -67,9 +64,6 @@ export class NoteReactionEntityService implements OnModuleInit { createdAt: this.idService.parse(reaction.id).date.toISOString(), user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me), type: this.reactionService.convertLegacyReaction(reaction.reaction), - ...(opts.withNote ? { - note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me), - } : {}), }; } @@ -77,16 +71,50 @@ export class NoteReactionEntityService implements OnModuleInit { public async packMany( reactions: MiNoteReaction[], me?: { id: MiUser['id'] } | null | undefined, - options?: { - withNote: boolean; - }, + options?: object, ): Promise[]> { const opts = Object.assign({ - withNote: false, }, options); const _users = reactions.map(({ user, userId }) => user ?? userId); const _userMap = await this.userEntityService.packMany(_users, me) .then(users => new Map(users.map(u => [u.id, u]))); return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) }))); } + + @bindThis + public async packWithNote( + src: MiNoteReaction['id'] | MiNoteReaction, + me?: { id: MiUser['id'] } | null | undefined, + options?: object, + hints?: { + packedUser?: Packed<'UserLite'> + }, + ): Promise> { + const opts = Object.assign({ + }, options); + + const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); + + return { + id: reaction.id, + createdAt: this.idService.parse(reaction.id).date.toISOString(), + user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me), + type: this.reactionService.convertLegacyReaction(reaction.reaction), + note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me), + }; + } + + @bindThis + public async packManyWithNote( + reactions: MiNoteReaction[], + me?: { id: MiUser['id'] } | null | undefined, + options?: object, + ): Promise[]> { + const opts = Object.assign({ + }, options); + const _users = reactions.map(({ user, userId }) => user ?? userId); + const _userMap = await this.userEntityService.packMany(_users, me) + .then(users => new Map(users.map(u => [u.id, u]))); + return Promise.all(reactions.map(reaction => this.packWithNote(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) }))); + } } diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index ed47edff9b..dca92e1037 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -22,7 +22,7 @@ import { packedFollowingSchema } from '@/models/json-schema/following.js'; import { packedMutingSchema } from '@/models/json-schema/muting.js'; import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js'; import { packedBlockingSchema } from '@/models/json-schema/blocking.js'; -import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js'; +import { packedNoteReactionSchema, packedNoteReactionWithNoteSchema } from '@/models/json-schema/note-reaction.js'; import { packedHashtagSchema } from '@/models/json-schema/hashtag.js'; import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js'; import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js'; @@ -92,6 +92,7 @@ export const refs = { Note: packedNoteSchema, NoteDraft: packedNoteDraftSchema, NoteReaction: packedNoteReactionSchema, + NoteReactionWithNote: packedNoteReactionWithNoteSchema, NoteFavorite: packedNoteFavoriteSchema, Notification: packedNotificationSchema, DriveFile: packedDriveFileSchema, diff --git a/packages/backend/src/models/json-schema/note-reaction.ts b/packages/backend/src/models/json-schema/note-reaction.ts index 95658ace1f..04c9f34232 100644 --- a/packages/backend/src/models/json-schema/note-reaction.ts +++ b/packages/backend/src/models/json-schema/note-reaction.ts @@ -10,7 +10,6 @@ export const packedNoteReactionSchema = { type: 'string', optional: false, nullable: false, format: 'id', - example: 'xxxxxxxxxx', }, createdAt: { type: 'string', @@ -28,3 +27,33 @@ export const packedNoteReactionSchema = { }, }, } as const; + +export const packedNoteReactionWithNoteSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + user: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, + type: { + type: 'string', + optional: false, nullable: false, + }, + note: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, +} as const; diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 81a788de2b..804bd5d9b9 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -49,6 +49,34 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + icon: { + type: 'string', + optional: false, nullable: true, + }, + display: { + type: 'string', + optional: false, nullable: false, + }, + isActive: { + type: 'boolean', + optional: false, nullable: false, + }, + forExistingUsers: { + type: 'boolean', + optional: false, nullable: false, + }, + silence: { + type: 'boolean', + optional: false, nullable: false, + }, + needConfirmationToRead: { + type: 'boolean', + optional: false, nullable: false, + }, + userId: { + type: 'string', + optional: false, nullable: true, + }, imageUrl: { type: 'string', optional: false, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts index e378669f0a..8696c6f6e8 100644 --- a/packages/backend/src/server/api/endpoints/flash/update.ts +++ b/packages/backend/src/server/api/endpoints/flash/update.ts @@ -73,8 +73,8 @@ export default class extends Endpoint { // eslint- updatedAt: new Date(), ...Object.fromEntries( Object.entries(ps).filter( - ([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key) - ) + ([key, val]) => (key !== 'flashId') && Object.hasOwn(paramDef.properties, key), + ), ), }); }); diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index 8756801fe4..ed5952d4c5 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -23,6 +23,16 @@ export const meta = { type: 'object', optional: false, nullable: false, ref: 'UserList', + properties: { + likedCount: { + type: 'number', + optional: true, nullable: false, + }, + isLiked: { + type: 'boolean', + optional: true, nullable: false, + }, + }, }, errors: { diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index d6f1ecd8ed..d84a191f7a 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -28,7 +28,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - ref: 'NoteReaction', + ref: 'NoteReactionWithNote', }, }, @@ -120,7 +120,7 @@ export default class extends Endpoint { // eslint- return true; }); - return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true }); + return await this.noteReactionEntityService.packManyWithNote(reactions, me); }); } } diff --git a/packages/frontend-embed/src/theme.ts b/packages/frontend-embed/src/theme.ts index 680ab80167..c9b1c0d0c6 100644 --- a/packages/frontend-embed/src/theme.ts +++ b/packages/frontend-embed/src/theme.ts @@ -3,6 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +// TODO: (可能な部分を)sharedに抽出して frontend と共通化 + import tinycolor from 'tinycolor2'; import lightTheme from '@@/themes/_light.json5'; import darkTheme from '@@/themes/_dark.json5'; diff --git a/packages/frontend/.storybook/charts.ts b/packages/frontend/.storybook/charts.ts index 31bb9e51c5..93e1287d69 100644 --- a/packages/frontend/.storybook/charts.ts +++ b/packages/frontend/.storybook/charts.ts @@ -6,7 +6,7 @@ import { HttpResponse, http } from 'msw'; import type { DefaultBodyType, HttpResponseResolver, JsonBodyType, PathParams } from 'msw'; import seedrandom from 'seedrandom'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] { const rng = seedrandom(seed); diff --git a/packages/frontend/.vscode/storybook.code-snippets b/packages/frontend/.vscode/storybook.code-snippets index 785d0a1608..d7063f7200 100644 --- a/packages/frontend/.vscode/storybook.code-snippets +++ b/packages/frontend/.vscode/storybook.code-snippets @@ -42,7 +42,7 @@ "prefix": "storyimplevent", "body": [ "/* eslint-disable @typescript-eslint/explicit-function-return-type */", - "import { action } from '@storybook/addon-actions';", + "import { action } from 'storybook/actions';", "import { StoryObj } from '@storybook/vue3';", "import $1 from './$1.vue';", "export const Default = {", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index e78f85b34f..8789c65fa0 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -85,7 +85,6 @@ }, "devDependencies": { "@misskey-dev/summaly": "5.2.3", - "@storybook/addon-actions": "9.0.8", "@storybook/addon-essentials": "8.6.14", "@storybook/addon-interactions": "8.6.14", "@storybook/addon-links": "9.1.3", diff --git a/packages/frontend/src/accounts.ts b/packages/frontend/src/accounts.ts index 3693ac3308..afa2ecb911 100644 --- a/packages/frontend/src/accounts.ts +++ b/packages/frontend/src/accounts.ts @@ -23,7 +23,7 @@ export async function getAccounts(): Promise<{ host: string; id: Misskey.entities.User['id']; username: Misskey.entities.User['username']; - user?: Misskey.entities.User | null; + user?: Misskey.entities.MeDetailed | null; token: string | null; }[]> { const tokens = store.s.accountTokens; @@ -38,7 +38,7 @@ export async function getAccounts(): Promise<{ })); } -async function addAccount(host: string, user: Misskey.entities.User, token: AccountWithToken['token']) { +async function addAccount(host: string, user: Misskey.entities.MeDetailed, token: AccountWithToken['token']) { if (!prefer.s.accounts.some(x => x[0] === host && x[1].id === user.id)) { store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + user.id]: token }); store.set('accountInfos', { ...store.s.accountInfos, [host + '/' + user.id]: user }); @@ -149,9 +149,10 @@ export function updateCurrentAccountPartial(accountData: Partial { if (reason === isAccountDeleted) { - removeAccount(host, $i.id); + removeAccount(host, me.id); if (Object.keys(store.s.accountTokens).length > 0) { login(Object.values(store.s.accountTokens)[0]); } else { @@ -214,19 +215,37 @@ export async function openAccountMenu(opts: { includeCurrentAccount?: boolean; withExtraOperation: boolean; active?: Misskey.entities.User['id']; - onChoose?: (account: Misskey.entities.User) => void; + onChoose?: (account: Misskey.entities.MeDetailed) => void; }, ev: MouseEvent) { if (!$i) return; + const me = $i; - function createItem(host: string, id: Misskey.entities.User['id'], username: Misskey.entities.User['username'], account: Misskey.entities.User | null | undefined, token: string): MenuItem { + const callback = opts.onChoose; + + function createItem(host: string, id: Misskey.entities.User['id'], username: Misskey.entities.User['username'], account: Misskey.entities.MeDetailed | null | undefined, token: string | null): MenuItem { if (account) { return { type: 'user' as const, user: account, active: opts.active != null ? opts.active === id : false, action: async () => { - if (opts.onChoose) { - opts.onChoose(account); + if (callback) { + callback(account); + } else { + switchAccount(host, id); + } + }, + }; + } else if (token != null) { + return { + type: 'button' as const, + text: username, + active: opts.active != null ? opts.active === id : false, + action: async () => { + if (callback) { + fetchAccount(token, id).then(account => { + callback(account); + }); } else { switchAccount(host, id); } @@ -238,13 +257,7 @@ export async function openAccountMenu(opts: { text: username, active: opts.active != null ? opts.active === id : false, action: async () => { - if (opts.onChoose) { - fetchAccount(token, id).then(account => { - opts.onChoose(account); - }); - } else { - switchAccount(host, id); - } + // TODO }, }; } @@ -253,7 +266,7 @@ export async function openAccountMenu(opts: { const menuItems: MenuItem[] = []; // TODO: $iのホストも比較したいけど通常null - const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id))).map(a => createItem(a.host, a.id, a.username, a.user, a.token)); + const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.id !== me.id))).map(a => createItem(a.host, a.id, a.username, a.user, a.token)); if (opts.withExtraOperation) { menuItems.push({ diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 6ae8379801..18817d3f79 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -368,11 +368,6 @@ export async function mainBoot() { }); }); - main.on('unreadAntenna', () => { - updateCurrentAccountPartial({ hasUnreadAntenna: true }); - sound.playMisskeySfx('antenna'); - }); - main.on('newChatMessage', () => { updateCurrentAccountPartial({ hasUnreadChatMessages: true }); sound.playMisskeySfx('chatMessage'); diff --git a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts index b62096bbe9..2eb17b1b9e 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts +++ b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts @@ -2,14 +2,13 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ - -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; -import type { StoryObj } from '@storybook/vue3'; + +import { action } from 'storybook/actions'; import { HttpResponse, http } from 'msw'; import { userDetailed } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkAbuseReportWindow from './MkAbuseReportWindow.vue'; +import type { StoryObj } from '@storybook/vue3'; export const Default = { render(args) { return { diff --git a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts index b907b5b25a..fff29262f1 100644 --- a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts +++ b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts @@ -4,7 +4,7 @@ */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import type { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; import { commonHandlers } from '../../.storybook/mocks.js'; diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index 70766634ce..bf39c1e983 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum', }]" > -
+
@@ -71,7 +71,7 @@ const props = withDefaults(defineProps<{ const achievements = ref(null); const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x))); -function fetch() { +function _fetch_() { misskeyApi('users/achievements', { userId: props.user.id }).then(res => { achievements.value = []; for (const t of ACHIEVEMENT_TYPES) { @@ -84,11 +84,11 @@ function fetch() { function clickHere() { claimAchievement('clickedClickHere'); - fetch(); + _fetch_(); } onMounted(() => { - fetch(); + _fetch_(); }); diff --git a/packages/frontend/src/components/MkAnimBg.vue b/packages/frontend/src/components/MkAnimBg.vue index 82606c9aa4..19a21f6e24 100644 --- a/packages/frontend/src/components/MkAnimBg.vue +++ b/packages/frontend/src/components/MkAnimBg.vue @@ -265,6 +265,8 @@ onUnmounted(() => { if (handle) { window.cancelAnimationFrame(handle); } + + // TODO: WebGLリソースの解放 }); diff --git a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts index 627cb0c4ff..743bdda032 100644 --- a/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts +++ b/packages/frontend/src/components/MkAnnouncementDialog.stories.impl.ts @@ -4,7 +4,7 @@ */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import type { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; import { commonHandlers } from '../../.storybook/mocks.js'; diff --git a/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts b/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts index 4d921a4c48..5c4b05481a 100644 --- a/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts +++ b/packages/frontend/src/components/MkAntennaEditor.stories.impl.ts @@ -4,7 +4,7 @@ */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import type { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; import { commonHandlers } from '../../.storybook/mocks.js'; diff --git a/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts b/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts index 5878b52fb9..1a70cb745c 100644 --- a/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts +++ b/packages/frontend/src/components/MkAntennaEditorDialog.stories.impl.ts @@ -4,7 +4,7 @@ */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import type { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; import { commonHandlers } from '../../.storybook/mocks.js'; diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts index 64ccb708aa..15aab8daed 100644 --- a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts +++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts @@ -4,7 +4,7 @@ */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import { expect, userEvent, waitFor, within } from '@storybook/test'; import type { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; diff --git a/packages/frontend/src/components/MkButton.stories.impl.ts b/packages/frontend/src/components/MkButton.stories.impl.ts index 0a569b3beb..4420cc4f05 100644 --- a/packages/frontend/src/components/MkButton.stories.impl.ts +++ b/packages/frontend/src/components/MkButton.stories.impl.ts @@ -5,7 +5,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable import/no-default-export */ -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import type { StoryObj } from '@storybook/vue3'; import MkButton from './MkButton.vue'; export const Default = { diff --git a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts index 4304c2e2b7..095805ba95 100644 --- a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts +++ b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts @@ -2,9 +2,9 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ - + import { HttpResponse, http } from 'msw'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import { expect, userEvent, within } from '@storybook/test'; import { channel } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; diff --git a/packages/frontend/src/components/MkChannelList.stories.impl.ts b/packages/frontend/src/components/MkChannelList.stories.impl.ts index 47ca864dc0..867526ea5e 100644 --- a/packages/frontend/src/components/MkChannelList.stories.impl.ts +++ b/packages/frontend/src/components/MkChannelList.stories.impl.ts @@ -7,7 +7,7 @@ /* eslint-disable import/no-default-export */ import type { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import { channel } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkChannelList from './MkChannelList.vue'; diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index 4d67bba70d..c54081ad42 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -589,7 +589,10 @@ const fetchDriveFilesChart = async (): Promise => { }; const fetchInstanceRequestsChart = async (): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -611,7 +614,10 @@ const fetchInstanceRequestsChart = async (): Promise => { }; const fetchInstanceUsersChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Users', @@ -626,7 +632,10 @@ const fetchInstanceUsersChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Notes', @@ -641,7 +650,10 @@ const fetchInstanceNotesChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Following', @@ -664,7 +676,10 @@ const fetchInstanceFfChart = async (total: boolean): Promise = }; const fetchInstanceDriveUsageChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -680,7 +695,10 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span }); + const host = props.args?.host; + if (host == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span }); return { series: [{ name: 'Drive files', @@ -695,7 +713,10 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise => { - const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/notes', { userId: userId, limit: props.limit, span: props.span }); return { series: [...(props.args?.withoutAll ? [] : [{ name: 'All', @@ -727,7 +748,10 @@ const fetchPerUserNotesChart = async (): Promise => { }; const fetchPerUserPvChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/pv', { userId: userId, limit: props.limit, span: props.span }); return { series: [{ name: 'Unique PV (user)', @@ -754,7 +778,10 @@ const fetchPerUserPvChart = async (): Promise => { }; const fetchPerUserFollowingChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -769,7 +796,10 @@ const fetchPerUserFollowingChart = async (): Promise => { }; const fetchPerUserFollowersChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -784,7 +814,10 @@ const fetchPerUserFollowersChart = async (): Promise => { }; const fetchPerUserDriveChart = async (): Promise => { - const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span }); + const userId = props.args?.user?.id; + if (userId == null) return { series: [] }; + + const raw = await misskeyApiGet('charts/user/drive', { userId: userId, limit: props.limit, span: props.span }); return { bytes: true, series: [{ diff --git a/packages/frontend/src/components/MkChatHistories.stories.impl.ts b/packages/frontend/src/components/MkChatHistories.stories.impl.ts index 8268adc36f..74fdff6fdd 100644 --- a/packages/frontend/src/components/MkChatHistories.stories.impl.ts +++ b/packages/frontend/src/components/MkChatHistories.stories.impl.ts @@ -4,7 +4,7 @@ */ import { http, HttpResponse } from 'msw'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import { chatMessage } from '../../.storybook/fakes'; import MkChatHistories from './MkChatHistories.vue'; import type { StoryObj } from '@storybook/vue3'; diff --git a/packages/frontend/src/components/MkClickerGame.stories.impl.ts b/packages/frontend/src/components/MkClickerGame.stories.impl.ts index 6e1eb13d61..f9012742cb 100644 --- a/packages/frontend/src/components/MkClickerGame.stories.impl.ts +++ b/packages/frontend/src/components/MkClickerGame.stories.impl.ts @@ -2,9 +2,9 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ - + import { HttpResponse, http } from 'msw'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import { expect, userEvent, within } from '@storybook/test'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkClickerGame from './MkClickerGame.vue'; diff --git a/packages/frontend/src/components/MkCodeEditor.stories.impl.ts b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts index c76b6fd08e..24b8e9119b 100644 --- a/packages/frontend/src/components/MkCodeEditor.stories.impl.ts +++ b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts @@ -6,7 +6,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable import/no-default-export */ import type { StoryObj } from '@storybook/vue3'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import MkCodeEditor from './MkCodeEditor.vue'; const code = `for (let i, 100) { <: if (i % 15 == 0) "FizzBuzz" diff --git a/packages/frontend/src/components/MkColorInput.stories.impl.ts b/packages/frontend/src/components/MkColorInput.stories.impl.ts index 3df92ca858..f8ec58bbcc 100644 --- a/packages/frontend/src/components/MkColorInput.stories.impl.ts +++ b/packages/frontend/src/components/MkColorInput.stories.impl.ts @@ -6,7 +6,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable import/no-default-export */ import type { StoryObj } from '@storybook/vue3'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import MkColorInput from './MkColorInput.vue'; export const Default = { render(args) { diff --git a/packages/frontend/src/components/MkCropperDialog.stories.impl.ts b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts index 78cb4120de..bd6733f9a8 100644 --- a/packages/frontend/src/components/MkCropperDialog.stories.impl.ts +++ b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts @@ -4,7 +4,7 @@ */ import { HttpResponse, http } from 'msw'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import { file } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; import MkCropperDialog from './MkCropperDialog.vue'; diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index 7f592fba79..6c07eac47a 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/grid/MkGrid.stories.impl.ts b/packages/frontend/src/components/grid/MkGrid.stories.impl.ts index f85bf146e8..5ed8465299 100644 --- a/packages/frontend/src/components/grid/MkGrid.stories.impl.ts +++ b/packages/frontend/src/components/grid/MkGrid.stories.impl.ts @@ -4,7 +4,7 @@ */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; import type { StoryObj } from '@storybook/vue3'; import { ref } from 'vue'; import { commonHandlers } from '../../../.storybook/mocks.js'; diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index bf0e5e1b37..56a2b8d269 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -9,7 +9,7 @@ import { markRaw, ref, defineAsyncComponent, nextTick } from 'vue'; import { EventEmitter } from 'eventemitter3'; import * as Misskey from 'misskey-js'; import type { Component, Ref } from 'vue'; -import type { ComponentProps as CP } from 'vue-component-type-helpers'; +import type { ComponentEmit, ComponentProps as CP } from 'vue-component-type-helpers'; import type { Form, GetFormResultType } from '@/utility/form.js'; import type { MenuItem } from '@/types/menu.js'; import type { PostFormProps } from '@/types/post-form.js'; @@ -157,28 +157,9 @@ export function claimZIndex(priority: keyof typeof zIndexes = 'low'): number { return zIndexes[priority]; } -// InstanceType['$emit'] だとインターセクション型が返ってきて -// 使い物にならないので、代わりに ['$props'] から色々省くことで emit の型を生成する -// FIXME: 何故か *.ts ファイルからだと型がうまく取れない?ことがあるのをなんとかしたい -type ComponentEmit = T extends new () => { $props: infer Props } - ? [keyof Pick>] extends [never] - ? Record // *.ts ファイルから型がうまく取れないとき用(これがないと {} になって型エラーがうるさい) - : EmitsExtractor - : T extends (...args: any) => any - ? ReturnType extends { [x: string]: any; __ctx?: { [x: string]: any; props: infer Props } } - ? [keyof Pick>] extends [never] - ? Record - : EmitsExtractor - : never - : never; - // props に ref を許可するようにする type ComponentProps = { [K in keyof CP]: CP[K] | Ref[K]> }; -type EmitsExtractor = { - [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize : K extends string ? never : K]: T[K]; -}; - export function popup( component: T, props: ComponentProps, @@ -703,7 +684,7 @@ export async function cropImageFile(imageFile: File | Blob, options: { }); } -export function popupMenu(items: MenuItem[], anchorElement?: HTMLElement | EventTarget | null, options?: { +export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElement | EventTarget | null, options?: { align?: string; width?: number; onClosing?: () => void; @@ -715,7 +696,7 @@ export function popupMenu(items: MenuItem[], anchorElement?: HTMLElement | Event let returnFocusTo = getHTMLElementOrNull(anchorElement) ?? getHTMLElementOrNull(window.document.activeElement); return new Promise(resolve => nextTick(() => { const { dispose } = popup(MkPopupMenu, { - items, + items: items.filter(x => x != null), anchorElement, width: options?.width, align: options?.align, diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index b166dfd940..43133bb573 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only - +
diff --git a/packages/frontend/src/pages/about.overview.vue b/packages/frontend/src/pages/about.overview.vue index 0b9eee7d49..4786615173 100644 --- a/packages/frontend/src/pages/about.overview.vue +++ b/packages/frontend/src/pages/about.overview.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
{{ instance.name ?? host }}
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 7a49ba542f..052829ffe2 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -111,13 +111,13 @@ const props = defineProps<{ fileId: string, }>(); -async function fetch() { +async function _fetch_() { file.value = await misskeyApi('drive/files/show', { fileId: props.fileId }); info.value = await misskeyApi('admin/drive/show-file', { fileId: props.fileId }); isSensitive.value = file.value.isSensitive; } -fetch(); +_fetch_(); async function del() { const { canceled } = await os.confirm({ @@ -172,7 +172,7 @@ const headerTabs = computed(() => [{ key: 'raw', title: 'Raw data', icon: 'ti ti-code', -}]); +}].filter(x => x != null)); definePage(() => ({ title: file.value ? `${i18n.ts.file}: ${file.value.name}` : i18n.ts.file, diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index 437bf22363..38e3c7a11b 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -143,8 +143,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-
Assigned:
-
Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id).expiresAt).toLocaleString() }}
+
Assigned:
+
Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id)!.expiresAt!).toLocaleString() }}
Period: {{ i18n.ts.indefinitely }}
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index c5baeda7b0..06a28db088 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -140,15 +140,15 @@ function toggleDayOfWeek(ad, index) { function add() { ads.value.unshift({ - id: null, + id: '', memo: '', place: 'square', priority: 'middle', ratio: 1, url: '', - imageUrl: null, - expiresAt: null, - startsAt: null, + imageUrl: '', + expiresAt: new Date().toISOString(), + startsAt: new Date().toISOString(), dayOfWeek: 0, }); } @@ -160,7 +160,7 @@ function remove(ad) { }).then(({ canceled }) => { if (canceled) return; ads.value = ads.value.filter(x => x !== ad); - if (ad.id == null) return; + if (ad.id === '') return; os.apiWithDialog('admin/ad/delete', { id: ad.id, }).then(() => { @@ -170,7 +170,7 @@ function remove(ad) { } function save(ad) { - if (ad.id == null) { + if (ad.id === '') { misskeyApi('admin/ad/create', { ...ad, expiresAt: new Date(ad.expiresAt).getTime(), @@ -207,7 +207,7 @@ function save(ad) { } function more() { - misskeyApi('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => { + misskeyApi('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id !== '' ? ad : acc).id, publishing: publishing }).then(adsResponse => { if (adsResponse == null) return; ads.value = ads.value.concat(adsResponse.map(r => { const exdate = new Date(r.expiresAt); diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index 83438bb1d9..435dd9c462 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -25,11 +25,19 @@ SPDX-License-Identifier: AGPL-3.0-only - + - - -