Merge branch 'develop' into fix-10650

This commit is contained in:
かっこかり 2024-02-16 09:48:54 +09:00 committed by GitHub
commit 9ce9c6e2e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 1454 additions and 2279 deletions

View File

@ -23,16 +23,35 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/preview') if: github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/preview')
outputs: outputs:
is-allowed-user: ${{ steps.check-allowed-users.outputs.is-allowed-user }}
pr-ref: ${{ steps.get-ref.outputs.pr-ref }} pr-ref: ${{ steps.get-ref.outputs.pr-ref }}
wait_time: ${{ steps.get-wait-time.outputs.wait_time }} wait_time: ${{ steps.get-wait-time.outputs.wait_time }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Check allowed users
id: check-allowed-users
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ORG_ID: ${{ github.repository_owner_id }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
run: |
MEMBERSHIP_STATUS=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/organizations/$ORG_ID/public_members/$COMMENT_AUTHOR" \
-o /dev/null -w '%{http_code}\n' -s)
if [ "$MEMBERSHIP_STATUS" -eq 204 ]; then
echo "is-allowed-user=true" > $GITHUB_OUTPUT
else
echo "is-allowed-user=false" > $GITHUB_OUTPUT
fi
- name: Get PR ref - name: Get PR ref
id: get-ref id: get-ref
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
PR_NUMBER=$(jq --raw-output .issue.number $GITHUB_EVENT_PATH) PR_NUMBER=$(jq --raw-output .issue.number $GITHUB_EVENT_PATH)
PR_REF=$(gh pr view $PR_NUMBER --json headRefName -q '.headRefName') PR_REF=$(gh pr view $PR_NUMBER --json headRefName -q '.headRefName')
@ -40,13 +59,15 @@ jobs:
- name: Extract wait time - name: Extract wait time
id: get-wait-time id: get-wait-time
env:
COMMENT_BODY: ${{ github.event.comment.body }}
run: | run: |
COMMENT_BODY="${{ github.event.comment.body }}"
WAIT_TIME=$(echo "$COMMENT_BODY" | grep -oP '(?<=/preview\s)\d+' || echo "1800") WAIT_TIME=$(echo "$COMMENT_BODY" | grep -oP '(?<=/preview\s)\d+' || echo "1800")
echo "wait_time=$WAIT_TIME" > $GITHUB_OUTPUT echo "wait_time=$WAIT_TIME" > $GITHUB_OUTPUT
deploy-test-environment-pr-comment: deploy-test-environment-pr-comment:
needs: get-pr-ref needs: get-pr-ref
if: needs.get-pr-ref.outputs.is-allowed-user == 'true'
uses: joinmisskey/misskey-tga/.github/workflows/deploy-test-environment.yml@main uses: joinmisskey/misskey-tga/.github/workflows/deploy-test-environment.yml@main
with: with:
repository: ${{ github.repository }} repository: ${{ github.repository }}

113
.github/workflows/storybook.yml vendored Normal file
View File

@ -0,0 +1,113 @@
name: Storybook
on:
push:
branches:
- master
- develop
- dev/storybook8 # for testing
pull_request_target:
jobs:
build:
runs-on: ubuntu-latest
env:
NODE_OPTIONS: "--max_old_space_size=7168"
steps:
- uses: actions/checkout@v3.6.0
if: github.event_name != 'pull_request_target'
with:
fetch-depth: 0
submodules: true
- uses: actions/checkout@v3.6.0
if: github.event_name == 'pull_request_target'
with:
fetch-depth: 0
submodules: true
ref: "refs/pull/${{ github.event.number }}/merge"
- name: Checkout actual HEAD
if: github.event_name == 'pull_request_target'
id: rev
run: |
echo "base=$(git rev-list --parents -n1 HEAD | cut -d" " -f2)" >> $GITHUB_OUTPUT
git checkout $(git rev-list --parents -n1 HEAD | cut -d" " -f3)
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- name: Use Node.js 20.x
uses: actions/setup-node@v3.8.1
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: corepack enable
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Build misskey-js
run: pnpm --filter misskey-js build
- name: Build storybook
run: pnpm --filter frontend build-storybook
- name: Publish to Chromatic
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/master'
run: pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Publish to Chromatic
if: github.event_name != 'pull_request_target' && github.ref != 'refs/heads/master'
id: chromatic_push
run: |
DIFF="${{ github.event.before }} HEAD"
if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
DIFF="HEAD"
fi
CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))"
if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
fi
if pnpm --filter frontend chromatic -d storybook-static $(echo "$CHROMATIC_PARAMETER"); then
echo "success=true" >> $GITHUB_OUTPUT
else
echo "success=false" >> $GITHUB_OUTPUT
fi
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Publish to Chromatic
if: github.event_name == 'pull_request_target'
id: chromatic_pull_request
run: |
DIFF="${{ steps.rev.outputs.base }} HEAD"
if [ "$DIFF" = "0000000000000000000000000000000000000000 HEAD" ]; then
DIFF="HEAD"
fi
CHROMATIC_PARAMETER="$(node packages/frontend/.storybook/changes.js $(git diff-tree --no-commit-id --name-only -r $(echo "$DIFF") | xargs))"
if [ "$CHROMATIC_PARAMETER" = " --skip" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
fi
BRANCH="${{ github.event.pull_request.head.user.login }}:${{ github.event.pull_request.head.ref }}"
if [ "$BRANCH" = "misskey-dev:${{ github.event.pull_request.head.ref }}" ]; then
BRANCH="${{ github.event.pull_request.head.ref }}"
fi
pnpm --filter frontend chromatic --exit-once-uploaded -d storybook-static --branch-name $BRANCH $(echo "$CHROMATIC_PARAMETER")
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
- name: Notify that Chromatic detects changes
uses: actions/github-script@v6.4.0
if: github.event_name != 'pull_request_target' && steps.chromatic_push.outputs.success == 'false'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.repos.createCommitComment({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
body: 'Chromatic detects changes. Please [review the changes on Chromatic](https://www.chromatic.com/builds?appId=6428f7d7b962f0b79f97d6e4).'
})
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: storybook
path: packages/frontend/storybook-static

View File

@ -14,9 +14,9 @@ export type FanoutTimelineName =
| `homeTimeline:${string}` | `homeTimeline:${string}`
| `homeTimelineWithFiles:${string}` // only notes with files are included | `homeTimelineWithFiles:${string}` // only notes with files are included
// local timeline // local timeline
| 'localTimeline' // replies are not included | `localTimeline` // replies are not included
| 'localTimelineWithFiles' // only non-reply notes with files are included | `localTimelineWithFiles` // only non-reply notes with files are included
| 'localTimelineWithReplies' // only replies are included | `localTimelineWithReplies` // only replies are included
| `localTimelineWithReplyTo:${string}` // Only replies to specific local user are included. Parameter is reply user id. | `localTimelineWithReplyTo:${string}` // Only replies to specific local user are included. Parameter is reply user id.
// antenna // antenna

View File

@ -12,11 +12,11 @@ import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js';
import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js'; import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
import type httpSignature from '@peertube/http-signature'; import type httpSignature from '@peertube/http-signature';
import type * as Bull from 'bullmq'; import type * as Bull from 'bullmq';
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
@Injectable() @Injectable()
export class QueueService { export class QueueService {

View File

@ -31,7 +31,7 @@ export class ApMfmService {
const parsed = mfm.parse(srcMfm); const parsed = mfm.parse(srcMfm);
if (!apAppend && parsed.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) { if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
noMisskeyContent = true; noMisskeyContent = true;
} }

View File

@ -34,7 +34,7 @@ export class RelationshipProcessorService {
@bindThis @bindThis
public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> { public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? 'with replies' : 'without replies'}`); this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`);
await this.userFollowingService.follow(job.data.from, job.data.to, { await this.userFollowingService.follow(job.data.from, job.data.to, {
requestId: job.data.requestId, requestId: job.data.requestId,
silent: job.data.silent, silent: job.data.silent,

View File

@ -38,7 +38,7 @@ export class NodeinfoServerService {
public getLinks() { public getLinks() {
return [{ return [{
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
href: this.config.url + nodeinfo2_1path, href: this.config.url + nodeinfo2_1path
}, { }, {
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
href: this.config.url + nodeinfo2_0path, href: this.config.url + nodeinfo2_0path,

View File

@ -25,8 +25,8 @@ export const meta = {
items: { items: {
type: 'object', type: 'object',
}, },
}, }
}, }
}, },
} as const; } as const;

View File

@ -71,7 +71,7 @@ export const paramDef = {
type: 'object', type: 'object',
properties: { properties: {
userId: { type: 'string', format: 'misskey:id' }, userId: { type: 'string', format: 'misskey:id' },
withReplies: { type: 'boolean' }, withReplies: { type: 'boolean' }
}, },
required: ['userId'], required: ['userId'],
} as const; } as const;

View File

@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UsersRepository } from '@/models/_.js'; import type { UsersRepository } from '@/models/_.js';
import { safeForSql } from '@/misc/safe-for-sql.js'; import { safeForSql } from "@/misc/safe-for-sql.js";
import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';

View File

@ -14,7 +14,7 @@ export const meta = {
tags: ['account'], tags: ['account'],
requireCredential: true, requireCredential: true,
kind: 'read:account', kind: "read:account",
res: { res: {
type: 'object', type: 'object',

View File

@ -38,9 +38,9 @@ export const meta = {
type: 'array', type: 'array',
uniqueItems: true, uniqueItems: true,
items: { items: {
type: 'string', type: 'string'
},
}, },
}
}, },
}, },
}, },

View File

@ -35,7 +35,7 @@ export const meta = {
type: 'array', type: 'array',
uniqueItems: true, uniqueItems: true,
items: { items: {
type: 'string', type: 'string'
}, },
}, },
isAuthorized: { isAuthorized: {

View File

@ -22,7 +22,7 @@ export const meta = {
res: { res: {
type: 'object', type: 'object',
}, }
} as const; } as const;
export const paramDef = { export const paramDef = {

View File

@ -22,7 +22,7 @@ export const meta = {
res: { res: {
type: 'object', type: 'object',
}, }
} as const; } as const;
export const paramDef = { export const paramDef = {

View File

@ -22,8 +22,8 @@ export const meta = {
type: 'array', type: 'array',
items: { items: {
type: 'string', type: 'string',
}, }
}, }
}, },
domain: { domain: {
type: 'string', type: 'string',
@ -31,7 +31,7 @@ export const meta = {
}, },
}, },
}, },
}, }
} as const; } as const;
export const paramDef = { export const paramDef = {

View File

@ -33,7 +33,7 @@ export const meta = {
properties: { properties: {
id: { id: {
type: 'string', type: 'string',
format: 'misskey:id', format: 'misskey:id'
}, },
userId: { userId: {
type: 'string', type: 'string',
@ -45,7 +45,7 @@ export const meta = {
items: { items: {
type: 'string', type: 'string',
enum: webhookEventTypes, enum: webhookEventTypes,
}, }
}, },
url: { type: 'string' }, url: { type: 'string' },
secret: { type: 'string' }, secret: { type: 'string' },

View File

@ -23,7 +23,7 @@ export const meta = {
properties: { properties: {
id: { id: {
type: 'string', type: 'string',
format: 'misskey:id', format: 'misskey:id'
}, },
userId: { userId: {
type: 'string', type: 'string',
@ -35,7 +35,7 @@ export const meta = {
items: { items: {
type: 'string', type: 'string',
enum: webhookEventTypes, enum: webhookEventTypes,
}, }
}, },
url: { type: 'string' }, url: { type: 'string' },
secret: { type: 'string' }, secret: { type: 'string' },
@ -43,8 +43,8 @@ export const meta = {
latestSentAt: { type: 'string', format: 'date-time', nullable: true }, latestSentAt: { type: 'string', format: 'date-time', nullable: true },
latestStatus: { type: 'integer', nullable: true }, latestStatus: { type: 'integer', nullable: true },
}, },
}, }
}, }
} as const; } as const;
export const paramDef = { export const paramDef = {

View File

@ -30,7 +30,7 @@ export const meta = {
properties: { properties: {
id: { id: {
type: 'string', type: 'string',
format: 'misskey:id', format: 'misskey:id'
}, },
userId: { userId: {
type: 'string', type: 'string',
@ -42,7 +42,7 @@ export const meta = {
items: { items: {
type: 'string', type: 'string',
enum: webhookEventTypes, enum: webhookEventTypes,
}, }
}, },
url: { type: 'string' }, url: { type: 'string' },
secret: { type: 'string' }, secret: { type: 'string' },

View File

@ -47,7 +47,7 @@ export const meta = {
bothWithRepliesAndWithFiles: { bothWithRepliesAndWithFiles: {
message: 'Specifying both withReplies and withFiles is not supported', message: 'Specifying both withReplies and withFiles is not supported',
code: 'BOTH_WITH_REPLIES_AND_WITH_FILES', code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f', id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f'
}, },
}, },
} as const; } as const;

View File

@ -18,7 +18,7 @@ export const meta = {
properties: { properties: {
id: { id: {
type: 'string', type: 'string',
format: 'misskey:id', format: 'misskey:id'
}, },
required: { required: {
type: 'boolean', type: 'boolean',
@ -34,8 +34,8 @@ export const meta = {
default: 'hello', default: 'hello',
nullable: true, nullable: true,
}, },
}, }
}, }
} as const; } as const;
export const paramDef = { export const paramDef = {

View File

@ -7,10 +7,11 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { MiNote } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js';
import type { Packed } from '@/misc/json-schema.js';
import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js'; import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import type{ Repository } from 'typeorm'; import type{ Repository } from 'typeorm'
import type { Packed } from '@/misc/json-schema.js';
describe('Drive', () => { describe('Drive', () => {
let Notes: Repository<MiNote>; let Notes: Repository<MiNote>;
@ -30,7 +31,7 @@ describe('Drive', () => {
const marker = Math.random().toString(); const marker = Math.random().toString();
const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'; const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'
const catcher = makeStreamCatcher( const catcher = makeStreamCatcher(
alice, alice,
@ -50,7 +51,7 @@ describe('Drive', () => {
assert.strictEqual(res.status, 204); assert.strictEqual(res.status, 204);
assert.strictEqual(file.name, 'Lenna.jpg'); assert.strictEqual(file.name, 'Lenna.jpg');
assert.strictEqual(file.type, 'image/jpeg'); assert.strictEqual(file.type, 'image/jpeg');
}); })
test('ローカルからアップロードできる', async () => { test('ローカルからアップロードできる', async () => {
// APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする // APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする
@ -58,27 +59,27 @@ describe('Drive', () => {
const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' }); const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' });
assert.strictEqual(res.body?.name, 'テスト画像.jpg'); assert.strictEqual(res.body?.name, 'テスト画像.jpg');
assert.strictEqual(res.body.type, 'image/jpeg'); assert.strictEqual(res.body?.type, 'image/jpeg');
}); })
test('添付ノート一覧を取得できる', async () => { test('添付ノート一覧を取得できる', async () => {
const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id); const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id)
const note0 = await post(alice, { fileIds: [ids[0]] }); const note0 = await post(alice, { fileIds: [ids[0]] });
const note1 = await post(alice, { fileIds: [ids[0], ids[1]] }); const note1 = await post(alice, { fileIds: [ids[0], ids[1]] });
const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice); const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice);
assert.strictEqual(attached0.body.length, 2); assert.strictEqual(attached0.body.length, 2);
assert.strictEqual(attached0.body[0].id, note1.id); assert.strictEqual(attached0.body[0].id, note1.id)
assert.strictEqual(attached0.body[1].id, note0.id); assert.strictEqual(attached0.body[1].id, note0.id)
const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice); const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice);
assert.strictEqual(attached1.body.length, 1); assert.strictEqual(attached1.body.length, 1);
assert.strictEqual(attached1.body[0].id, note1.id); assert.strictEqual(attached1.body[0].id, note1.id)
const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice); const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice);
assert.strictEqual(attached2.body.length, 0); assert.strictEqual(attached2.body.length, 0)
}); })
test('添付ノート一覧は他の人から見えない', async () => { test('添付ノート一覧は他の人から見えない', async () => {
const file = await uploadFile(alice); const file = await uploadFile(alice);
@ -88,6 +89,7 @@ describe('Drive', () => {
const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob); const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual('error' in res.body, true); assert.strictEqual('error' in res.body, true);
});
})
}); });

View File

@ -13,10 +13,10 @@ import fetch, { File, RequestInit } from 'node-fetch';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { DEFAULT_POLICIES } from '@/core/RoleService.js';
import { Packed } from '@/misc/json-schema.js';
import { entities } from '../src/postgres.js'; import { entities } from '../src/postgres.js';
import { loadConfig } from '../src/config.js'; import { loadConfig } from '../src/config.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { Packed } from '@/misc/json-schema.js';
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js'; export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
@ -124,8 +124,8 @@ function timeoutPromise<T>(p: Promise<T>, timeout: number): Promise<T> {
return Promise.race([ return Promise.race([
p, p,
new Promise((reject) =>{ new Promise((reject) =>{
setTimeout(() => { reject(new Error('timed out')); }, timeout); setTimeout(() => { reject(new Error('timed out')); }, timeout)
}) as never, }) as never
]); ]);
} }
@ -343,7 +343,7 @@ export const uploadUrl = async (user: UserToken, url: string): Promise<Packed<'D
'main', 'main',
(msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker, (msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker,
(msg) => msg.body.file as Packed<'DriveFile'>, (msg) => msg.body.file as Packed<'DriveFile'>,
60 * 1000, 60 * 1000
); );
await api('drive/files/upload-from-url', { await api('drive/files/upload-from-url', {
@ -439,15 +439,15 @@ export function makeStreamCatcher<T>(
cond: (message: Record<string, any>) => boolean, cond: (message: Record<string, any>) => boolean,
extractor: (message: Record<string, any>) => T, extractor: (message: Record<string, any>) => T,
timeout = 60 * 1000): Promise<T> { timeout = 60 * 1000): Promise<T> {
let ws: WebSocket; let ws: WebSocket
const p = new Promise<T>(async (resolve) => { const p = new Promise<T>(async (resolve) => {
ws = await connectStream(user, channel, (msg) => { ws = await connectStream(user, channel, (msg) => {
if (cond(msg)) { if (cond(msg)) {
resolve(extractor(msg)); resolve(extractor(msg))
} }
}); });
}).finally(() => { }).finally(() => {
ws.close(); ws?.close();
}); });
return timeoutPromise(p, timeout); return timeoutPromise(p, timeout);

View File

@ -3,25 +3,28 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { resolve } from 'node:path'; import { createRequire } from 'node:module';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import type { StorybookConfig } from '@storybook/vue3-vite'; import type { StorybookConfig } from '@storybook/vue3-vite';
import { type Plugin, mergeConfig } from 'vite'; import { type Plugin, mergeConfig } from 'vite';
import turbosnap from 'vite-plugin-turbosnap'; import turbosnap from 'vite-plugin-turbosnap';
const dirname = fileURLToPath(new URL('.', import.meta.url)); const require = createRequire(import.meta.url);
const _dirname = fileURLToPath(new URL('.', import.meta.url));
const config = { const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [ addons: [
'@storybook/addon-essentials', getAbsolutePath('@storybook/addon-essentials'),
'@storybook/addon-interactions', getAbsolutePath('@storybook/addon-interactions'),
'@storybook/addon-links', getAbsolutePath('@storybook/addon-links'),
'@storybook/addon-storysource', getAbsolutePath('@storybook/addon-storysource'),
resolve(dirname, '../node_modules/storybook-addon-misskey-theme'), getAbsolutePath('@storybook/addon-mdx-gfm'),
resolve(_dirname, '../node_modules/storybook-addon-misskey-theme'),
], ],
framework: { framework: {
name: '@storybook/vue3-vite', name: getAbsolutePath('@storybook/vue3-vite') as '@storybook/vue3-vite',
options: {}, options: {},
}, },
docs: { docs: {
@ -37,10 +40,13 @@ const config = {
} }
return mergeConfig(config, { return mergeConfig(config, {
plugins: [ plugins: [
{
// XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8 // XXX: https://github.com/IanVS/vite-plugin-turbosnap/issues/8
(turbosnap as any as typeof turbosnap['default'])({ ...(turbosnap as any as typeof turbosnap['default'])({
rootDir: config.root ?? process.cwd(), rootDir: config.root ?? process.cwd(),
}), }),
name: 'fake-turbosnap',
},
], ],
build: { build: {
target: [ target: [
@ -53,3 +59,7 @@ const config = {
}, },
} satisfies StorybookConfig; } satisfies StorybookConfig;
export default config; export default config;
function getAbsolutePath(value: string): string {
return dirname(require.resolve(join(value, 'package.json')));
}

View File

@ -3,8 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { addons } from '@storybook/addons';
import { FORCE_REMOUNT } from '@storybook/core-events'; import { FORCE_REMOUNT } from '@storybook/core-events';
import { addons } from '@storybook/preview-api';
import { type Preview, setup } from '@storybook/vue3'; import { type Preview, setup } from '@storybook/vue3';
import isChromatic from 'chromatic/isChromatic'; import isChromatic from 'chromatic/isChromatic';
import { initialize, mswDecorator } from 'msw-storybook-addon'; import { initialize, mswDecorator } from 'msw-storybook-addon';

View File

@ -8,7 +8,7 @@
"build": "vite build", "build": "vite build",
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"", "storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js", "build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
"build-storybook": "pnpm build-storybook-pre && storybook build", "build-storybook": "pnpm build-storybook-pre && storybook build --webpack-stats-json storybook-static",
"chromatic": "chromatic", "chromatic": "chromatic",
"test": "vitest --run --globals", "test": "vitest --run --globals",
"test-and-coverage": "vitest --run --coverage --globals", "test-and-coverage": "vitest --run --coverage --globals",
@ -78,24 +78,24 @@
"devDependencies": { "devDependencies": {
"@misskey-dev/eslint-plugin": "1.0.0", "@misskey-dev/eslint-plugin": "1.0.0",
"@misskey-dev/summaly": "5.0.3", "@misskey-dev/summaly": "5.0.3",
"@storybook/addon-actions": "7.6.10", "@storybook/addon-actions": "8.0.0-beta.2",
"@storybook/addon-essentials": "7.6.10", "@storybook/addon-essentials": "8.0.0-beta.2",
"@storybook/addon-interactions": "7.6.10", "@storybook/addon-interactions": "8.0.0-beta.2",
"@storybook/addon-links": "7.6.10", "@storybook/addon-links": "8.0.0-beta.2",
"@storybook/addon-storysource": "7.6.10", "@storybook/addon-mdx-gfm": "8.0.0-beta.2",
"@storybook/addons": "7.6.10", "@storybook/addon-storysource": "8.0.0-beta.2",
"@storybook/blocks": "7.6.10", "@storybook/blocks": "8.0.0-beta.2",
"@storybook/core-events": "7.6.10", "@storybook/components": "8.0.0-beta.2",
"@storybook/jest": "0.2.3", "@storybook/core-events": "8.0.0-beta.2",
"@storybook/manager-api": "7.6.10", "@storybook/manager-api": "8.0.0-beta.2",
"@storybook/preview-api": "7.6.10", "@storybook/preview-api": "8.0.0-beta.2",
"@storybook/react": "7.6.10", "@storybook/react": "8.0.0-beta.2",
"@storybook/react-vite": "7.6.10", "@storybook/react-vite": "8.0.0-beta.2",
"@storybook/testing-library": "0.2.2", "@storybook/test": "8.0.0-beta.2",
"@storybook/theming": "7.6.10", "@storybook/theming": "8.0.0-beta.2",
"@storybook/types": "7.6.10", "@storybook/types": "8.0.0-beta.2",
"@storybook/vue3": "7.6.10", "@storybook/vue3": "8.0.0-beta.2",
"@storybook/vue3-vite": "7.6.10", "@storybook/vue3-vite": "8.0.0-beta.2",
"@testing-library/vue": "8.0.2", "@testing-library/vue": "8.0.2",
"@types/escape-regexp": "0.0.3", "@types/escape-regexp": "0.0.3",
"@types/estree": "1.0.5", "@types/estree": "1.0.5",
@ -129,12 +129,12 @@
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"start-server-and-test": "2.0.3", "start-server-and-test": "2.0.3",
"storybook": "7.6.10", "storybook": "8.0.0-beta.2",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.6", "vitest": "0.34.6",
"vitest-fetch-mock": "0.2.2", "vitest-fetch-mock": "0.2.2",
"vue-component-type-helpers": "^1.8.27", "vue-component-type-helpers": "1.8.27",
"vue-eslint-parser": "9.4.2", "vue-eslint-parser": "9.4.2",
"vue-tsc": "1.8.27" "vue-tsc": "1.8.27"
} }

View File

@ -5,8 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest'; import { expect, userEvent, waitFor, within } from '@storybook/test';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js'; import { userDetailed } from '../../.storybook/fakes.js';

View File

@ -123,7 +123,7 @@ function callback(response?: string) {
function onReceivedMessage(message: MessageEvent) { function onReceivedMessage(message: MessageEvent) {
if (message.data.token) { if (message.data.token) {
if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) { if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) {
callback(<string>message.data.token); callback(message.data.token);
} }
} }
} }

View File

@ -4,8 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { expect } from '@storybook/jest'; import { expect, userEvent, waitFor, within } from '@storybook/test';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import { galleryPost } from '../../.storybook/fakes.js'; import { galleryPost } from '../../.storybook/fakes.js';
import MkGalleryPostPreview from './MkGalleryPostPreview.vue'; import MkGalleryPostPreview from './MkGalleryPostPreview.vue';

View File

@ -4,8 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { expect } from '@storybook/jest'; import { expect, userEvent, waitFor, within } from '@storybook/test';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import { onBeforeUnmount } from 'vue'; import { onBeforeUnmount } from 'vue';
import MkSignupServerRules from './MkSignupDialog.rules.vue'; import MkSignupServerRules from './MkSignupDialog.rules.vue';

View File

@ -4,8 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { expect } from '@storybook/jest'; import { expect, userEvent, within } from '@storybook/test';
import { userEvent, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import MkA from './MkA.vue'; import MkA from './MkA.vue';
import { tick } from '@/scripts/test-utils.js'; import { tick } from '@/scripts/test-utils.js';

View File

@ -5,8 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest'; import { expect, waitFor } from '@storybook/test';
import { waitFor } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import MkError from './MkError.vue'; import MkError from './MkError.vue';
export const Default = { export const Default = {

View File

@ -5,8 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import { within } from '@storybook/testing-library'; import { expect, within } from '@storybook/test';
import { expect } from '@storybook/jest';
import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js'; import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
export const Default = { export const Default = {
render(args) { render(args) {

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { waitFor } from '@storybook/testing-library'; import { waitFor } from '@storybook/test';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import MkPageHeader from './MkPageHeader.vue'; import MkPageHeader from './MkPageHeader.vue';
export const Empty = { export const Empty = {

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { expect } from '@storybook/jest'; import { expect } from '@storybook/test';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import MkTime from './MkTime.vue'; import MkTime from './MkTime.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';

View File

@ -4,8 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { expect } from '@storybook/jest'; import { expect, userEvent, waitFor, within } from '@storybook/test';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import { HttpResponse, http } from 'msw'; import { HttpResponse, http } from 'msw';
import { commonHandlers } from '../../../.storybook/mocks.js'; import { commonHandlers } from '../../../.storybook/mocks.js';

View File

@ -4,7 +4,7 @@
*/ */
/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable @typescript-eslint/explicit-function-return-type */
import { expect } from '@storybook/jest'; import { expect } from '@storybook/test';
import { StoryObj } from '@storybook/vue3'; import { StoryObj } from '@storybook/vue3';
import { userDetailed } from '../../../.storybook/fakes.js'; import { userDetailed } from '../../../.storybook/fakes.js';
import MkUserName from './MkUserName.vue'; import MkUserName from './MkUserName.vue';

View File

@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>{{ i18n.ts.youShouldUpgradeClient }}</div> <div>{{ i18n.ts.youShouldUpgradeClient }}</div>
<MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton> <MkButton style="margin: 8px auto;" @click="reload">{{ i18n.ts.reload }}</MkButton>
</template> </template>
<div><MkA to="/docs/general/troubleshooting" class="_link">{{ i18n.ts.troubleshooting }}</MkA></div> <div><MkLink url="https://misskey-hub.net/docs/for-users/resources/troubleshooting/" target="_blank">{{ i18n.ts.troubleshooting }}</MkLink></div>
<div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div> <div v-if="error" style="opacity: 0.7;">ERROR: {{ error }}</div>
</div> </div>
</div> </div>
@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkLink from '@/components/MkLink.vue';
import { version } from '@/config.js'; import { version } from '@/config.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import { unisonReload } from '@/scripts/unison-reload.js'; import { unisonReload } from '@/scripts/unison-reload.js';

View File

@ -101,7 +101,7 @@ const announcements = {
limit: 10, limit: 10,
}; };
const isTimelineAvailable = ref(instance.policies.ltlAvailable || instance.policies.gtlAvailable); const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
const showMenu = ref(false); const showMenu = ref(false);
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);

File diff suppressed because it is too large Load Diff