Merge pull request MisskeyIO#414 from merge-upstream
This commit is contained in:
commit
de54df9cd7
@ -73,6 +73,8 @@
|
|||||||
- Fix: キャプションが空の画像をクロップするとキャプションにnullという文字列が入ってしまう問題の修正
|
- Fix: キャプションが空の画像をクロップするとキャプションにnullという文字列が入ってしまう問題の修正
|
||||||
- Fix: プロフィールを編集してもリロードするまで反映されない問題を修正
|
- Fix: プロフィールを編集してもリロードするまで反映されない問題を修正
|
||||||
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
- Fix: エラー画像URLを設定した後解除すると,デフォルトの画像が表示されない問題の修正
|
||||||
|
- Fix: MkCodeEditorで行がずれていってしまう問題の修正
|
||||||
|
- Fix: Summaly proxy利用時にプレイヤーが動作しないことがあるのを修正 #13196
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
|
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
|
||||||
@ -87,6 +89,7 @@
|
|||||||
- Fix: properly handle cc followers
|
- Fix: properly handle cc followers
|
||||||
- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec
|
- Fix: ジョブに関する設定の名前を修正 relashionshipJobPerSec -> relationshipJobPerSec
|
||||||
- Fix: コントロールパネル->モデレーション->「誰でも新規登録できるようにする」の初期値をONからOFFに変更 #13122
|
- Fix: コントロールパネル->モデレーション->「誰でも新規登録できるようにする」の初期値をONからOFFに変更 #13122
|
||||||
|
- Enhance: 連合向けのノート配信を軽量化 #13192
|
||||||
|
|
||||||
### Service Worker
|
### Service Worker
|
||||||
- Enhance: オフライン表示のデザインを改善・多言語対応
|
- Enhance: オフライン表示のデザインを改善・多言語対応
|
||||||
|
@ -286,18 +286,17 @@ export const argTypes = {
|
|||||||
min: 1,
|
min: 1,
|
||||||
max: 4,
|
max: 4,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Also, you can use msw to mock API requests in the storybook. Creating a `MyComponent.stories.msw.ts` file to define the mock handlers.
|
Also, you can use msw to mock API requests in the storybook. Creating a `MyComponent.stories.msw.ts` file to define the mock handlers.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
export const handlers = [
|
export const handlers = [
|
||||||
rest.post('/api/notes/timeline', (req, res, ctx) => {
|
http.post('/api/notes/timeline', ({ request }) => {
|
||||||
return res(
|
return HttpResponse.json([]);
|
||||||
ctx.json([]),
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/misskey-dev/misskey.git"
|
"url": "https://github.com/misskey-dev/misskey.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.12.1",
|
"packageManager": "pnpm@8.15.1",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/frontend",
|
"packages/frontend",
|
||||||
"packages/backend",
|
"packages/backend",
|
||||||
@ -54,7 +54,7 @@
|
|||||||
"cssnano": "6.0.3",
|
"cssnano": "6.0.3",
|
||||||
"execa": "8.0.1",
|
"execa": "8.0.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"postcss": "8.4.33",
|
"postcss": "8.4.35",
|
||||||
"terser": "5.27.0",
|
"terser": "5.27.0",
|
||||||
"typescript": "5.3.3"
|
"typescript": "5.3.3"
|
||||||
},
|
},
|
||||||
@ -62,7 +62,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.6.3",
|
"cypress": "13.6.4",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"start-server-and-test": "2.0.3"
|
"start-server-and-test": "2.0.3"
|
||||||
|
@ -81,9 +81,9 @@
|
|||||||
"@fastify/view": "8.2.0",
|
"@fastify/view": "8.2.0",
|
||||||
"@misskey-dev/sharp-read-bmp": "^1.1.1",
|
"@misskey-dev/sharp-read-bmp": "^1.1.1",
|
||||||
"@misskey-dev/summaly": "^5.0.3",
|
"@misskey-dev/summaly": "^5.0.3",
|
||||||
"@nestjs/common": "10.3.1",
|
"@nestjs/common": "10.3.2",
|
||||||
"@nestjs/core": "10.3.1",
|
"@nestjs/core": "10.3.2",
|
||||||
"@nestjs/testing": "10.3.1",
|
"@nestjs/testing": "10.3.2",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@simplewebauthn/server": "9.0.1",
|
"@simplewebauthn/server": "9.0.1",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
@ -98,9 +98,9 @@
|
|||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "5.1.5",
|
"bullmq": "5.1.9",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.1",
|
"cbor": "9.0.2",
|
||||||
"chalk": "5.3.0",
|
"chalk": "5.3.0",
|
||||||
"chalk-template": "1.1.0",
|
"chalk-template": "1.1.0",
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "3.5.3",
|
||||||
@ -115,7 +115,7 @@
|
|||||||
"file-type": "19.0.0",
|
"file-type": "19.0.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"got": "14.1.0",
|
"got": "14.2.0",
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "10.0.3",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"http-link-header": "1.1.1",
|
"http-link-header": "1.1.1",
|
||||||
@ -127,7 +127,7 @@
|
|||||||
"jsdom": "23.2.0",
|
"jsdom": "23.2.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jsonld": "8.3.2",
|
"jsonld": "8.3.2",
|
||||||
"jsrsasign": "11.0.0",
|
"jsrsasign": "11.1.0",
|
||||||
"meilisearch": "0.37.0",
|
"meilisearch": "0.37.0",
|
||||||
"mfm-js": "0.24.0",
|
"mfm-js": "0.24.0",
|
||||||
"microformats-parser": "2.0.2",
|
"microformats-parser": "2.0.2",
|
||||||
@ -135,10 +135,10 @@
|
|||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"nanoid": "5.0.4",
|
"nanoid": "5.0.5",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "6.9.8",
|
"nodemailer": "6.9.9",
|
||||||
"nsfwjs": "2.4.2",
|
"nsfwjs": "2.4.2",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.0",
|
||||||
"oauth2orize": "1.12.0",
|
"oauth2orize": "1.12.0",
|
||||||
@ -147,7 +147,7 @@
|
|||||||
"otpauth": "9.2.2",
|
"otpauth": "9.2.2",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.3",
|
"pg": "8.11.3",
|
||||||
"pino": "8.17.2",
|
"pino": "8.18.0",
|
||||||
"pino-pretty": "10.3.1",
|
"pino-pretty": "10.3.1",
|
||||||
"pkce-challenge": "4.1.0",
|
"pkce-challenge": "4.1.0",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
@ -186,9 +186,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.7.0",
|
"@jest/globals": "29.7.0",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@nestjs/platform-express": "10.3.1",
|
"@nestjs/platform-express": "10.3.2",
|
||||||
"@simplewebauthn/types": "9.0.1",
|
"@simplewebauthn/types": "9.0.1",
|
||||||
"@swc/jest": "0.2.31",
|
"@swc/jest": "0.2.36",
|
||||||
"@types/accepts": "1.3.7",
|
"@types/accepts": "1.3.7",
|
||||||
"@types/archiver": "6.0.2",
|
"@types/archiver": "6.0.2",
|
||||||
"@types/bcryptjs": "2.4.6",
|
"@types/bcryptjs": "2.4.6",
|
||||||
@ -197,14 +197,14 @@
|
|||||||
"@types/content-disposition": "0.5.8",
|
"@types/content-disposition": "0.5.8",
|
||||||
"@types/fluent-ffmpeg": "2.1.24",
|
"@types/fluent-ffmpeg": "2.1.24",
|
||||||
"@types/http-link-header": "1.0.5",
|
"@types/http-link-header": "1.0.5",
|
||||||
"@types/jest": "29.5.11",
|
"@types/jest": "29.5.12",
|
||||||
"@types/js-yaml": "4.0.9",
|
"@types/js-yaml": "4.0.9",
|
||||||
"@types/jsdom": "21.1.6",
|
"@types/jsdom": "21.1.6",
|
||||||
"@types/jsonld": "1.5.13",
|
"@types/jsonld": "1.5.13",
|
||||||
"@types/jsrsasign": "10.5.12",
|
"@types/jsrsasign": "10.5.12",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "20.11.10",
|
"@types/node": "20.11.16",
|
||||||
"@types/nodemailer": "6.4.14",
|
"@types/nodemailer": "6.4.14",
|
||||||
"@types/oauth": "0.9.4",
|
"@types/oauth": "0.9.4",
|
||||||
"@types/oauth2orize": "1.11.3",
|
"@types/oauth2orize": "1.11.3",
|
||||||
@ -216,7 +216,7 @@
|
|||||||
"@types/random-seed": "0.3.5",
|
"@types/random-seed": "0.3.5",
|
||||||
"@types/ratelimiter": "3.4.6",
|
"@types/ratelimiter": "3.4.6",
|
||||||
"@types/rename": "1.0.7",
|
"@types/rename": "1.0.7",
|
||||||
"@types/sanitize-html": "2.9.5",
|
"@types/sanitize-html": "2.11.0",
|
||||||
"@types/semver": "7.5.6",
|
"@types/semver": "7.5.6",
|
||||||
"@types/simple-oauth2": "5.0.7",
|
"@types/simple-oauth2": "5.0.7",
|
||||||
"@types/sinonjs__fake-timers": "8.1.5",
|
"@types/sinonjs__fake-timers": "8.1.5",
|
||||||
|
@ -397,7 +397,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
|
|||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public checkDuplicate(name: string): Promise<boolean> {
|
public checkDuplicate(name: string): Promise<boolean> {
|
||||||
return this.emojisRepository.exist({ where: { name, host: IsNull() } });
|
return this.emojisRepository.exists({ where: { name, host: IsNull() } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -419,6 +419,10 @@ export class MfmService {
|
|||||||
},
|
},
|
||||||
|
|
||||||
text: (node) => {
|
text: (node) => {
|
||||||
|
if (!node.props.text.match(/[\r\n]/)) {
|
||||||
|
return doc.createTextNode(node.props.text);
|
||||||
|
}
|
||||||
|
|
||||||
const el = doc.createElement('span');
|
const el = doc.createElement('span');
|
||||||
const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));
|
const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));
|
||||||
|
|
||||||
|
@ -610,7 +610,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
if (data.reply) {
|
if (data.reply) {
|
||||||
// 通知
|
// 通知
|
||||||
if (data.reply.userHost === null) {
|
if (data.reply.userHost === null) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userId: data.reply.userId,
|
userId: data.reply.userId,
|
||||||
threadId: data.reply.threadId ?? data.reply.id,
|
threadId: data.reply.threadId ?? data.reply.id,
|
||||||
@ -778,7 +778,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||||||
@bindThis
|
@bindThis
|
||||||
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
|
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
|
||||||
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userId: u.id,
|
userId: u.id,
|
||||||
threadId: note.threadId ?? note.id,
|
threadId: note.threadId ?? note.id,
|
||||||
|
@ -49,7 +49,7 @@ export class NoteReadService implements OnApplicationShutdown {
|
|||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// スレッドミュート
|
// スレッドミュート
|
||||||
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
|
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
threadId: note.threadId ?? note.id,
|
threadId: note.threadId ?? note.id,
|
||||||
@ -70,7 +70,7 @@ export class NoteReadService implements OnApplicationShutdown {
|
|||||||
|
|
||||||
// 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する
|
// 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する
|
||||||
setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => {
|
setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => {
|
||||||
const exist = await this.noteUnreadsRepository.exist({ where: { id: unread.id } });
|
const exist = await this.noteUnreadsRepository.exists({ where: { id: unread.id } });
|
||||||
|
|
||||||
if (!exist) return;
|
if (!exist) return;
|
||||||
|
|
||||||
|
@ -74,12 +74,12 @@ export class SignupService {
|
|||||||
const secret = generateUserToken();
|
const secret = generateUserToken();
|
||||||
|
|
||||||
// Check username duplication
|
// Check username duplication
|
||||||
if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
||||||
throw new Error('DUPLICATED_USERNAME');
|
throw new Error('DUPLICATED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check deleted username duplication
|
// Check deleted username duplication
|
||||||
if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) {
|
if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
|
||||||
throw new Error('USED_USERNAME');
|
throw new Error('USED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
let autoAccept = false;
|
let autoAccept = false;
|
||||||
|
|
||||||
// 鍵アカウントであっても、既にフォローされていた場合はスルー
|
// 鍵アカウントであっても、既にフォローされていた場合はスルー
|
||||||
const isFollowing = await this.followingsRepository.exist({
|
const isFollowing = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
@ -155,7 +155,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
|
|
||||||
// フォローしているユーザーは自動承認オプション
|
// フォローしているユーザーは自動承認オプション
|
||||||
if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) {
|
if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) {
|
||||||
const isFollowed = await this.followingsRepository.exist({
|
const isFollowed = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: followee.id,
|
followerId: followee.id,
|
||||||
followeeId: follower.id,
|
followeeId: follower.id,
|
||||||
@ -169,7 +169,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
if (followee.isLocked && !autoAccept) {
|
if (followee.isLocked && !autoAccept) {
|
||||||
autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs(
|
autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs(
|
||||||
follower,
|
follower,
|
||||||
(oldSrc, newSrc) => this.followingsRepository.exist({
|
(oldSrc, newSrc) => this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
followerId: newSrc.id,
|
followerId: newSrc.id,
|
||||||
@ -232,7 +232,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
|
|
||||||
this.cacheService.userFollowingsCache.refresh(follower.id);
|
this.cacheService.userFollowingsCache.refresh(follower.id);
|
||||||
|
|
||||||
const requestExist = await this.followRequestsRepository.exist({
|
const requestExist = await this.followRequestsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
@ -530,7 +530,7 @@ export class UserFollowingService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestExist = await this.followRequestsRepository.exist({
|
const requestExist = await this.followRequestsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
|
@ -642,7 +642,7 @@ export class ApInboxService {
|
|||||||
return 'skip: follower not found';
|
return 'skip: follower not found';
|
||||||
}
|
}
|
||||||
|
|
||||||
const isFollowing = await this.followingsRepository.exist({
|
const isFollowing = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: actor.id,
|
followeeId: actor.id,
|
||||||
@ -699,14 +699,14 @@ export class ApInboxService {
|
|||||||
return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません';
|
return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません';
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestExist = await this.followRequestsRepository.exist({
|
const requestExist = await this.followRequestsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: actor.id,
|
followerId: actor.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const isFollowing = await this.followingsRepository.exist({
|
const isFollowing = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: actor.id,
|
followerId: actor.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
|
@ -25,8 +25,21 @@ export class ApMfmService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public getNoteHtml(note: MiNote): string | null {
|
public getNoteHtml(note: MiNote, apAppend?: string) {
|
||||||
if (!note.text) return '';
|
let noMisskeyContent = false;
|
||||||
return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
|
const srcMfm = (note.text ?? '') + (apAppend ?? '');
|
||||||
|
|
||||||
|
const parsed = mfm.parse(srcMfm);
|
||||||
|
|
||||||
|
if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
|
||||||
|
noMisskeyContent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers));
|
||||||
|
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
noMisskeyContent,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ export class ApRendererService {
|
|||||||
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
|
||||||
|
|
||||||
if (inReplyToNote != null) {
|
if (inReplyToNote != null) {
|
||||||
const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } });
|
const inReplyToUserExist = await this.usersRepository.exists({ where: { id: inReplyToNote.userId } });
|
||||||
|
|
||||||
if (inReplyToUserExist) {
|
if (inReplyToUserExist) {
|
||||||
if (inReplyToNote.uri) {
|
if (inReplyToNote.uri) {
|
||||||
@ -389,17 +389,15 @@ export class ApRendererService {
|
|||||||
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
let apText = text;
|
let apAppend = '';
|
||||||
|
|
||||||
if (quote) {
|
if (quote) {
|
||||||
apText += `\n\nRE: ${quote}`;
|
apAppend += `\n\nRE: ${quote}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
|
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
|
||||||
|
|
||||||
const content = this.apMfmService.getNoteHtml(Object.assign({}, note, {
|
const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, apAppend);
|
||||||
text: apText,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const emojis = await this.getEmojis(note.emojis);
|
const emojis = await this.getEmojis(note.emojis);
|
||||||
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
|
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
|
||||||
@ -412,9 +410,6 @@ export class ApRendererService {
|
|||||||
|
|
||||||
const asPoll = poll ? {
|
const asPoll = poll ? {
|
||||||
type: 'Question',
|
type: 'Question',
|
||||||
content: this.apMfmService.getNoteHtml(Object.assign({}, note, {
|
|
||||||
text: text,
|
|
||||||
})),
|
|
||||||
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
|
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
|
||||||
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
|
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
|
||||||
type: 'Note',
|
type: 'Note',
|
||||||
@ -432,11 +427,13 @@ export class ApRendererService {
|
|||||||
attributedTo,
|
attributedTo,
|
||||||
summary: summary ?? undefined,
|
summary: summary ?? undefined,
|
||||||
content: content ?? undefined,
|
content: content ?? undefined,
|
||||||
_misskey_content: text,
|
...(noMisskeyContent ? {} : {
|
||||||
source: {
|
_misskey_content: text,
|
||||||
content: text,
|
source: {
|
||||||
mediaType: 'text/x.misskeymarkdown',
|
content: text,
|
||||||
},
|
mediaType: 'text/x.misskeymarkdown',
|
||||||
|
},
|
||||||
|
}),
|
||||||
_misskey_quote: quote,
|
_misskey_quote: quote,
|
||||||
quoteUrl: quote,
|
quoteUrl: quote,
|
||||||
published: this.idService.parse(note.id).date.toISOString(),
|
published: this.idService.parse(note.id).date.toISOString(),
|
||||||
|
@ -50,14 +50,14 @@ export class ChannelEntityService {
|
|||||||
|
|
||||||
const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null;
|
const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null;
|
||||||
|
|
||||||
const isFollowing = meId ? await this.channelFollowingsRepository.exist({
|
const isFollowing = meId ? await this.channelFollowingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
followeeId: channel.id,
|
followeeId: channel.id,
|
||||||
},
|
},
|
||||||
}) : false;
|
}) : false;
|
||||||
|
|
||||||
const isFavorited = meId ? await this.channelFavoritesRepository.exist({
|
const isFavorited = meId ? await this.channelFavoritesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userId: meId,
|
userId: meId,
|
||||||
channelId: channel.id,
|
channelId: channel.id,
|
||||||
|
@ -45,7 +45,7 @@ export class ClipEntityService {
|
|||||||
description: clip.description,
|
description: clip.description,
|
||||||
isPublic: clip.isPublic,
|
isPublic: clip.isPublic,
|
||||||
favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }),
|
favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }),
|
||||||
isFavorited: meId ? await this.clipFavoritesRepository.exist({ where: { clipId: clip.id, userId: meId } }) : undefined,
|
isFavorited: meId ? await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export class FlashEntityService {
|
|||||||
summary: flash.summary,
|
summary: flash.summary,
|
||||||
script: flash.script,
|
script: flash.script,
|
||||||
likedCount: flash.likedCount,
|
likedCount: flash.likedCount,
|
||||||
isLiked: meId ? await this.flashLikesRepository.exist({ where: { flashId: flash.id, userId: meId } }) : undefined,
|
isLiked: meId ? await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ export class GalleryPostEntityService {
|
|||||||
tags: post.tags.length > 0 ? post.tags : undefined,
|
tags: post.tags.length > 0 ? post.tags : undefined,
|
||||||
isSensitive: post.isSensitive,
|
isSensitive: post.isSensitive,
|
||||||
likedCount: post.likedCount,
|
likedCount: post.likedCount,
|
||||||
isLiked: meId ? await this.galleryLikesRepository.exist({ where: { postId: post.id, userId: meId } }) : undefined,
|
isLiked: meId ? await this.galleryLikesRepository.exists({ where: { postId: post.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ export class NoteEntityService implements OnModuleInit {
|
|||||||
hide = false;
|
hide = false;
|
||||||
} else {
|
} else {
|
||||||
// フォロワーかどうか
|
// フォロワーかどうか
|
||||||
const isFollowing = await this.followingsRepository.exist({
|
const isFollowing = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followeeId: packedNote.userId,
|
followeeId: packedNote.userId,
|
||||||
followerId: meId,
|
followerId: meId,
|
||||||
|
@ -103,7 +103,7 @@ export class PageEntityService {
|
|||||||
eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId, me) : null,
|
eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId, me) : null,
|
||||||
attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null), me),
|
attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null), me),
|
||||||
likedCount: page.likedCount,
|
likedCount: page.likedCount,
|
||||||
isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined,
|
isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,43 +153,43 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
followerId: me,
|
followerId: me,
|
||||||
followeeId: target,
|
followeeId: target,
|
||||||
}),
|
}),
|
||||||
this.followingsRepository.exist({
|
this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: target,
|
followerId: target,
|
||||||
followeeId: me,
|
followeeId: me,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.followRequestsRepository.exist({
|
this.followRequestsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: me,
|
followerId: me,
|
||||||
followeeId: target,
|
followeeId: target,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.followRequestsRepository.exist({
|
this.followRequestsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: target,
|
followerId: target,
|
||||||
followeeId: me,
|
followeeId: me,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.blockingsRepository.exist({
|
this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: me,
|
blockerId: me,
|
||||||
blockeeId: target,
|
blockeeId: target,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.blockingsRepository.exist({
|
this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: target,
|
blockerId: target,
|
||||||
blockeeId: me,
|
blockeeId: me,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.mutingsRepository.exist({
|
this.mutingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
muterId: me,
|
muterId: me,
|
||||||
muteeId: target,
|
muteeId: target,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
this.renoteMutingsRepository.exist({
|
this.renoteMutingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
muterId: me,
|
muterId: me,
|
||||||
muteeId: target,
|
muteeId: target,
|
||||||
@ -216,7 +216,7 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
/*
|
/*
|
||||||
const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId);
|
const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId);
|
||||||
|
|
||||||
const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exist({
|
const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
antennaId: In(myAntennas.map(x => x.id)),
|
antennaId: In(myAntennas.map(x => x.id)),
|
||||||
read: false,
|
read: false,
|
||||||
|
@ -163,12 +163,12 @@ export class SignupApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (instance.emailRequiredForSignup) {
|
if (instance.emailRequiredForSignup) {
|
||||||
if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
|
||||||
throw new FastifyReplyError(400, 'DUPLICATED_USERNAME');
|
throw new FastifyReplyError(400, 'DUPLICATED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check deleted username duplication
|
// Check deleted username duplication
|
||||||
if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) {
|
if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
|
||||||
throw new FastifyReplyError(400, 'USED_USERNAME');
|
throw new FastifyReplyError(400, 'USED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
|
|
||||||
const exist = await this.promoNotesRepository.exist({ where: { noteId: note.id } });
|
const exist = await this.promoNotesRepository.exists({ where: { noteId: note.id } });
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
throw new ApiError(meta.errors.alreadyPromoted);
|
throw new ApiError(meta.errors.alreadyPromoted);
|
||||||
|
@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
const accessToken = secureRndstr(32);
|
const accessToken = secureRndstr(32);
|
||||||
|
|
||||||
// Fetch exist access token
|
// Fetch exist access token
|
||||||
const exist = await this.accessTokensRepository.exist({
|
const exist = await this.accessTokensRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
appId: session.appId,
|
appId: session.appId,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Check if already blocking
|
// Check if already blocking
|
||||||
const exist = await this.blockingsRepository.exist({
|
const exist = await this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: blocker.id,
|
blockerId: blocker.id,
|
||||||
blockeeId: blockee.id,
|
blockeeId: blockee.id,
|
||||||
|
@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Check not blocking
|
// Check not blocking
|
||||||
const exist = await this.blockingsRepository.exist({
|
const exist = await this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: blocker.id,
|
blockerId: blocker.id,
|
||||||
blockeeId: blockee.id,
|
blockeeId: blockee.id,
|
||||||
|
@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.noSuchClip);
|
throw new ApiError(meta.errors.noSuchClip);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.clipFavoritesRepository.exist({
|
const exist = await this.clipFavoritesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
clipId: clip.id,
|
clipId: clip.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -38,7 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
private driveFilesRepository: DriveFilesRepository,
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const exist = await this.driveFilesRepository.exist({
|
const exist = await this.driveFilesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
md5: ps.md5,
|
md5: ps.md5,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if already liked
|
// if already liked
|
||||||
const exist = await this.flashLikesRepository.exist({
|
const exist = await this.flashLikesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
flashId: flash.id,
|
flashId: flash.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Check if already following
|
// Check if already following
|
||||||
const exist = await this.followingsRepository.exist({
|
const exist = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
|
@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Check not following
|
// Check not following
|
||||||
const exist = await this.followingsRepository.exist({
|
const exist = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followerId: follower.id,
|
followerId: follower.id,
|
||||||
followeeId: followee.id,
|
followeeId: followee.id,
|
||||||
|
@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if already liked
|
// if already liked
|
||||||
const exist = await this.galleryLikesRepository.exist({
|
const exist = await this.galleryLikesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
postId: post.id,
|
postId: post.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
private downloadService: DownloadService,
|
private downloadService: DownloadService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const userExist = await this.usersRepository.exist({ where: { id: me.id } });
|
const userExist = await this.usersRepository.exists({ where: { id: me.id } });
|
||||||
if (!userExist) throw new ApiError(meta.errors.noSuchUser);
|
if (!userExist) throw new ApiError(meta.errors.noSuchUser);
|
||||||
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
|
||||||
if (file === null) throw new ApiError(meta.errors.noSuchFile);
|
if (file === null) throw new ApiError(meta.errors.noSuchFile);
|
||||||
|
@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
if (ps.tokenId) {
|
if (ps.tokenId) {
|
||||||
const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } });
|
const tokenExist = await this.accessTokensRepository.exists({ where: { id: ps.tokenId } });
|
||||||
|
|
||||||
if (tokenExist) {
|
if (tokenExist) {
|
||||||
await this.accessTokensRepository.delete({
|
await this.accessTokensRepository.delete({
|
||||||
@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (ps.token) {
|
} else if (ps.token) {
|
||||||
const tokenExist = await this.accessTokensRepository.exist({ where: { token: ps.token } });
|
const tokenExist = await this.accessTokensRepository.exists({ where: { token: ps.token } });
|
||||||
|
|
||||||
if (tokenExist) {
|
if (tokenExist) {
|
||||||
await this.accessTokensRepository.delete({
|
await this.accessTokensRepository.delete({
|
||||||
|
@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Check if already muting
|
// Check if already muting
|
||||||
const exist = await this.mutingsRepository.exist({
|
const exist = await this.mutingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
muterId: muter.id,
|
muterId: muter.id,
|
||||||
muteeId: mutee.id,
|
muteeId: mutee.id,
|
||||||
|
@ -261,7 +261,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (renote.userId !== me.id) {
|
if (renote.userId !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exist({
|
const blockExist = await this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: renote.userId,
|
blockerId: renote.userId,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
@ -309,7 +309,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (reply.userId !== me.id) {
|
if (reply.userId !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exist({
|
const blockExist = await this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: reply.userId,
|
blockerId: reply.userId,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
|
@ -69,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
|
|
||||||
// if already favorited
|
// if already favorited
|
||||||
const exist = await this.noteFavoritesRepository.exist({
|
const exist = await this.noteFavoritesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if already liked
|
// if already liked
|
||||||
const exist = await this.pageLikesRepository.exist({
|
const exist = await this.pageLikesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
pageId: page.id,
|
pageId: page.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const exist = await this.promoReadsRepository.exist({
|
const exist = await this.promoReadsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
if (me == null) {
|
if (me == null) {
|
||||||
throw new ApiError(meta.errors.forbidden);
|
throw new ApiError(meta.errors.forbidden);
|
||||||
} else if (me.id !== user.id) {
|
} else if (me.id !== user.id) {
|
||||||
const isFollowing = await this.followingsRepository.exist({
|
const isFollowing = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followeeId: user.id,
|
followeeId: user.id,
|
||||||
followerId: me.id,
|
followerId: me.id,
|
||||||
|
@ -109,7 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
if (me == null) {
|
if (me == null) {
|
||||||
throw new ApiError(meta.errors.forbidden);
|
throw new ApiError(meta.errors.forbidden);
|
||||||
} else if (me.id !== user.id) {
|
} else if (me.id !== user.id) {
|
||||||
const isFollowing = await this.followingsRepository.exist({
|
const isFollowing = await this.followingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
followeeId: user.id,
|
followeeId: user.id,
|
||||||
followerId: me.id,
|
followerId: me.id,
|
||||||
|
@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
private roleService: RoleService,
|
private roleService: RoleService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const listExist = await this.userListsRepository.exist({
|
const listExist = await this.userListsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
id: ps.listId,
|
id: ps.listId,
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
@ -123,7 +123,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (currentUser.id !== me.id) {
|
if (currentUser.id !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exist({
|
const blockExist = await this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: currentUser.id,
|
blockerId: currentUser.id,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
@ -134,7 +134,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.userListMembershipsRepository.exist({
|
const exist = await this.userListMembershipsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userListId: userList.id,
|
userListId: userList.id,
|
||||||
userId: currentUser.id,
|
userId: currentUser.id,
|
||||||
|
@ -48,7 +48,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const userListExist = await this.userListsRepository.exist({
|
const userListExist = await this.userListsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
id: ps.listId,
|
id: ps.listId,
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
@ -59,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
throw new ApiError(meta.errors.noSuchList);
|
throw new ApiError(meta.errors.noSuchList);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.userListFavoritesRepository.exist({
|
const exist = await this.userListFavoritesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
userListId: ps.listId,
|
userListId: ps.listId,
|
||||||
|
@ -105,7 +105,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
|
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (user.id !== me.id) {
|
if (user.id !== me.id) {
|
||||||
const blockExist = await this.blockingsRepository.exist({
|
const blockExist = await this.blockingsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
blockerId: user.id,
|
blockerId: user.id,
|
||||||
blockeeId: me.id,
|
blockeeId: me.id,
|
||||||
@ -116,7 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await this.userListMembershipsRepository.exist({
|
const exist = await this.userListMembershipsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userListId: userList.id,
|
userListId: userList.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
userListId: ps.listId,
|
userListId: ps.listId,
|
||||||
});
|
});
|
||||||
if (me !== null) {
|
if (me !== null) {
|
||||||
additionalProperties.isLiked = await this.userListFavoritesRepository.exist({
|
additionalProperties.isLiked = await this.userListFavoritesRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
userListId: ps.listId,
|
userListId: ps.listId,
|
||||||
|
@ -46,7 +46,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
private userListFavoritesRepository: UserListFavoritesRepository,
|
private userListFavoritesRepository: UserListFavoritesRepository,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const userListExist = await this.userListsRepository.exist({
|
const userListExist = await this.userListsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
id: ps.listId,
|
id: ps.listId,
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
|
@ -43,7 +43,7 @@ class UserListChannel extends Channel {
|
|||||||
this.withRenotes = params.withRenotes ?? true;
|
this.withRenotes = params.withRenotes ?? true;
|
||||||
|
|
||||||
// Check existence and owner
|
// Check existence and owner
|
||||||
const listExist = await this.userListsRepository.exist({
|
const listExist = await this.userListsRepository.exists({
|
||||||
where: {
|
where: {
|
||||||
id: this.listId,
|
id: this.listId,
|
||||||
userId: this.user!.id,
|
userId: this.user!.id,
|
||||||
|
44
packages/backend/test/unit/ApMfmService.ts
Normal file
44
packages/backend/test/unit/ApMfmService.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import * as assert from 'assert';
|
||||||
|
import { Test } from '@nestjs/testing';
|
||||||
|
|
||||||
|
import { CoreModule } from '@/core/CoreModule.js';
|
||||||
|
import { ApMfmService } from '@/core/activitypub/ApMfmService.js';
|
||||||
|
import { GlobalModule } from '@/GlobalModule.js';
|
||||||
|
import { MiNote } from '@/models/Note.js';
|
||||||
|
|
||||||
|
describe('ApMfmService', () => {
|
||||||
|
let apMfmService: ApMfmService;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const app = await Test.createTestingModule({
|
||||||
|
imports: [GlobalModule, CoreModule],
|
||||||
|
}).compile();
|
||||||
|
apMfmService = app.get<ApMfmService>(ApMfmService);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getNoteHtml', () => {
|
||||||
|
test('Do not provide _misskey_content for simple text', () => {
|
||||||
|
const note: MiNote = {
|
||||||
|
text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com',
|
||||||
|
mentionedRemoteUsers: '[]',
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
|
||||||
|
|
||||||
|
assert.equal(noMisskeyContent, true, 'noMisskeyContent');
|
||||||
|
assert.equal(content, '<p>テキスト <a href="http://misskey.local/tags/タグ" rel="tag">#タグ</a> <a href="http://misskey.local/@mention" class="u-url mention">@mention</a> 🍊 :emoji: <a href="https://example.com">https://example.com</a></p>', 'content');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Provide _misskey_content for MFM', () => {
|
||||||
|
const note: MiNote = {
|
||||||
|
text: '$[tada foo]',
|
||||||
|
mentionedRemoteUsers: '[]',
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
|
||||||
|
|
||||||
|
assert.equal(noMisskeyContent, false, 'noMisskeyContent');
|
||||||
|
assert.equal(content, '<p><i>foo</i></p>', 'content');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -33,6 +33,12 @@ describe('MfmService', () => {
|
|||||||
const output = '<p><span>foo<br>bar<br>baz</span></p>';
|
const output = '<p><span>foo<br>bar<br>baz</span></p>';
|
||||||
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
|
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Do not generate unnecessary span', () => {
|
||||||
|
const input = 'foo $[tada bar]';
|
||||||
|
const output = '<p>foo <i>bar</i></p>';
|
||||||
|
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fromHtml', () => {
|
describe('fromHtml', () => {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type SharedOptions, rest } from 'msw';
|
import { type SharedOptions, http, HttpResponse } from 'msw';
|
||||||
|
|
||||||
export const onUnhandledRequest = ((req, print) => {
|
export const onUnhandledRequest = ((req, print) => {
|
||||||
if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) {
|
if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) {
|
||||||
@ -13,19 +13,31 @@ export const onUnhandledRequest = ((req, print) => {
|
|||||||
}) satisfies SharedOptions['onUnhandledRequest'];
|
}) satisfies SharedOptions['onUnhandledRequest'];
|
||||||
|
|
||||||
export const commonHandlers = [
|
export const commonHandlers = [
|
||||||
rest.get('/fluent-emoji/:codepoints.png', async (req, res, ctx) => {
|
http.get('/fluent-emoji/:codepoints.png', async ({ params }) => {
|
||||||
const { codepoints } = req.params;
|
const { codepoints } = params;
|
||||||
const value = await fetch(`https://raw.githubusercontent.com/MisskeyIO/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
const value = await fetch(`https://raw.githubusercontent.com/MisskeyIO/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
||||||
return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
|
return new HttpResponse(value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'image/png',
|
||||||
|
},
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
rest.get('/fluent-emojis/:codepoints.png', async (req, res, ctx) => {
|
http.get('/fluent-emojis/:codepoints.png', async ({ params }) => {
|
||||||
const { codepoints } = req.params;
|
const { codepoints } = params;
|
||||||
const value = await fetch(`https://raw.githubusercontent.com/MisskeyIO/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
const value = await fetch(`https://raw.githubusercontent.com/MisskeyIO/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
|
||||||
return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
|
return new HttpResponse(value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'image/png',
|
||||||
|
},
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
rest.get('/twemoji/:codepoints.svg', async (req, res, ctx) => {
|
http.get('/twemoji/:codepoints.svg', async ({ params }) => {
|
||||||
const { codepoints } = req.params;
|
const { codepoints } = params;
|
||||||
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
|
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
|
||||||
return res(ctx.set('Content-Type', 'image/svg+xml'), ctx.body(value));
|
return new HttpResponse(value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'image/svg+xml',
|
||||||
|
},
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
@ -29,8 +29,8 @@
|
|||||||
"@tabler/icons-webfont": "2.46.0",
|
"@tabler/icons-webfont": "2.46.0",
|
||||||
"@twemoji/parser": "15.0.0",
|
"@twemoji/parser": "15.0.0",
|
||||||
"@vitejs/plugin-vue": "5.0.3",
|
"@vitejs/plugin-vue": "5.0.3",
|
||||||
"@vue/compiler-sfc": "3.4.15",
|
"@vue/compiler-sfc": "3.4.16",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
|
||||||
"astring": "1.8.6",
|
"astring": "1.8.6",
|
||||||
"broadcast-channel": "7.0.0",
|
"broadcast-channel": "7.0.0",
|
||||||
"buraha": "0.0.1",
|
"buraha": "0.0.1",
|
||||||
@ -61,10 +61,10 @@
|
|||||||
"rollup": "4.9.6",
|
"rollup": "4.9.6",
|
||||||
"sanitize-html": "2.11.0",
|
"sanitize-html": "2.11.0",
|
||||||
"sass": "1.70.0",
|
"sass": "1.70.0",
|
||||||
"shiki": "1.0.0-beta.3",
|
"shiki": "1.0.0",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.160.1",
|
"three": "0.161.0",
|
||||||
"throttle-debounce": "5.0.0",
|
"throttle-debounce": "5.0.0",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.8",
|
||||||
@ -72,39 +72,39 @@
|
|||||||
"typescript": "5.3.3",
|
"typescript": "5.3.3",
|
||||||
"uuid": "9.0.1",
|
"uuid": "9.0.1",
|
||||||
"v-code-diff": "1.7.2",
|
"v-code-diff": "1.7.2",
|
||||||
"vite": "5.0.12",
|
"vite": "5.1.0",
|
||||||
"vue": "3.4.15",
|
"vue": "3.4.16",
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
},
|
},
|
||||||
"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": "7.6.13",
|
||||||
"@storybook/addon-essentials": "7.6.10",
|
"@storybook/addon-essentials": "7.6.13",
|
||||||
"@storybook/addon-interactions": "7.6.10",
|
"@storybook/addon-interactions": "7.6.13",
|
||||||
"@storybook/addon-links": "7.6.10",
|
"@storybook/addon-links": "7.6.13",
|
||||||
"@storybook/addon-storysource": "7.6.10",
|
"@storybook/addon-storysource": "7.6.13",
|
||||||
"@storybook/addons": "7.6.10",
|
"@storybook/addons": "7.6.13",
|
||||||
"@storybook/blocks": "7.6.10",
|
"@storybook/blocks": "7.6.13",
|
||||||
"@storybook/core-events": "7.6.10",
|
"@storybook/core-events": "7.6.13",
|
||||||
"@storybook/jest": "0.2.3",
|
"@storybook/jest": "0.2.3",
|
||||||
"@storybook/manager-api": "7.6.10",
|
"@storybook/manager-api": "7.6.13",
|
||||||
"@storybook/preview-api": "7.6.10",
|
"@storybook/preview-api": "7.6.13",
|
||||||
"@storybook/react": "7.6.10",
|
"@storybook/react": "7.6.13",
|
||||||
"@storybook/react-vite": "7.6.10",
|
"@storybook/react-vite": "7.6.13",
|
||||||
"@storybook/testing-library": "0.2.2",
|
"@storybook/testing-library": "0.2.2",
|
||||||
"@storybook/theming": "7.6.10",
|
"@storybook/theming": "7.6.13",
|
||||||
"@storybook/types": "7.6.10",
|
"@storybook/types": "7.6.13",
|
||||||
"@storybook/vue3": "7.6.10",
|
"@storybook/vue3": "7.6.13",
|
||||||
"@storybook/vue3-vite": "7.6.10",
|
"@storybook/vue3-vite": "7.6.13",
|
||||||
"@testing-library/vue": "8.0.1",
|
"@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",
|
||||||
"@types/matter-js": "0.19.6",
|
"@types/matter-js": "0.19.6",
|
||||||
"@types/micromatch": "4.0.6",
|
"@types/micromatch": "4.0.6",
|
||||||
"@types/node": "20.11.10",
|
"@types/node": "20.11.16",
|
||||||
"@types/punycode": "2.1.3",
|
"@types/punycode": "2.1.3",
|
||||||
"@types/sanitize-html": "2.9.5",
|
"@types/sanitize-html": "2.11.0",
|
||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/uuid": "9.0.8",
|
"@types/uuid": "9.0.8",
|
||||||
@ -112,25 +112,25 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
"@vitest/coverage-v8": "0.34.6",
|
"@vitest/coverage-v8": "0.34.6",
|
||||||
"@vue/runtime-core": "3.4.15",
|
"@vue/runtime-core": "3.4.16",
|
||||||
"acorn": "8.11.3",
|
"acorn": "8.11.3",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.6.3",
|
"cypress": "13.6.4",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-vue": "9.21.0",
|
"eslint-plugin-vue": "9.21.1",
|
||||||
"fast-glob": "3.3.2",
|
"fast-glob": "3.3.2",
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "10.0.3",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.5",
|
"micromatch": "4.0.5",
|
||||||
"msw": "2.1.5",
|
"msw": "2.1.7",
|
||||||
"msw-storybook-addon": "1.10.0",
|
"msw-storybook-addon": "2.0.0-beta.1",
|
||||||
"nodemon": "3.0.3",
|
"nodemon": "3.0.3",
|
||||||
"prettier": "3.2.4",
|
"prettier": "3.2.5",
|
||||||
"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": "7.6.13",
|
||||||
"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",
|
||||||
|
@ -6,7 +6,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 { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { abuseUserReport } from '../../.storybook/fakes.js';
|
import { abuseUserReport } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAbuseReport from './MkAbuseReport.vue';
|
import MkAbuseReport from './MkAbuseReport.vue';
|
||||||
@ -44,9 +44,9 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/admin/resolve-abuse-user-report', async (req, res, ctx) => {
|
http.post('/api/admin/resolve-abuse-user-report', async ({ request }) => {
|
||||||
action('POST /api/admin/resolve-abuse-user-report')(await req.json());
|
action('POST /api/admin/resolve-abuse-user-report')(await request.json());
|
||||||
return res(ctx.json({}));
|
return HttpResponse.json({});
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,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 { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
|
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
|
||||||
@ -44,9 +44,9 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users/report-abuse', async (req, res, ctx) => {
|
http.post('/api/users/report-abuse', async ({ request }) => {
|
||||||
action('POST /api/users/report-abuse')(await req.json());
|
action('POST /api/users/report-abuse')(await request.json());
|
||||||
return res(ctx.json({}));
|
return HttpResponse.json({});
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -5,7 +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 { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAchievements from './MkAchievements.vue';
|
import MkAchievements from './MkAchievements.vue';
|
||||||
@ -39,8 +39,8 @@ export const Empty = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users/achievements', (req, res, ctx) => {
|
http.post('/api/users/achievements', () => {
|
||||||
return res(ctx.json([]));
|
return HttpResponse.json([]);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -52,8 +52,8 @@ export const All = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users/achievements', (req, res, ctx) => {
|
http.post('/api/users/achievements', () => {
|
||||||
return res(ctx.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 }))));
|
return HttpResponse.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 })));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@ import { action } from '@storybook/addon-actions';
|
|||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAutocomplete from './MkAutocomplete.vue';
|
import MkAutocomplete from './MkAutocomplete.vue';
|
||||||
@ -99,11 +99,11 @@ export const User = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users/search-by-username-and-host', (req, res, ctx) => {
|
http.post('/api/users/search-by-username-and-host', () => {
|
||||||
return res(ctx.json([
|
return HttpResponse.json([
|
||||||
userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
|
userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
|
||||||
userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
|
userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
|
||||||
]));
|
]);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -132,12 +132,12 @@ export const Hashtag = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/hashtags/search', (req, res, ctx) => {
|
http.post('/api/hashtags/search', () => {
|
||||||
return res(ctx.json([
|
return HttpResponse.json([
|
||||||
'気象警報注意報',
|
'気象警報注意報',
|
||||||
'気象警報',
|
'気象警報',
|
||||||
'気象情報',
|
'気象情報',
|
||||||
]));
|
]);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -5,7 +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 { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkAvatars from './MkAvatars.vue';
|
import MkAvatars from './MkAvatars.vue';
|
||||||
@ -38,12 +38,12 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users/show', (req, res, ctx) => {
|
http.post('/api/users/show', () => {
|
||||||
return res(ctx.json([
|
return HttpResponse.json([
|
||||||
userDetailed('17'),
|
userDetailed('17'),
|
||||||
userDetailed('20'),
|
userDetailed('20'),
|
||||||
userDetailed('18'),
|
userDetailed('18'),
|
||||||
]));
|
]);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -77,6 +77,7 @@ watch(() => props.lang, (to) => {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid var(--divider);
|
border: 1px solid var(--divider);
|
||||||
|
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
|
||||||
|
|
||||||
color: var(--shiki-fallback);
|
color: var(--shiki-fallback);
|
||||||
background-color: var(--shiki-fallback-bg);
|
background-color: var(--shiki-fallback-bg);
|
||||||
|
@ -25,11 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, shallowRef, computed, nextTick, watch } from 'vue';
|
import { ref, shallowRef, computed, nextTick, watch } from 'vue';
|
||||||
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
|
import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
import { isHorizontalSwipeSwiping as isSwiping } from '@/scripts/touch.js';
|
||||||
|
|
||||||
const rootEl = shallowRef<HTMLDivElement>();
|
const rootEl = shallowRef<HTMLDivElement>();
|
||||||
|
|
||||||
@ -49,16 +49,16 @@ const shouldAnimate = computed(() => defaultStore.reactiveState.enableHorizontal
|
|||||||
// ▼ しきい値 ▼ //
|
// ▼ しきい値 ▼ //
|
||||||
|
|
||||||
// スワイプと判定される最小の距離
|
// スワイプと判定される最小の距離
|
||||||
const MIN_SWIPE_DISTANCE = 50;
|
const MIN_SWIPE_DISTANCE = 20;
|
||||||
|
|
||||||
// スワイプ時の動作を発火する最小の距離
|
// スワイプ時の動作を発火する最小の距離
|
||||||
const SWIPE_DISTANCE_THRESHOLD = 125;
|
const SWIPE_DISTANCE_THRESHOLD = 70;
|
||||||
|
|
||||||
// スワイプを中断するY方向の移動距離
|
// スワイプを中断するY方向の移動距離
|
||||||
const SWIPE_ABORT_Y_THRESHOLD = 75;
|
const SWIPE_ABORT_Y_THRESHOLD = 75;
|
||||||
|
|
||||||
// スワイプできる最大の距離
|
// スワイプできる最大の距離
|
||||||
const MAX_SWIPE_DISTANCE = 150;
|
const MAX_SWIPE_DISTANCE = 120;
|
||||||
|
|
||||||
// ▲ しきい値 ▲ //
|
// ▲ しきい値 ▲ //
|
||||||
|
|
||||||
@ -68,7 +68,6 @@ let startScreenY: number | null = null;
|
|||||||
const currentTabIndex = computed(() => props.tabs.findIndex(tab => tab.key === tabModel.value));
|
const currentTabIndex = computed(() => props.tabs.findIndex(tab => tab.key === tabModel.value));
|
||||||
|
|
||||||
const pullDistance = ref(0);
|
const pullDistance = ref(0);
|
||||||
const isSwiping = ref(false);
|
|
||||||
const isSwipingForClass = ref(false);
|
const isSwipingForClass = ref(false);
|
||||||
let swipeAborted = false;
|
let swipeAborted = false;
|
||||||
|
|
||||||
@ -77,6 +76,8 @@ function touchStart(event: TouchEvent) {
|
|||||||
|
|
||||||
if (event.touches.length !== 1) return;
|
if (event.touches.length !== 1) return;
|
||||||
|
|
||||||
|
if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return;
|
||||||
|
|
||||||
startScreenX = event.touches[0].screenX;
|
startScreenX = event.touches[0].screenX;
|
||||||
startScreenY = event.touches[0].screenY;
|
startScreenY = event.touches[0].screenY;
|
||||||
}
|
}
|
||||||
@ -90,6 +91,8 @@ function touchMove(event: TouchEvent) {
|
|||||||
|
|
||||||
if (swipeAborted) return;
|
if (swipeAborted) return;
|
||||||
|
|
||||||
|
if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return;
|
||||||
|
|
||||||
let distanceX = event.touches[0].screenX - startScreenX;
|
let distanceX = event.touches[0].screenX - startScreenX;
|
||||||
let distanceY = event.touches[0].screenY - startScreenY;
|
let distanceY = event.touches[0].screenY - startScreenY;
|
||||||
|
|
||||||
@ -139,6 +142,8 @@ function touchEnd(event: TouchEvent) {
|
|||||||
|
|
||||||
if (!isSwiping.value) return;
|
if (!isSwiping.value) return;
|
||||||
|
|
||||||
|
if (hasSomethingToDoWithXSwipe(event.target as HTMLElement)) return;
|
||||||
|
|
||||||
const distance = event.changedTouches[0].screenX - startScreenX;
|
const distance = event.changedTouches[0].screenX - startScreenX;
|
||||||
|
|
||||||
if (Math.abs(distance) > SWIPE_DISTANCE_THRESHOLD) {
|
if (Math.abs(distance) > SWIPE_DISTANCE_THRESHOLD) {
|
||||||
@ -162,6 +167,24 @@ function touchEnd(event: TouchEvent) {
|
|||||||
}, 400);
|
}, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 横スワイプに関与する可能性のある要素を調べる */
|
||||||
|
function hasSomethingToDoWithXSwipe(el: HTMLElement) {
|
||||||
|
if (['INPUT', 'TEXTAREA'].includes(el.tagName)) return true;
|
||||||
|
if (el.isContentEditable) return true;
|
||||||
|
if (el.scrollWidth > el.clientWidth) return true;
|
||||||
|
|
||||||
|
const style = window.getComputedStyle(el);
|
||||||
|
if (['absolute', 'fixed', 'sticky'].includes(style.position)) return true;
|
||||||
|
if (['scroll', 'auto'].includes(style.overflowX)) return true;
|
||||||
|
if (style.touchAction === 'pan-x') return true;
|
||||||
|
|
||||||
|
if (el.parentElement && el.parentElement !== rootEl.value) {
|
||||||
|
return hasSomethingToDoWithXSwipe(el.parentElement);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const transitionName = ref<'swipeAnimationLeft' | 'swipeAnimationRight' | undefined>(undefined);
|
const transitionName = ref<'swipeAnimationLeft' | 'swipeAnimationRight' | undefined>(undefined);
|
||||||
|
|
||||||
watch(tabModel, (newTab, oldTab) => {
|
watch(tabModel, (newTab, oldTab) => {
|
||||||
@ -182,6 +205,7 @@ watch(tabModel, (newTab, oldTab) => {
|
|||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.transitionRoot {
|
.transitionRoot {
|
||||||
|
touch-action: pan-y pinch-zoom;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 100%;
|
grid-template-columns: 100%;
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
|
@ -5,7 +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 { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed, inviteCode } from '../../.storybook/fakes.js';
|
import { userDetailed, inviteCode } from '../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import MkInviteCode from './MkInviteCode.vue';
|
import MkInviteCode from './MkInviteCode.vue';
|
||||||
@ -39,8 +39,8 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users/show', (req, res, ctx) => {
|
http.post('/api/users/show', ({ params }) => {
|
||||||
return res(ctx.json(userDetailed(req.params.userId as string)));
|
return HttpResponse.json(userDetailed(params.userId as string));
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,7 @@ import MkLoading from '@/components/global/MkLoading.vue';
|
|||||||
import { onMounted, onUnmounted, onActivated, onDeactivated, ref, shallowRef } from 'vue';
|
import { onMounted, onUnmounted, onActivated, onDeactivated, ref, shallowRef } from 'vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { getScrollContainer } from '@/scripts/scroll.js';
|
import { getScrollContainer } from '@/scripts/scroll.js';
|
||||||
|
import { isHorizontalSwipeSwiping } from '@/scripts/touch.js';
|
||||||
|
|
||||||
const SCROLL_STOP = 10;
|
const SCROLL_STOP = 10;
|
||||||
const MAX_PULL_DISTANCE = Infinity;
|
const MAX_PULL_DISTANCE = Infinity;
|
||||||
@ -144,7 +145,7 @@ function moving(event: TouchEvent | PointerEvent) {
|
|||||||
if (!isPullStart.value && scrollEl?.scrollTop === 0) moveStart(event);
|
if (!isPullStart.value && scrollEl?.scrollTop === 0) moveStart(event);
|
||||||
if (!isPullStart.value || isRefreshing.value || disabled) return;
|
if (!isPullStart.value || isRefreshing.value || disabled) return;
|
||||||
|
|
||||||
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value)) {
|
if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value) || isHorizontalSwipeSwiping.value) {
|
||||||
pullDistance.value = 0;
|
pullDistance.value = 0;
|
||||||
isPullEnd.value = false;
|
isPullEnd.value = false;
|
||||||
moveEnd();
|
moveEnd();
|
||||||
@ -167,6 +168,10 @@ function moving(event: TouchEvent | PointerEvent) {
|
|||||||
if (event.cancelable) event.preventDefault();
|
if (event.cancelable) event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pullDistance.value > SCROLL_STOP) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
isPullEnd.value = pullDistance.value >= FIRE_THRESHOLD && moveRatio.value > FIRE_THRESHOLD_RATIO;
|
isPullEnd.value = pullDistance.value >= FIRE_THRESHOLD && moveRatio.value > FIRE_THRESHOLD_RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,9 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Misskey from 'misskey-js';
|
|
||||||
import { computed, watch, onUnmounted, provide, shallowRef } from 'vue';
|
import { computed, watch, onUnmounted, provide, shallowRef } from 'vue';
|
||||||
import { Connection } from 'misskey-js/streaming.js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
@ -87,8 +86,8 @@ function prepend(note) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let connection: Connection;
|
let connection: Misskey.ChannelConnection | null = null;
|
||||||
let connection2: Connection;
|
let connection2: Misskey.ChannelConnection | null = null;
|
||||||
let paginationQuery: Paging | null = null;
|
let paginationQuery: Paging | null = null;
|
||||||
|
|
||||||
const stream = useStream();
|
const stream = useStream();
|
||||||
@ -157,7 +156,7 @@ function connectChannel() {
|
|||||||
roleId: props.role,
|
roleId: props.role,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (props.src !== 'directs' && props.src !== 'mentions') connection.on('note', prepend);
|
if (props.src !== 'directs' && props.src !== 'mentions') connection?.on('note', prepend);
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnectChannel() {
|
function disconnectChannel() {
|
||||||
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
v-if="player.url.startsWith('http://') || player.url.startsWith('https://')"
|
v-if="player.url.startsWith('http://') || player.url.startsWith('https://')"
|
||||||
sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin"
|
sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin"
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
:allow="player.allow.join(';')"
|
:allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')"
|
||||||
:class="$style.playerIframe"
|
:class="$style.playerIframe"
|
||||||
:src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')"
|
:src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')"
|
||||||
:style="{ border: 0 }"
|
:style="{ border: 0 }"
|
||||||
|
@ -5,7 +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 { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
|
import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
|
||||||
@ -38,17 +38,17 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users', (req, res, ctx) => {
|
http.post('/api/users', () => {
|
||||||
return res(ctx.json([
|
return HttpResponse.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]));
|
]);
|
||||||
}),
|
}),
|
||||||
rest.post('/api/pinned-users', (req, res, ctx) => {
|
http.post('/api/pinned-users', () => {
|
||||||
return res(ctx.json([
|
return HttpResponse.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]));
|
]);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
||||||
|
@ -5,7 +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 { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { commonHandlers } from '../../.storybook/mocks.js';
|
import { commonHandlers } from '../../.storybook/mocks.js';
|
||||||
import { userDetailed } from '../../.storybook/fakes.js';
|
import { userDetailed } from '../../.storybook/fakes.js';
|
||||||
import MkUserSetupDialog from './MkUserSetupDialog.vue';
|
import MkUserSetupDialog from './MkUserSetupDialog.vue';
|
||||||
@ -38,17 +38,17 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users', (req, res, ctx) => {
|
http.post('/api/users', () => {
|
||||||
return res(ctx.json([
|
return HttpResponse.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]));
|
]);
|
||||||
}),
|
}),
|
||||||
rest.post('/api/pinned-users', (req, res, ctx) => {
|
http.post('/api/pinned-users', () => {
|
||||||
return res(ctx.json([
|
return HttpResponse.json([
|
||||||
userDetailed('44'),
|
userDetailed('44'),
|
||||||
userDetailed('49'),
|
userDetailed('49'),
|
||||||
]));
|
]);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks.js';
|
import { commonHandlers } from '../../../.storybook/mocks.js';
|
||||||
import MkUrl from './MkUrl.vue';
|
import MkUrl from './MkUrl.vue';
|
||||||
export const Default = {
|
export const Default = {
|
||||||
@ -59,8 +59,8 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.get('/url', (req, res, ctx) => {
|
http.get('/url', () => {
|
||||||
return res(ctx.json({
|
return HttpResponse.json({
|
||||||
title: 'Misskey Hub',
|
title: 'Misskey Hub',
|
||||||
icon: 'https://misskey-hub.net/favicon.ico',
|
icon: 'https://misskey-hub.net/favicon.ico',
|
||||||
description: 'Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。',
|
description: 'Misskeyはオープンソースの分散型ソーシャルネットワーキングプラットフォームです。',
|
||||||
@ -74,7 +74,7 @@ export const Default = {
|
|||||||
sitename: 'misskey-hub.net',
|
sitename: 'misskey-hub.net',
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
url: 'https://misskey-hub.net/',
|
url: 'https://misskey-hub.net/',
|
||||||
}));
|
});
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -213,13 +213,13 @@ const patronsWithIcon = [{
|
|||||||
icon: 'https://assets.misskey-hub.net/patrons/302dce2898dd457ba03c3f7dc037900b.jpg',
|
icon: 'https://assets.misskey-hub.net/patrons/302dce2898dd457ba03c3f7dc037900b.jpg',
|
||||||
}, {
|
}, {
|
||||||
name: 'taichan',
|
name: 'taichan',
|
||||||
icon: 'https://assets.misskey-hub.net/patrons/f981ab0159fb4e2c998e05f7263e1cd9.png',
|
icon: 'https://assets.misskey-hub.net/patrons/f981ab0159fb4e2c998e05f7263e1cd9.jpg',
|
||||||
}, {
|
}, {
|
||||||
name: '猫吉よりお',
|
name: '猫吉よりお',
|
||||||
icon: 'https://assets.misskey-hub.net/patrons/a11518b3b34b4536a4bdd7178ba76a7b.png',
|
icon: 'https://assets.misskey-hub.net/patrons/a11518b3b34b4536a4bdd7178ba76a7b.jpg',
|
||||||
}, {
|
}, {
|
||||||
name: '有栖かずみ',
|
name: '有栖かずみ',
|
||||||
icon: 'https://assets.misskey-hub.net/patrons/9240e8e0ba294a8884143e99ac7ed6a0.png',
|
icon: 'https://assets.misskey-hub.net/patrons/9240e8e0ba294a8884143e99ac7ed6a0.jpg',
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const patrons = [
|
const patrons = [
|
||||||
|
@ -5,7 +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 { rest } from 'msw';
|
import { HttpResponse, http } from 'msw';
|
||||||
import { userDetailed } from '../../../.storybook/fakes.js';
|
import { userDetailed } from '../../../.storybook/fakes.js';
|
||||||
import { commonHandlers } from '../../../.storybook/mocks.js';
|
import { commonHandlers } from '../../../.storybook/mocks.js';
|
||||||
import home_ from './home.vue';
|
import home_ from './home.vue';
|
||||||
@ -39,12 +39,13 @@ export const Default = {
|
|||||||
msw: {
|
msw: {
|
||||||
handlers: [
|
handlers: [
|
||||||
...commonHandlers,
|
...commonHandlers,
|
||||||
rest.post('/api/users/notes', (req, res, ctx) => {
|
http.post('/api/users/notes', () => {
|
||||||
return res(ctx.json([]));
|
return HttpResponse.json([]);
|
||||||
}),
|
}),
|
||||||
rest.get('/api/charts/user/notes', (req, res, ctx) => {
|
http.get('/api/charts/user/notes', ({ request }) => {
|
||||||
const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
const url = new URL(request.url);
|
||||||
return res(ctx.json({
|
const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
||||||
|
return HttpResponse.json({
|
||||||
total: Array.from({ length }, () => 0),
|
total: Array.from({ length }, () => 0),
|
||||||
inc: Array.from({ length }, () => 0),
|
inc: Array.from({ length }, () => 0),
|
||||||
dec: Array.from({ length }, () => 0),
|
dec: Array.from({ length }, () => 0),
|
||||||
@ -54,11 +55,12 @@ export const Default = {
|
|||||||
renote: Array.from({ length }, () => 0),
|
renote: Array.from({ length }, () => 0),
|
||||||
withFile: Array.from({ length }, () => 0),
|
withFile: Array.from({ length }, () => 0),
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
}),
|
}),
|
||||||
rest.get('/api/charts/user/pv', (req, res, ctx) => {
|
http.get('/api/charts/user/pv', ({ request }) => {
|
||||||
const length = Math.max(Math.min(parseInt(req.url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
const url = new URL(request.url);
|
||||||
return res(ctx.json({
|
const length = Math.max(Math.min(parseInt(url.searchParams.get('limit') ?? '30', 10), 1), 300);
|
||||||
|
return HttpResponse.json({
|
||||||
upv: {
|
upv: {
|
||||||
user: Array.from({ length }, () => 0),
|
user: Array.from({ length }, () => 0),
|
||||||
visitor: Array.from({ length }, () => 0),
|
visitor: Array.from({ length }, () => 0),
|
||||||
@ -67,7 +69,7 @@ export const Default = {
|
|||||||
user: Array.from({ length }, () => 0),
|
user: Array.from({ length }, () => 0),
|
||||||
visitor: Array.from({ length }, () => 0),
|
visitor: Array.from({ length }, () => 0),
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -68,10 +68,7 @@ export async function initHighlighter() {
|
|||||||
themes,
|
themes,
|
||||||
langs: [
|
langs: [
|
||||||
import('shiki/langs/javascript.mjs'),
|
import('shiki/langs/javascript.mjs'),
|
||||||
{
|
aiScriptGrammar.default as unknown as LanguageRegistration,
|
||||||
aliases: ['is', 'ais'],
|
|
||||||
...aiScriptGrammar.default,
|
|
||||||
} as unknown as LanguageRegistration,
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
import { deviceKind } from '@/scripts/device-kind.js';
|
import { deviceKind } from '@/scripts/device-kind.js';
|
||||||
|
|
||||||
const isTouchSupported = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
|
const isTouchSupported = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
|
||||||
@ -16,3 +17,6 @@ if (isTouchSupported && !isTouchUsing) {
|
|||||||
isTouchUsing = true;
|
isTouchUsing = true;
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** (MkHorizontalSwipe) 横スワイプ中か? */
|
||||||
|
export const isHorizontalSwipeSwiping = ref(false);
|
||||||
|
@ -116,6 +116,34 @@ describe('MkUrlPreview', () => {
|
|||||||
assert.strictEqual(iframe?.allow, 'fullscreen;web-share');
|
assert.strictEqual(iframe?.allow, 'fullscreen;web-share');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('A Summaly proxy response without allow falls back to the default', async () => {
|
||||||
|
const iframe = await renderAndOpenPreview({
|
||||||
|
url: 'https://example.local',
|
||||||
|
player: {
|
||||||
|
url: 'https://example.local/player',
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
allow: undefined as any,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.exists(iframe, 'iframe should exist');
|
||||||
|
assert.strictEqual(iframe?.allow, 'autoplay;encrypted-media;fullscreen');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Filtering the allow list from the Summaly proxy', async () => {
|
||||||
|
const iframe = await renderAndOpenPreview({
|
||||||
|
url: 'https://example.local',
|
||||||
|
player: {
|
||||||
|
url: 'https://example.local/player',
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
allow: ['autoplay', 'camera', 'fullscreen'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.exists(iframe, 'iframe should exist');
|
||||||
|
assert.strictEqual(iframe?.allow, 'autoplay;fullscreen');
|
||||||
|
});
|
||||||
|
|
||||||
test('Having a player width should keep the fixed aspect ratio', async () => {
|
test('Having a player width should keep the fixed aspect ratio', async () => {
|
||||||
const iframe = await renderAndOpenPreview({
|
const iframe = await renderAndOpenPreview({
|
||||||
url: 'https://example.local',
|
url: 'https://example.local',
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/matter-js": "0.19.6",
|
"@types/matter-js": "0.19.6",
|
||||||
"@types/node": "20.11.10",
|
"@types/node": "20.11.16",
|
||||||
"@types/seedrandom": "3.0.8",
|
"@types/seedrandom": "3.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "^1.0.0",
|
"@misskey-dev/eslint-plugin": "^1.0.0",
|
||||||
"@readme/openapi-parser": "2.5.0",
|
"@readme/openapi-parser": "2.5.0",
|
||||||
"@types/node": "20.11.10",
|
"@types/node": "20.11.16",
|
||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
|
@ -34,11 +34,11 @@
|
|||||||
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@microsoft/api-extractor": "7.39.4",
|
"@microsoft/api-extractor": "7.40.1",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@swc/jest": "0.2.31",
|
"@swc/jest": "0.2.36",
|
||||||
"@types/jest": "29.5.11",
|
"@types/jest": "29.5.12",
|
||||||
"@types/node": "20.11.10",
|
"@types/node": "20.11.16",
|
||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/node": "20.11.10",
|
"@types/node": "20.11.16",
|
||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
"eslint": "8.56.0",
|
"eslint": "8.56.0",
|
||||||
|
2677
pnpm-lock.yaml
2677
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user