diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index e8b65dc3b9..5acad83336 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,3 +2,7 @@ contact_links: - name: 💬 Misskey official Discord url: https://discord.gg/Wp8gVStHW3 about: Chat freely about Misskey + # 仮 + - name: 💬 Start discussion + url: https://github.com/misskey-dev/misskey/discussions + about: The official forum to join conversation and ask question diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d482d50d0..097a69b687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正 ### Client +- `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968) ### Server diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index d366ce48d0..c72bdaa727 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -82,34 +82,14 @@ import { MiReversiGame } from '@/models/ReversiGame.js'; import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; export interface MiRepository { - createTableColumnNames(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder): string[]; - createTableColumnNamesWithPrimaryKey(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder): string[]; + createTableColumnNames(this: Repository & MiRepository): string[]; insertOne(this: Repository & MiRepository, entity: QueryDeepPartialEntity, findOptions?: Pick, 'relations'>): Promise; selectAliasColumnNames(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder, builder: SelectQueryBuilder): void; } export const miRepository = { - createTableColumnNames(queryBuilder) { - // @ts-expect-error -- protected - const insertedColumns = queryBuilder.getInsertedColumns(); - if (insertedColumns.length) { - return insertedColumns.map(column => column.databaseName); - } - if (!queryBuilder.expressionMap.mainAlias?.hasMetadata && !queryBuilder.expressionMap.insertColumns.length) { - // @ts-expect-error -- protected - const valueSets = queryBuilder.getValueSets(); - if (valueSets.length === 1) { - return Object.keys(valueSets[0]); - } - } - return queryBuilder.expressionMap.insertColumns; - }, - createTableColumnNamesWithPrimaryKey(queryBuilder) { - const columnNames = this.createTableColumnNames(queryBuilder); - if (!columnNames.includes('id')) { - columnNames.unshift('id'); - } - return columnNames; + createTableColumnNames() { + return this.metadata.columns.filter(column => column.isSelect && !column.isVirtual).map(column => column.databaseName); }, async insertOne(entity, findOptions?) { const queryBuilder = this.createQueryBuilder().insert().values(entity); @@ -117,7 +97,7 @@ export const miRepository = { const mainAlias = queryBuilder.expressionMap.mainAlias!; const name = mainAlias.name; mainAlias.name = 't'; - const columnNames = this.createTableColumnNamesWithPrimaryKey(queryBuilder); + const columnNames = this.createTableColumnNames(); queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2)); const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames }); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -138,7 +118,7 @@ export const miRepository = { selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName); return builder.select(selection, selectionAliasName); }; - for (const columnName of this.createTableColumnNamesWithPrimaryKey(queryBuilder)) { + for (const columnName of this.createTableColumnNames()) { selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`); } }, diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index fdb155261b..9d789a34ff 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -125,6 +125,35 @@ export function file(isSensitive = false) { }; } +export function federationInstance(): entities.FederationInstance { + return { + id: 'someinstanceid', + firstRetrievedAt: '2021-01-01T00:00:00.000Z', + host: 'misskey-hub.net', + usersCount: 10, + notesCount: 20, + followingCount: 5, + followersCount: 15, + isNotResponding: false, + isSuspended: false, + suspensionState: 'none', + isBlocked: false, + softwareName: 'misskey', + softwareVersion: '2024.5.0', + openRegistrations: false, + name: 'Misskey Hub', + description: '', + maintainerName: '', + maintainerEmail: '', + isSilenced: false, + iconUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true', + faviconUrl: '', + themeColor: '', + infoUpdatedAt: '', + latestRequestReceivedAt: '', + }; +} + export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed { return { id, diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index d21eea9d17..7b6c86447e 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -403,6 +403,7 @@ function toStories(component: string): Promise { glob('src/components/MkSignupServerRules.vue'), glob('src/components/MkUserSetupDialog.vue'), glob('src/components/MkUserSetupDialog.*.vue'), + glob('src/components/MkInstanceCardMini.vue'), glob('src/components/MkInviteCode.vue'), glob('src/pages/user/home.vue'), ]); diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts index 6b0cc3b858..d1d17f6dc0 100644 --- a/packages/frontend/src/components/MkChart.stories.impl.ts +++ b/packages/frontend/src/components/MkChart.stories.impl.ts @@ -29,7 +29,7 @@ function getChartArray(seed: string, limit: number, option?: { accumulate?: bool return array; } -function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record }): HttpResponseResolver { +export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record }): HttpResponseResolver { return ({ request }) => { action(`GET ${request.url}`)(); const limitParam = new URL(request.url).searchParams.get('limit'); diff --git a/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts new file mode 100644 index 0000000000..a069a0eb8e --- /dev/null +++ b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { StoryObj } from '@storybook/vue3'; +import { HttpResponse, http } from 'msw'; +import { federationInstance } from '../../.storybook/fakes.js'; +import { commonHandlers } from '../../.storybook/mocks.js'; +import MkInstanceCardMini from './MkInstanceCardMini.vue'; +import { getChartResolver } from './MkChart.stories.impl.js'; +export const Default = { + render(args) { + return { + components: { + MkInstanceCardMini, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '', + }; + }, + args: { + instance: federationInstance(), + }, + parameters: { + layout: 'centered', + msw: { + handlers: [ + ...commonHandlers, + http.get('/undefined/preview.webp', async ({ request }) => { + const urlStr = new URL(request.url).searchParams.get('url'); + if (urlStr == null) { + return new HttpResponse(null, { status: 404 }); + } + const url = new URL(urlStr); + + if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) { + const image = await (await fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob(); + return new HttpResponse(image, { + headers: { + 'Content-Type': 'image/jpeg', + }, + }); + } else { + return new HttpResponse(null, { status: 404 }); + } + }), + http.get('/api/charts/instance', getChartResolver(['requests.received'])), + ], + }, + }, +} satisfies StoryObj; diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index e26aef0f69..17c974dd04 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -29,8 +29,8 @@ const chartValues = ref(null); misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く - res['requests.received'].splice(0, 1); - chartValues.value = res['requests.received']; + res.requests.received.splice(0, 1); + chartValues.value = res.requests.received; }); function getInstanceIcon(instance): string {