From f9fc743c0581cc3043cb0ff4ffb137113d7797b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:42:42 +0900 Subject: [PATCH 001/121] build(deps): bump tibdex/github-app-token from 1 to 2 (#11815) Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 1 to 2. - [Release notes](https://github.com/tibdex/github-app-token/releases) - [Commits](https://github.com/tibdex/github-app-token/compare/v1...v2) --- updated-dependencies: - dependency-name: tibdex/github-app-token dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ok-to-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml index 71d09f781..c02b980e4 100644 --- a/.github/workflows/ok-to-test.yml +++ b/.github/workflows/ok-to-test.yml @@ -17,7 +17,7 @@ jobs: # See app.yml for an example app manifest - name: Generate token id: generate_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.DEPLOYBOT_APP_ID }} private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }} From 42c7aad251e1ddce52a8ea04eedfe59fce27b4fc Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 12 Sep 2023 15:38:00 +0900 Subject: [PATCH 002/121] enhance(backend): configure ratelimit for notification creation apis --- .../backend/src/server/api/endpoints/notifications/create.ts | 5 +++++ .../server/api/endpoints/notifications/test-notification.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 020fc2d3d..268628cf7 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -14,6 +14,11 @@ export const meta = { kind: 'write:notifications', + limit: { + duration: 1000 * 60, + max: 10, + }, + errors: { }, } as const; diff --git a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts index 04a68a805..8f5f8485c 100644 --- a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts +++ b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts @@ -13,6 +13,11 @@ export const meta = { requireCredential: true, kind: 'write:notifications', + + limit: { + duration: 1000 * 60, + max: 10, + }, } as const; export const paramDef = { From 3456680e1d827bada1009aad7a7247ae17f01184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:48:19 +0900 Subject: [PATCH 003/121] =?UTF-8?q?=E6=96=B0=E3=81=97=E3=81=84=E5=AE=9F?= =?UTF-8?q?=E7=B8=BE=E3=82=92=E8=BF=BD=E5=8A=A0=20(#11817)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (add) new achievement * (update) changelog * Update test-notification.ts * tweak --------- Co-authored-by: syuilo --- CHANGELOG.md | 3 +- locales/index.d.ts | 4 +++ locales/ja-JP.yml | 3 ++ .../backend/src/core/AchievementService.ts | 1 + .../frontend/src/pages/settings/general.vue | 32 +++++++++++++++-- .../src/pages/settings/notifications.vue | 7 ++-- packages/frontend/src/scripts/achievements.ts | 6 ++++ .../frontend/src/scripts/test-notification.ts | 34 ------------------- 8 files changed, 51 insertions(+), 39 deletions(-) delete mode 100644 packages/frontend/src/scripts/test-notification.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c93f70d..1cc6b3f0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - センシティブチャンネルのノートはユーザープロフィールに表示されません - 二要素認証のバックアップコードが生成されるようになりました ref. https://github.com/MisskeyIO/misskey/pull/121 - 二要素認証でパスキーをサポートするようになりました +- 通知をテストできるようになりました ### Client - プロフィールにその人が作ったPlayの一覧出せるように @@ -33,7 +34,6 @@ - 投稿フォームのプレビューの表示状態を記憶するように - AiScriptからMisskeyサーバーAPIを呼び出す際の制限を撤廃 - Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`) -- 通知をテストできるように - Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように - Enhance: 自分が押したリアクションのデザインを改善 - Enhance: ノート検索にローカルのみ検索可能なオプションの追加 @@ -46,6 +46,7 @@ - リアクションの表示サイズをより大きくできるように - ノート詳細ページ読み込み時のパフォーマンスを改善 - タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 +- 新しい実績を追加 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正 - Fix: iOSで画面を回転させるとテキストサイズが変わる問題を修正 diff --git a/locales/index.d.ts b/locales/index.d.ts index 36897285c..746c51c99 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1467,6 +1467,10 @@ export interface Locale { "description": string; "flavor": string; }; + "_smashTestNotificationButton": { + "title": string; + "description": string; + }; }; }; "_role": { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f9427e13e..3cde23ca9 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1391,6 +1391,9 @@ _achievements: title: "Brain Diver" description: "Brain Diverへのリンクを投稿した" flavor: "Misskey-Misskey La-Tu-Ma" + _smashTestNotificationButton: + title: "テスト過剰" + description: "通知のテストをごく短時間のうちに連続して行った" _role: new: "ロールの作成" diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index aa810015e..a35acd368 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -85,6 +85,7 @@ export const ACHIEVEMENT_TYPES = [ 'setNameToSyuilo', 'cookieClicked', 'brainDiver', + 'smashTestNotificationButton', ] as const; @Injectable() diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 31d5dd93e..b486e6d80 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only - {{ i18n.ts._notification.checkNotificationBehavior }} + {{ i18n.ts._notification.checkNotificationBehavior }} @@ -176,6 +176,7 @@ SPDX-License-Identifier: AGPL-3.0-only - - diff --git a/packages/frontend/src/components/MkRenotedUsersDialog.vue b/packages/frontend/src/components/MkRenotedUsersDialog.vue deleted file mode 100644 index 5e6784bb9..000000000 --- a/packages/frontend/src/components/MkRenotedUsersDialog.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 5bda993ff..d9fae946d 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -238,18 +238,6 @@ export function getNoteMenu(props: { os.pageWindow(`/notes/${appearNote.id}`); } - function showReactions(): void { - os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), { - noteId: appearNote.id, - }, {}, 'closed'); - } - - function showRenotes(): void { - os.popup(defineAsyncComponent(() => import('@/components/MkRenotedUsersDialog.vue')), { - noteId: appearNote.id, - }, {}, 'closed'); - } - async function translate(): Promise { if (props.translation.value != null) return; props.translating.value = true; @@ -279,14 +267,6 @@ export function getNoteMenu(props: { icon: 'ti ti-info-circle', text: i18n.ts.details, action: openDetail, - }, { - icon: 'ti ti-repeat', - text: i18n.ts.renotesList, - action: showRenotes, - }, { - icon: 'ti ti-icons', - text: i18n.ts.reactionsList, - action: showReactions, }, { icon: 'ti ti-copy', text: i18n.ts.copyContent, From f7c6932a83f80b6c43bcb79dcd11e8d490ce242a Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 10:55:26 +0900 Subject: [PATCH 020/121] =?UTF-8?q?enhance:=20=E5=90=84=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=8C=E8=A2=AB=E3=82=AF=E3=83=AA=E3=83=83=E3=83=97?= =?UTF-8?q?=E6=95=B0=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=80=81=E7=84=A1=E6=84=8F=E5=91=B3?= =?UTF-8?q?=E3=81=ABnotes/clips=E3=82=92=E5=8F=A9=E3=81=8B=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../1694850832075-server-icons-and-manifest.js | 5 +++++ .../migration/1694915420864-clipped-count.js | 16 ++++++++++++++++ .../src/core/entities/NoteEntityService.ts | 2 ++ packages/backend/src/models/entities/Note.ts | 5 +++++ .../src/server/api/endpoints/clips/add-note.ts | 9 +++++++-- .../server/api/endpoints/clips/remove-note.ts | 5 +++++ packages/frontend/src/pages/note.vue | 11 ++++++----- packages/misskey-js/etc/misskey-js.api.md | 1 + packages/misskey-js/src/entities.ts | 1 + 9 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 packages/backend/migration/1694915420864-clipped-count.js diff --git a/packages/backend/migration/1694850832075-server-icons-and-manifest.js b/packages/backend/migration/1694850832075-server-icons-and-manifest.js index cbbb07d39..1bd8979d9 100644 --- a/packages/backend/migration/1694850832075-server-icons-and-manifest.js +++ b/packages/backend/migration/1694850832075-server-icons-and-manifest.js @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + export class ServerIconsAndManifest1694850832075 { name = 'ServerIconsAndManifest1694850832075' diff --git a/packages/backend/migration/1694915420864-clipped-count.js b/packages/backend/migration/1694915420864-clipped-count.js new file mode 100644 index 000000000..1ad8e04ce --- /dev/null +++ b/packages/backend/migration/1694915420864-clipped-count.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class ClippedCount1694915420864 { + name = 'ClippedCount1694915420864' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "clippedCount" smallint NOT NULL DEFAULT '0'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "clippedCount"`); + } +} diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index add50457c..242ef07e7 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -340,6 +340,8 @@ export class NoteEntityService implements OnModuleInit { url: note.url ?? undefined, ...(opts.detail ? { + clippedCount: note.clippedCount, + reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, { detail: false, _hint_: options?._hint_, diff --git a/packages/backend/src/models/entities/Note.ts b/packages/backend/src/models/entities/Note.ts index 42343f015..effc1509e 100644 --- a/packages/backend/src/models/entities/Note.ts +++ b/packages/backend/src/models/entities/Note.ts @@ -107,6 +107,11 @@ export class MiNote { }) public repliesCount: number; + @Column('smallint', { + default: 0, + }) + public clippedCount: number; + @Column('jsonb', { default: {}, }) diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index e7fae8662..00b8bb09a 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -8,7 +8,7 @@ import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; -import type { ClipNotesRepository, ClipsRepository } from '@/models/_.js'; +import type { ClipNotesRepository, ClipsRepository, NotesRepository } from '@/models/_.js'; import { GetterService } from '@/server/api/GetterService.js'; import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; @@ -72,6 +72,9 @@ export default class extends Endpoint { // eslint- @Inject(DI.clipNotesRepository) private clipNotesRepository: ClipNotesRepository, + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private idService: IdService, private roleService: RoleService, private getterService: GetterService, @@ -115,9 +118,11 @@ export default class extends Endpoint { // eslint- clipId: clip.id, }); - await this.clipsRepository.update(clip.id, { + this.clipsRepository.update(clip.id, { lastClippedAt: new Date(), }); + + this.notesRepository.increment({ id: note.id }, 'clippedCount', 1); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 53389d028..65fad5f97 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -52,6 +52,9 @@ export default class extends Endpoint { // eslint- @Inject(DI.clipNotesRepository) private clipNotesRepository: ClipNotesRepository, + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { @@ -73,6 +76,8 @@ export default class extends Endpoint { // eslint- noteId: note.id, clipId: clip.id, }); + + this.notesRepository.decrement({ id: note.id }, 'clippedCount', 1); }); } } diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index e1f80bf12..ea5e4f509 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -94,13 +94,14 @@ function fetchNote() { noteId: props.noteId, }).then(res => { note = res; - Promise.all([ + // 古いノートは被クリップ数をカウントしていないので、2023-10-01以前のものは強制的にnotes/clipsを叩く + if (note.clippedCount > 0 || new Date(note.createdAt).getTime() < new Date('2023-10-01').getTime()) { os.api('notes/clips', { noteId: note.id, - }), - ]).then(([_clips]) => { - clips = _clips; - }); + }).then((_clips) => { + clips = _clips; + }); + } }).catch(err => { error = err; }); diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index f993f59b6..99b3852b0 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2539,6 +2539,7 @@ type Note = { reactions: Record; renoteCount: number; repliesCount: number; + clippedCount?: number; poll?: { expiresAt: DateString | null; multiple: boolean; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index c556df8f5..287633910 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -175,6 +175,7 @@ export type Note = { reactions: Record; renoteCount: number; repliesCount: number; + clippedCount?: number; poll?: { expiresAt: DateString | null; multiple: boolean; From fba08c63100474fbefc910e1510694a9a582be99 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 14:36:57 +0900 Subject: [PATCH 021/121] fix --- .../backend/src/server/api/endpoints/clips/remove-note.ts | 2 +- packages/backend/test/e2e/clips.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 65fad5f97..28a2f8ebd 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { ClipNotesRepository, ClipsRepository } from '@/models/_.js'; +import type { ClipNotesRepository, ClipsRepository, NotesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts index 833b48f73..1a94eb77a 100644 --- a/packages/backend/test/e2e/clips.ts +++ b/packages/backend/test/e2e/clips.ts @@ -911,7 +911,7 @@ describe('クリップ', () => { const bobClip = await create({ isPublic: true }, { user: bob } ); await addNote({ clipId: bobClip.id, noteId: aliceNote.id }, { user: bob }); const res = await notes({ clipId: bobClip.id }); - assert.deepStrictEqual(res, [aliceNote]); + assert.deepStrictEqual(res.map(x => x.id), [aliceNote.id]); }); test('はPublicなクリップなら認証なしでも取得できる。(非公開ノートはhideされて返ってくる)', async () => { @@ -928,8 +928,8 @@ describe('クリップ', () => { hiddenNote(aliceFollowersNote), hiddenNote(aliceSpecifiedNote), ]; assert.deepStrictEqual( - res.sort(compareBy(s => s.id)), - expects.sort(compareBy(s => s.id))); + res.sort(compareBy(s => s.id)).map(x => x.id), + expects.sort(compareBy(s => s.id)).map(x => x.id)); }); test.todo('ブロック、ミュートされたユーザーからの設定&取得etc.'); From 0260a6af85984aec67ece3e2baadb6ec6e221267 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 15:11:33 +0900 Subject: [PATCH 022/121] fix test --- packages/backend/test/e2e/clips.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/backend/test/e2e/clips.ts b/packages/backend/test/e2e/clips.ts index 1a94eb77a..dfdc044ca 100644 --- a/packages/backend/test/e2e/clips.ts +++ b/packages/backend/test/e2e/clips.ts @@ -721,7 +721,7 @@ describe('クリップ', () => { await addNote({ clipId: aliceClip.id, noteId: aliceNote.id }); const res = await show({ clipId: aliceClip.id }); assert.strictEqual(res.lastClippedAt, new Date(res.lastClippedAt ?? '').toISOString()); - assert.deepStrictEqual(await notes({ clipId: aliceClip.id }), [aliceNote]); + assert.deepStrictEqual((await notes({ clipId: aliceClip.id })).map(x => x.id), [aliceNote.id]); // 他人の非公開ノートも突っ込める await addNote({ clipId: aliceClip.id, noteId: bobHomeNote.id }); @@ -861,8 +861,8 @@ describe('クリップ', () => { bobNote, bobHomeNote, ]; assert.deepStrictEqual( - res.sort(compareBy(s => s.id)), - expects.sort(compareBy(s => s.id))); + res.sort(compareBy(s => s.id)).map(x => x.id), + expects.sort(compareBy(s => s.id)).map(x => x.id)); }); test('を始端IDとlimitで取得できる。', async () => { @@ -881,8 +881,8 @@ describe('クリップ', () => { // Promise.allで返ってくる配列はID順で並んでないのでソートして厳密比較 const expects = [noteList[3], noteList[4], noteList[5]]; assert.deepStrictEqual( - res.sort(compareBy(s => s.id)), - expects.sort(compareBy(s => s.id))); + res.sort(compareBy(s => s.id)).map(x => x.id), + expects.sort(compareBy(s => s.id)).map(x => x.id)); }); test('をID範囲指定で取得できる。', async () => { @@ -901,8 +901,8 @@ describe('クリップ', () => { // Promise.allで返ってくる配列はID順で並んでないのでソートして厳密比較 const expects = [noteList[2], noteList[3]]; assert.deepStrictEqual( - res.sort(compareBy(s => s.id)), - expects.sort(compareBy(s => s.id))); + res.sort(compareBy(s => s.id)).map(x => x.id), + expects.sort(compareBy(s => s.id)).map(x => x.id)); }); test.todo('Remoteのノートもクリップできる。どうテストしよう?'); From 0dca6afa1f1d5d62dc9735cf7e5ec5ce12df34c6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 15:43:50 +0900 Subject: [PATCH 023/121] =?UTF-8?q?fix(backend):=20MK=5FONLY=5FSERVER?= =?UTF-8?q?=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=81=97=E3=81=9F=E9=9A=9B=E3=81=AB=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=83=83=E3=82=B7=E3=83=A5=E3=81=99=E3=82=8B=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: #11811 --- CHANGELOG.md | 1 + packages/backend/src/boot/master.ts | 33 ++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a2f88a9..dacaee692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ - nodeinfo 2.1対応 - 自分へのメンション一覧を取得する際のパフォーマンスを向上 - Docker環境でjemallocを使用することでメモリ使用量を削減 +- Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように - Fix: 一部のfeatured noteを照会できない問題を修正 - Fix: muteがapiからのuser list timeline取得で機能しない問題を修正 diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 0bf23c770..a45ea2bb8 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -68,21 +68,34 @@ export async function masterMain() { process.exit(1); } - if (envOption.onlyServer) { - await server(); - } else if (envOption.onlyQueue) { - await jobQueue(); - } else { - await server(); - } - bootLogger.succ('Misskey initialized'); - if (!envOption.disableClustering) { + if (envOption.disableClustering) { + if (envOption.onlyServer) { + await server(); + } else if (envOption.onlyQueue) { + await jobQueue(); + } else { + await server(); + await jobQueue(); + } + } else { + if (envOption.onlyServer) { + // nop + } else if (envOption.onlyQueue) { + // nop + } else { + await server(); + } + await spawnWorkers(config.clusterLimit); } - bootLogger.succ(config.socket ? `Now listening on socket ${config.socket} on ${config.url}` : `Now listening on port ${config.port} on ${config.url}`, null, true); + if (envOption.onlyQueue) { + bootLogger.succ('Queue started', null, true); + } else { + bootLogger.succ(config.socket ? `Now listening on socket ${config.socket} on ${config.url}` : `Now listening on port ${config.port} on ${config.url}`, null, true); + } } function showEnvironment(): void { From 52ec1b3fdef05cbed4ff216b5317b60735caac00 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 15:45:29 +0900 Subject: [PATCH 024/121] New Crowdin updates (#11836) * New translations ja-JP.yml (Romanian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (Czech) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Dutch) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (Portuguese) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Slovak) * New translations ja-JP.yml (Swedish) * New translations ja-JP.yml (Ukrainian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Vietnamese) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Bengali) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Uzbek) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Romanian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (Catalan) * New translations ja-JP.yml (Czech) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Greek) * New translations ja-JP.yml (Hungarian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Dutch) * New translations ja-JP.yml (Norwegian) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (Portuguese) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Slovak) * New translations ja-JP.yml (Swedish) * New translations ja-JP.yml (Turkish) * New translations ja-JP.yml (Ukrainian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Vietnamese) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Bengali) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Uzbek) * New translations ja-JP.yml (Kannada) * New translations ja-JP.yml (Lao) * New translations ja-JP.yml (Kabyle) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Chinese Traditional) --- locales/ar-SA.yml | 3 ++- locales/bn-BD.yml | 3 ++- locales/ca-ES.yml | 2 ++ locales/cs-CZ.yml | 5 ++++- locales/de-DE.yml | 10 +++++++++- locales/el-GR.yml | 2 ++ locales/en-US.yml | 14 +++++++++++--- locales/es-ES.yml | 21 +++++++++++++++++---- locales/fr-FR.yml | 5 +++-- locales/hu-HU.yml | 1 + locales/id-ID.yml | 5 ++++- locales/it-IT.yml | 7 +++++-- locales/ja-KS.yml | 5 ++++- locales/kab-KAB.yml | 1 + locales/kn-IN.yml | 1 + locales/ko-KR.yml | 5 ++++- locales/lo-LA.yml | 2 ++ locales/nl-NL.yml | 3 ++- locales/no-NO.yml | 2 ++ locales/pl-PL.yml | 3 ++- locales/pt-PT.yml | 5 ++++- locales/ro-RO.yml | 3 ++- locales/ru-RU.yml | 5 ++++- locales/sk-SK.yml | 3 ++- locales/sv-SE.yml | 3 ++- locales/th-TH.yml | 5 ++++- locales/tr-TR.yml | 2 ++ locales/uk-UA.yml | 3 ++- locales/uz-UZ.yml | 3 ++- locales/vi-VN.yml | 3 ++- locales/zh-CN.yml | 8 +++++++- locales/zh-TW.yml | 22 ++++++++++++++++------ 32 files changed, 129 insertions(+), 36 deletions(-) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 920914ff1..4d5872c64 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -348,7 +348,6 @@ invite: "دعوة" driveCapacityPerLocalAccount: "حصة التخزين لكل مستخدم محلي" driveCapacityPerRemoteAccount: "حصة التخزين لكل مستخدم بعيد" inMb: "بالميغابايت" -iconUrl: "رابط الأيقونة" bannerUrl: "رابط صورة اللافتة" backgroundImageUrl: "رابط صورة الخلفية" basicInfo: "المعلومات الأساسية " @@ -998,6 +997,8 @@ expirationDate: "تاريخ انتهاء الصلاحية" unused: "غير مستعمَل" expired: "منتهية صلاحيته" icon: "الصورة الرمزية" +replies: "رد" +renotes: "أعد النشر" _initialAccountSetting: accountCreated: "نجح إنشاء حسابك!" letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي." diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index d78b58535..f3475de22 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -328,7 +328,6 @@ invite: "আমন্ত্রণ" driveCapacityPerLocalAccount: "প্রত্যেক স্থানীয় ব্যাবহারকারীর জন্য ড্রাইভের জায়গা" driveCapacityPerRemoteAccount: "প্রত্যেক রিমোট ব্যাবহারকারীর জন্য ড্রাইভের জায়গা" inMb: "মেগাবাইটে লিখুন" -iconUrl: "আইকনের URL (ফ্যাভিকন, ইত্যাদি)" bannerUrl: "ব্যানার ছবির URL" backgroundImageUrl: "পটভূমির চিত্রের URL" basicInfo: "আপনার ব্যক্তিগত তথ্য" @@ -839,6 +838,8 @@ color: "রং" horizontal: "পাশে" youFollowing: "অনুসরণ করা হচ্ছে" icon: "প্রোফাইল ছবি" +replies: "জবাব" +renotes: "রিনোট" _role: priority: "অগ্রাধিকার" _priority: diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index e93b804dc..ec52c5761 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -381,6 +381,8 @@ user: "Usuaris" global: "Global" searchByGoogle: "Cercar" file: "Fitxers" +replies: "Respondre" +renotes: "Impulsa" _role: _options: antennaMax: "Nombre màxim d'antenes" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 200768094..18e7f02e6 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -354,7 +354,6 @@ invite: "Pozvat" driveCapacityPerLocalAccount: "Kapacita disku na lokálního uživatele" driveCapacityPerRemoteAccount: "Kapacita disku na vzdáleného uživatele" inMb: "V megabajtech" -iconUrl: "Favicon URL" bannerUrl: "Baner URL" backgroundImageUrl: "Adresa URL obrázku pozadí" basicInfo: "Základní informace" @@ -1095,6 +1094,8 @@ doYouAgree: "Souhlasíte?" beSureToReadThisAsItIsImportant: "Přečtěte si prosím tyto důležité informace." iHaveReadXCarefullyAndAgree: "Přečetl jsem si text \"{x}\" a souhlasím s ním." icon: "Avatar" +replies: "Odpovědět" +renotes: "Přeposlat" _initialAccountSetting: accountCreated: "Váš účet byl úspěšně vytvořen!" letsStartAccountSetup: "Pro začátek si nastavte svůj profil." @@ -1112,6 +1113,8 @@ _initialAccountSetting: laterAreYouSure: "Opravdu chcete provést nastavení profilu později?" _serverRules: description: "Soubor pravidel, která se zobrazí před registrací. Doporučuje se nastavit shrnutí podmínek služby." +_serverSettings: + iconUrl: "URL ikony" _accountMigration: moveFrom: "Migrace jiného účtu na tento účet" moveFromSub: "Vytvořit alias na jiný účet" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index ec8dc6074..bba4566a5 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -356,7 +356,6 @@ invite: "Einladen" driveCapacityPerLocalAccount: "Drive-Kapazität pro lokalem Benutzerkonto" driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer fremder Instanzen" inMb: "In Megabytes" -iconUrl: "Icon-URL (favicon etc)" bannerUrl: "Banner-URL" backgroundImageUrl: "Hintergrundbild-URL" basicInfo: "Grundlegende Informationen" @@ -1108,6 +1107,8 @@ currentAnnouncements: "Aktuelle Ankündigungen" pastAnnouncements: "Alte Ankündigungen" youHaveUnreadAnnouncements: "Es gibt neue Ankündigungen." useSecurityKey: "Folge bitten den Anweisungen deines Browsers bzw. Gerätes und verwende deinen Hardware-Sicherheitsschlüssel oder Passkey." +replies: "Antworten" +renotes: "Renote" _announcement: forExistingUsers: "Nur für existierende Nutzer" forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt." @@ -1134,6 +1135,13 @@ _initialAccountSetting: laterAreYouSure: "Die Kontoeinrichtung wirklich später erledigen?" _serverRules: description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen." +_serverSettings: + iconUrl: "Icon-URL" + appIconDescription: "Gibt das zu verwendende Icon bei der Anzeige von {host} als App an." + appIconUsageExample: "Beispielsweise als PWA, oder bei Lesezeichen auf dem Startbildschirm von Smartphones" + appIconStyleRecommendation: "Da das Icon zu einem Kreis oder Quadrat zugeschnitten wird, wird ein Icon mit gefülltem Margin um den Inhalt herum empfohlen." + appIconResolutionMustBe: "Die Mindestauflösung ist {resolution}." + manifestJsonOverride: "Überschreiben von manifest.json" _accountMigration: moveFrom: "Von einem anderen Konto zu diesem migrieren" moveFromSub: "Alias für ein anderes Konto erstellen" diff --git a/locales/el-GR.yml b/locales/el-GR.yml index 558d466f3..e8ed6f118 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -288,6 +288,8 @@ file: "Αρχεία" recommended: "Προτεινόμενα" cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω ανεπαρκούς Αποθηκευτικού Χώρου" icon: "Εικονίδιο" +replies: "Απάντηση" +renotes: "Κοινοποίηση σημειώματος" _email: _follow: title: "Έχετε ένα νέο ακόλουθο" diff --git a/locales/en-US.yml b/locales/en-US.yml index f7193a98f..e0358a846 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -48,7 +48,7 @@ copyLink: "Copy link" copyLinkRenote: "Copy renote link" delete: "Delete" deleteAndEdit: "Delete and edit" -deleteAndEditConfirm: "Are you sure you want to delete this note and edit it? You will lose all reactions, renotes and replies to it." +deleteAndEditConfirm: "Are you sure you want to redraft this note? This means you will lose all reactions, renotes, and replies to it." addToList: "Add to list" addToAntenna: "Add to antenna" sendMessage: "Send a message" @@ -106,7 +106,7 @@ unfollow: "Unfollow" followRequestPending: "Follow request pending" enterEmoji: "Enter an emoji" renote: "Renote" -unrenote: "Take back renote" +unrenote: "Remove renote" renoted: "Renoted." cantRenote: "This post can't be renoted." cantReRenote: "A renote can't be renoted." @@ -356,7 +356,6 @@ invite: "Invite" driveCapacityPerLocalAccount: "Drive capacity per local user" driveCapacityPerRemoteAccount: "Drive capacity per remote user" inMb: "In megabytes" -iconUrl: "Icon URL" bannerUrl: "Banner image URL" backgroundImageUrl: "Background image URL" basicInfo: "Basic info" @@ -1108,6 +1107,8 @@ currentAnnouncements: "Current announcements" pastAnnouncements: "Past announcements" youHaveUnreadAnnouncements: "There are unread announcements." useSecurityKey: "Please follow your browser's or device's instructions to use your security- or passkey." +replies: "Reply" +renotes: "Renote" _announcement: forExistingUsers: "Existing users only" forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it." @@ -1134,6 +1135,13 @@ _initialAccountSetting: laterAreYouSure: "Really do profile setup later?" _serverRules: description: "A set of rules to be displayed before registration. Setting a summary of the Terms of Service is recommended." +_serverSettings: + iconUrl: "Icon URL" + appIconDescription: "Specifies the icon to use when {host} is displayed as an app." + appIconUsageExample: "E.g. As PWA, or when displayed as a home screen bookmark on a phone" + appIconStyleRecommendation: "As the icon may be cropped to a square or circle, an icon with colored margin around the content is recommended." + appIconResolutionMustBe: "The minimum resolution is {resolution}." + manifestJsonOverride: "manifest.json Override" _accountMigration: moveFrom: "Migrate another account to this one" moveFromSub: "Create alias to another account" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index f96da7e0b..bd638af9c 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -20,7 +20,7 @@ noNotes: "No hay notas" noNotifications: "No hay notificaciones" instance: "Instancia" settings: "Configuración" -notificationSettings: "Configurar las notificaciones" +notificationSettings: "Ajustes de notificaciones" basicSettings: "Configuración básica" otherSettings: "Configuración avanzada" openInWindow: "Abrir en una ventana" @@ -56,8 +56,8 @@ copyRSS: "Copiar RSS" copyUsername: "Copiar nombre de usuario" copyUserId: "Copiar ID del usuario" copyNoteId: "Copiar ID de la nota" -copyFileId: "Copiar un archivo ID" -copyFolderId: "Copiar carpeta ID" +copyFileId: "Copiar ID del archivo" +copyFolderId: "Copiar ID de carpeta" copyProfileUrl: "Copiar la URL del perfil" searchUser: "Buscar un usuario" reply: "Responder" @@ -356,7 +356,6 @@ invite: "Invitar" driveCapacityPerLocalAccount: "Capacidad del drive por usuario local" driveCapacityPerRemoteAccount: "Capacidad del drive por usuario remoto" inMb: "En megabytes" -iconUrl: "URL de la imagen del avatar" bannerUrl: "URL de la imagen del banner" backgroundImageUrl: "URL de la imagen de fondo" basicInfo: "Información básica" @@ -417,6 +416,8 @@ totp: "Aplicación autentícadora" totpDescription: "Ingresa una contaseña de un sólo uso usando la aplicación autenticadora" moderator: "Moderador" moderation: "Moderación" +moderationNote: "Nota de moderación" +addModerationNote: "Añadir nota de moderación" nUsersMentioned: "{n} usuarios mencionados" securityKeyAndPasskey: "Clave de seguridad / clave de paso" securityKey: "Clave de seguridad" @@ -1106,6 +1107,8 @@ currentAnnouncements: "Anuncios actuales" pastAnnouncements: "Anuncios anteriores" youHaveUnreadAnnouncements: "Hay anuncios sin leer" useSecurityKey: "Por favor, sigue las instrucciones de tu dispositivo o navegador para usar tu clave de seguridad o tu clave de paso." +replies: "Responder" +renotes: "Renotar" _announcement: forExistingUsers: "Solo para usuarios registrados" forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán." @@ -1132,6 +1135,9 @@ _initialAccountSetting: laterAreYouSure: "¿Realmente quieres configurar tu perfil después?" _serverRules: description: "Un conjunto de reglas que serán mostradas antes del registro. Configurar un sumario de términos de servicio es recomendado." +_serverSettings: + iconUrl: "URL del ícono" + manifestJsonOverride: "Sobreescribir manifest.json" _accountMigration: moveFrom: "Trasladar de otra cuenta a ésta" moveFromSub: "Crear un alias para otra cuenta." @@ -1386,6 +1392,9 @@ _achievements: title: "Brain Diver" description: "Publicaste un vínculo a \"Brain Diver\"" flavor: "Misskey-Misskey La-Tu-Ma" + _smashTestNotificationButton: + title: "Sobrecarga de pruebas" + description: "Envía muchas notificaciones de prueba en un corto espacio de tiempo" _role: new: "Crear rol" edit: "Editar rol" @@ -1995,6 +2004,10 @@ _notification: unreadAntennaNote: "Antena {name}" emptyPushNotificationMessage: "Se han actualizado las notificaciones push" achievementEarned: "Logro desbloqueado" + testNotification: "Notificación de prueba" + checkNotificationBehavior: "Comprobar comportamiento de la notificación" + sendTestNotification: "Enviar notificación de prueba" + notificationWillBeDisplayedLikeThis: "Las notificaciones tendrán este aspecto" _types: all: "Todo" follow: "Siguiendo" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index ff890f159..3d9de3ef3 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -47,7 +47,7 @@ copyContent: "Copier le contenu" copyLink: "Copier le lien" delete: "Supprimer" deleteAndEdit: "Supprimer et réécrire" -deleteAndEditConfirm: "Êtes-vous sûr·e de vouloir supprimer cette note et la reformuler ? Vous perdrez toutes les réactions, renotes et réponses y afférentes." +deleteAndEditConfirm: "Êtes-vous sûr de vouloir effacer cette note et la modifier ? Vous perdrez toutes les réactions, renotes et réponses." addToList: "Ajouter à une liste" addToAntenna: "Ajouter à l’antenne" sendMessage: "Envoyer un message" @@ -351,7 +351,6 @@ invite: "Inviter" driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local" driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant" inMb: "en mégaoctets" -iconUrl: "URL de l'icône" bannerUrl: "URL de l’image de la bannière" backgroundImageUrl: "URL de l'image d'arrière-plan" basicInfo: "Informations basiques" @@ -988,6 +987,8 @@ expired: "Expiré" doYouAgree: "Êtes-vous d’accord ?" icon: "Avatar" forYou: "Pour vous" +replies: "Répondre" +renotes: "Renoter" _announcement: readConfirmTitle: "Marquer comme lu ?" _initialAccountSetting: diff --git a/locales/hu-HU.yml b/locales/hu-HU.yml index fdab9645c..023a91494 100644 --- a/locales/hu-HU.yml +++ b/locales/hu-HU.yml @@ -77,6 +77,7 @@ smtpUser: "Felhasználónév" smtpPass: "Jelszó" user: "Felhasználók" searchByGoogle: "Keresés" +renotes: "Renote" _theme: keys: renote: "Renote" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 0d225850f..c817850fe 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -354,7 +354,6 @@ invite: "Undang" driveCapacityPerLocalAccount: "Kapasitas drive per pengguna lokal" driveCapacityPerRemoteAccount: "Kapasitas drive per pengguna remote" inMb: "dalam Megabytes" -iconUrl: "URL Gambar ikon" bannerUrl: "URL Banner" backgroundImageUrl: "URL Gambar latar" basicInfo: "Informasi Umum" @@ -1099,6 +1098,8 @@ icon: "Avatar" forYou: "Untuk Anda" currentAnnouncements: "Pengumuman Saat Ini" pastAnnouncements: "Pengumuman Terdahulu" +replies: "Balas" +renotes: "Renote" _initialAccountSetting: accountCreated: "Akun kamu telah sukses dibuat!" letsStartAccountSetup: "Untuk pemula, ayo atur profilmu dulu." @@ -1116,6 +1117,8 @@ _initialAccountSetting: laterAreYouSure: "Yakin banget untuk atur profil nanti?" _serverRules: description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan." +_serverSettings: + iconUrl: "URL ikon" _accountMigration: moveFrom: "Pindahkan akun lain ke akun ini" moveFromSub: "Buat alias ke akun lain" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 41758737b..b073cdcdd 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -106,7 +106,7 @@ unfollow: "Non seguire" followRequestPending: "Richiesta in approvazione" enterEmoji: "Inserisci emoji" renote: "Rinota" -unrenote: "Annulla rinota" +unrenote: "Elimina la Rinota" renoted: "Rinotato!" cantRenote: "È impossibile rinotare questa nota." cantReRenote: "È impossibile rinotare una Rinota." @@ -356,7 +356,6 @@ invite: "Invita" driveCapacityPerLocalAccount: "Capienza del Drive per profilo locale" driveCapacityPerRemoteAccount: "Capienza del Drive per profilo remoto" inMb: "in Megabytes" -iconUrl: "URL di icona (favicon, ecc.)" bannerUrl: "URL dell'immagine d'intestazione" backgroundImageUrl: "URL dello sfondo" basicInfo: "Informazioni fondamentali" @@ -1108,6 +1107,8 @@ currentAnnouncements: "Annunci attuali" pastAnnouncements: "Annunci precedenti" youHaveUnreadAnnouncements: "Ci sono Annunci non letti" useSecurityKey: "Per utilizzare la chiave di sicurezza o la passkey, segui le indicazioni del dispositivo" +replies: "Rispondi" +renotes: "Rinota" _announcement: forExistingUsers: "Solo ai profili attuali" forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio." @@ -1134,6 +1135,8 @@ _initialAccountSetting: laterAreYouSure: "Vuoi davvero rimandare la configurazione iniziale?" _serverRules: description: "In Europa è necessario mostrare l'informativa sul trattamento dei dati personali, prima della registrazione al servizio." +_serverSettings: + iconUrl: "URL dell'icona" _accountMigration: moveFrom: "Migra un altro profilo dentro a questo" moveFromSub: "Crea un alias verso un altro profilo remoto" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 582163b02..7e07138b1 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -355,7 +355,6 @@ invite: "来てや" driveCapacityPerLocalAccount: "ローカルユーザーはんひとりあたりのドライブ容量" driveCapacityPerRemoteAccount: "リモートユーザーはんひとりあたりのドライブ容量" inMb: "メガバイト単位" -iconUrl: "アイコン画像のURL" bannerUrl: "バナー画像のURL" backgroundImageUrl: "背景画像のURL" basicInfo: "基本情報" @@ -1103,6 +1102,8 @@ forYou: "あんたへ" currentAnnouncements: "現在のお知らせやで" pastAnnouncements: "過去のお知らせやで" youHaveUnreadAnnouncements: "あんたまだこのお知らせ読んどらんやろ。" +replies: "返事" +renotes: "Renote" _announcement: forExistingUsers: "もうおるユーザーのみ" forExistingUsersDescription: "有効にすると、このお知らせ作成時点でおるユーザーにのみお知らせが表示されます。無効にすると、このお知らせ作成後にアカウントを作成したユーザーにもお知らせが表示されます。" @@ -1129,6 +1130,8 @@ _initialAccountSetting: laterAreYouSure: "初期設定あとでやり直すん?" _serverRules: description: "新規登録前に見せる、サーバーの簡潔なルールを設定すんで。内容は使うための決め事の要約とすることを推奨するわ。" +_serverSettings: + iconUrl: "アイコン画像のURL" _accountMigration: moveFrom: "別のアカウントからこのアカウントに引っ越す" moveFromSub: "別のアカウントへエイリアスを作る" diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml index 18fd8f5a5..22e24d3ba 100644 --- a/locales/kab-KAB.yml +++ b/locales/kab-KAB.yml @@ -56,6 +56,7 @@ accounts: "Imiḍan" searchByGoogle: "Nadi" file: "Ifuyla" account: "Imiḍan" +replies: "Err" _email: _follow: title: "Yeṭṭafaṛ-ik·em-id" diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml index ef66f3fbd..b3ad46f2b 100644 --- a/locales/kn-IN.yml +++ b/locales/kn-IN.yml @@ -61,6 +61,7 @@ smtpPass: "ಗುಪ್ತಪದ" user: "ಬಳಕೆದಾರ" searchByGoogle: "ಹುಡುಕು" file: "ಕಡತಗಳು" +replies: "ಉತ್ತರಿಸು" _email: _follow: title: "ಹಿಂಬಾಲಿಸಿದರು" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index c9d4b7b22..23996cbe9 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -356,7 +356,6 @@ invite: "초대" driveCapacityPerLocalAccount: "로컬 유저 한 명당 드라이브 용량" driveCapacityPerRemoteAccount: "리모트 유저 한 명당 드라이브 용량" inMb: "메가바이트 단위" -iconUrl: "아이콘 URL" bannerUrl: "배너 이미지 URL" backgroundImageUrl: "배경 이미지 URL" basicInfo: "기본 정보" @@ -1106,6 +1105,8 @@ currentAnnouncements: "현재 공지사항" pastAnnouncements: "과거 공지사항" youHaveUnreadAnnouncements: "읽지 않은 공지사항이 있습니다." useSecurityKey: "브라우저 또는 기기의 안내에 따라 보안 키 또는 패스키를 사용해 주십시오." +replies: "답글" +renotes: "리노트" _announcement: forExistingUsers: "기존 유저에게만 알림" forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다." @@ -1132,6 +1133,8 @@ _initialAccountSetting: laterAreYouSure: "초기 설정을 나중에 진행하시겠습니까?" _serverRules: description: "회원 가입 이전에 간단하게 표시할 서버 규칙입니다. 이용 약관의 요약으로 구성하는 것을 추천합니다." +_serverSettings: + iconUrl: "아이콘 URL" _accountMigration: moveFrom: "다른 계정에서 이 계정으로 이사" moveFromSub: "다른 계정에 대한 별칭을 생성" diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml index 10010e9ce..37251a95c 100644 --- a/locales/lo-LA.yml +++ b/locales/lo-LA.yml @@ -391,6 +391,8 @@ administration: "ການຈັດການ" middle: "ປານກາງ" searchByGoogle: "ຄົ້ນຫາ" file: "ໄຟລ໌" +replies: "ຕອບ​ໄປ​ທີ" +renotes: "Renote" _role: _priority: middle: "ປານກາງ" diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 30c7e1dca..d75f80731 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -338,7 +338,6 @@ invite: "Uitnodigen" driveCapacityPerLocalAccount: "Opslagruimte per lokale gebruiker" driveCapacityPerRemoteAccount: "Opslagruimte per externe gebruiker" inMb: "in megabytes" -iconUrl: "Pictogram URL" bannerUrl: "Banner URL" backgroundImageUrl: "URL afbeelding" basicInfo: "Basisinformatie" @@ -427,6 +426,8 @@ windowMaximize: "Maximaliseren" windowRestore: "Herstellen" loggedInAsBot: "Momenteel als bot ingelogd" icon: "Avatar" +replies: "Antwoord" +renotes: "Herdelen" _email: _follow: title: "volgde jou" diff --git a/locales/no-NO.yml b/locales/no-NO.yml index 72229ab0d..35a3866b9 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -462,6 +462,8 @@ continue: "Fortsett" youFollowing: "Følger" options: "Alternativ" icon: "Avatar" +replies: "Svar" +renotes: "Renote" _initialAccountSetting: theseSettingsCanEditLater: "Du kan endre disse innstillingene senere." _achievements: diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index c472d42ca..065e228c0 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -333,7 +333,6 @@ invite: "Zaproś" driveCapacityPerLocalAccount: "Powierzchnia dyskowa na lokalnego użytkownika" driveCapacityPerRemoteAccount: "Powierzchnia dyskowa na zdalnego użytkownika" inMb: "W megabajtach" -iconUrl: "Adres URL ikony" bannerUrl: "Adres URL banera" backgroundImageUrl: "Adres URL tła" basicInfo: "Podstawowe informacje" @@ -872,6 +871,8 @@ show: "Wyświetlanie" color: "Kolor" youFollowing: "Śledzeni" icon: "Awatar" +replies: "Odpowiedz" +renotes: "Udostępnij" _role: priority: "Priorytet" _priority: diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index c079745a2..826f0b45a 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -356,7 +356,6 @@ invite: "Convidar" driveCapacityPerLocalAccount: "Capacidade do drive por usuário local" driveCapacityPerRemoteAccount: "Capacidade do drive por usuário remoto" inMb: "Em ‘megabytes’" -iconUrl: "URL da imagem do ícone (favicon, etc.)" bannerUrl: "URL da imagem do ‘banner’" backgroundImageUrl: "URL da imagem de fundo" basicInfo: "Informações básicas" @@ -1007,8 +1006,12 @@ rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Se nenhum cargo for espe rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Estes cargos devem ser públicos." waitingForMailAuth: "Verificação de e-mail pendente " icon: "Avatar" +replies: "Responder" +renotes: "Repostar" _initialAccountSetting: followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo." +_serverSettings: + iconUrl: "URL do ícone" _accountMigration: moveFromDescription: "Se você deseja migrar de outra conta para esta, é necessário criar um alias aqui. Por favor, insira a conta de origem da migração no seguinte formato: @username@server.example.com. Para excluir o alias, deixe o campo em branco e clique em salvar (não recomendado)." moveAccountDescription: "Você está migrando para uma nova conta.\n ・Seus seguidores irão automaticamente seguir a nova conta.\n ・Todas as suas conexões de seguidores nesta conta serão removidas.\n ・Você não poderá mais criar novas notas nesta conta.\n\nA migração dos seguidores é automática, mas a migração das pessoas que você segue deve ser feita manualmente. Antes de migrar, exporte quem você está seguindo nesta conta e, assim que migrar, importe essa lista na nova conta.\nO mesmo se aplica para listas, silenciamentos e bloqueios, que também devem ser migrados manualmente.\n\n(Esta descrição se refere ao comportamento do servidor Misskey v13.12.0 ou posterior. Outros softwares ActivityPub, como Mastodon, podem ter comportamentos diferentes.)" diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 1861b2833..605fcc82f 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -328,7 +328,6 @@ invite: "Invită" driveCapacityPerLocalAccount: "Capacitatea Drive-ului per utilizator local" driveCapacityPerRemoteAccount: "Capacitatea Drive-ului per utilizator extern" inMb: "În megabytes" -iconUrl: "URL-ul iconiței" bannerUrl: "URL-ul imaginii de banner" backgroundImageUrl: "URL-ul imaginii de fundal" basicInfo: "Informații de bază" @@ -631,6 +630,8 @@ searchByGoogle: "Caută" file: "Fișiere" show: "Arată" icon: "Avatar" +replies: "Răspunde" +renotes: "Re-notează" _role: _priority: middle: "Mediu" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 3c74b2552..597b5f579 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -354,7 +354,6 @@ invite: "Пригласить" driveCapacityPerLocalAccount: "Объём диска на одного локального пользователя" driveCapacityPerRemoteAccount: "Объём диска на одного пользователя с другого сайта" inMb: "В мегабайтах" -iconUrl: "Ссылка на аватар" bannerUrl: "Ссылка на изображение в шапке" backgroundImageUrl: "Ссылка на фоновое изображение" basicInfo: "Общая информация" @@ -1066,6 +1065,8 @@ unused: "Неиспользуемый" expired: "Срок действия приглашения истёк" doYouAgree: "Согласны?" icon: "Аватар" +replies: "Ответить" +renotes: "Репост" _initialAccountSetting: accountCreated: "Аккаунт успешно создан!" letsStartAccountSetup: "Давайте настроим вашу учётную запись." @@ -1073,6 +1074,8 @@ _initialAccountSetting: privacySetting: "Настройки конфиденциальности" initialAccountSettingCompleted: "Первоначальная настройка успешно завершена!" skipAreYouSure: "Пропустить настройку?" +_serverSettings: + iconUrl: "Адрес на иконку роли" _achievements: earnedAt: "Разблокировано в" _types: diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 9f7e94091..a5639ce59 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -337,7 +337,6 @@ invite: "Pozvať" driveCapacityPerLocalAccount: "Kapacita disku pre používateľa" driveCapacityPerRemoteAccount: "Kapacita disku pre vzdialeného používateľa" inMb: "V megabajtoch" -iconUrl: "Favicon URL" bannerUrl: "URL obrázku bannera" backgroundImageUrl: "URL obrázku pozadia" basicInfo: "Základné informácie" @@ -920,6 +919,8 @@ color: "Farba" horizontal: "Strana" youFollowing: "Sledované" icon: "Avatar" +replies: "Odpovedať" +renotes: "Preposlať" _role: priority: "Priorita" _priority: diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index 5ba206f9f..0c47ef689 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -337,7 +337,6 @@ registration: "Registrera" enableRegistration: "Aktivera registrering av nya användare" invite: "Inbjudan" inMb: "I megabyte" -iconUrl: "URL till profilbilden" bannerUrl: "URL till banner-bilden" basicInfo: "Grundläggande info" pinnedUsers: "Fästa användare" @@ -486,6 +485,8 @@ pleaseDonate: "Misskey är en gratis programvara som används på {host}. Donera resetPasswordConfirm: "Återställ verkligen ditt lösenord?" dataSaver: "Databesparing" icon: "Profilbild" +replies: "Svara" +renotes: "Omnotera" _achievements: _types: _open3windows: diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 350a427bd..6a1cfd505 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -356,7 +356,6 @@ invite: "เชิญชวน" driveCapacityPerLocalAccount: "ความจุของไดรฟ์ต่อผู้ใช้ภายในเครื่อง" driveCapacityPerRemoteAccount: "ความจุของไดรฟ์ต่อผู้ใช้ระยะไกล" inMb: "เป็นเมกะไบต์" -iconUrl: "ไอคอน URL" bannerUrl: "URL รูปภาพแบนเนอร์" backgroundImageUrl: "URL ภาพพื้นหลัง" basicInfo: "ข้อมูลเบื้องต้น" @@ -1101,6 +1100,8 @@ iHaveReadXCarefullyAndAgree: "ฉันได้อ่านข้อควา dialog: "ไดอะล็อก" icon: "ไอคอน" forYou: "สำหรับคุณ" +replies: "ตอบกลับ" +renotes: "รีโน้ต" _announcement: forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน" needConfirmationToReadDescription: "ข้อความแจ้งแยก ถ้าหากต้องการเพื่อยืนยันว่ากำลังทำเครื่องหมายประกาศนี้ว่าอ่านแล้วจะแสดงขึ้นถ้าหากเปิดใช้งาน การประกาศนั้นจะไม่รวมอยู่ในฟังก์ชั่นว่า \"ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว\"" @@ -1125,6 +1126,8 @@ _initialAccountSetting: laterAreYouSure: "ต้องการตั้งค่าโปรไฟล์ในภายหลังจริงๆอย่างงั้นหรอ?" _serverRules: description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ" +_serverSettings: + iconUrl: "ไอคอน URL" _accountMigration: moveFrom: "ย้ายข้อมูลบัญชีอื่นไปยังอีกบัญชีนี้หนึ่ง" moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น" diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index d0b610917..f8fb275eb 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -371,6 +371,8 @@ noRole: "Rol bulunamadı" color: "Renk" addMemo: "Kısa not ekle" icon: "Avatar" +replies: "yanıt" +renotes: "vazgeçme" _accountDelete: started: "Silme işlemi başlatıldı" _email: diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index a6c1c94e6..777933bf5 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -338,7 +338,6 @@ invite: "Запросити" driveCapacityPerLocalAccount: "Об'єм диска на одного локального користувача" driveCapacityPerRemoteAccount: "Об'єм диска на одного віддаленого користувача" inMb: "В мегабайтах" -iconUrl: "URL аватара" bannerUrl: "URL банера" backgroundImageUrl: "URL-адреса фонового зображення" basicInfo: "Основна інформація" @@ -906,6 +905,8 @@ letsLookAtTimeline: "Перегляд історії" horizontal: "Збоку" youFollowing: "Підписки" icon: "Аватар" +replies: "Відповісти" +renotes: "Поширити" _achievements: earnedAt: "Відкрито" _types: diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml index 52577d2d8..8dbcbd9d0 100644 --- a/locales/uz-UZ.yml +++ b/locales/uz-UZ.yml @@ -355,7 +355,6 @@ invite: "Taklif qilish" driveCapacityPerLocalAccount: "Har bir mahalliy foydalanuvchi uchun disk maydoni" driveCapacityPerRemoteAccount: "Har bir masofaviy foydalanuvchi uchun disk maydoni" inMb: "Megabaytlarda" -iconUrl: "Ikonkaning URL manzili (masalan: favicon)" bannerUrl: "Banner URLi" backgroundImageUrl: "Fon rasmi URL manzili" basicInfo: "Asosiy ma'lumot" @@ -844,6 +843,8 @@ rolesAssignedToMe: "Mening rollarim" resetPasswordConfirm: "Qayta parol o'rnatmoqchimisiz?" sensitiveWords: "Ta'sirchan so'zlar" icon: "Avatar" +replies: "Javob berish" +renotes: "Qayta qayd etish" _achievements: _types: _viewInstanceChart: diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 569b0b42b..2187d3680 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -354,7 +354,6 @@ invite: "Mời" driveCapacityPerLocalAccount: "Dung lượng ổ đĩa tối đa cho mỗi người dùng" driveCapacityPerRemoteAccount: "Dung lượng ổ đĩa tối đa cho mỗi người dùng từ xa" inMb: "Tính bằng MB" -iconUrl: "URL Icon" bannerUrl: "URL Ảnh bìa" backgroundImageUrl: "URL Ảnh nền" basicInfo: "Thông tin cơ bản" @@ -992,6 +991,8 @@ letsLookAtTimeline: "Thử xem Timeline" horizontal: "Thanh bên" youFollowing: "Đang theo dõi" icon: "Ảnh đại diện" +replies: "Trả lời" +renotes: "Đăng lại" _achievements: earnedAt: "Ngày thu nhận" _types: diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index f793abb03..28ac48690 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -356,7 +356,6 @@ invite: "邀请" driveCapacityPerLocalAccount: "每个用户的网盘容量" driveCapacityPerRemoteAccount: "每个远程用户的网盘容量" inMb: "以兆字节(MegaByte)为单位" -iconUrl: "图标 URL" bannerUrl: "横幅 URL" backgroundImageUrl: "背景图 URL" basicInfo: "基本信息" @@ -1108,6 +1107,8 @@ currentAnnouncements: "现在的公告" pastAnnouncements: "过去的公告" youHaveUnreadAnnouncements: "您有未读的公告" useSecurityKey: "请根据浏览器或设备的提示,使用安全密钥或通行密钥。" +replies: "回复" +renotes: "转发" _announcement: forExistingUsers: "仅限现有用户" forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。" @@ -1134,6 +1135,11 @@ _initialAccountSetting: laterAreYouSure: "要稍后再进行初始设定吗?" _serverRules: description: "在新用户注册前显示服务器的简单规则。推荐显示服务条款的主要内容。" +_serverSettings: + iconUrl: "图标 URL" + appIconUsageExample: "例如:PWA和新增至手机主屏幕的书签。" + appIconStyleRecommendation: "因为可能会被裁切为圆形或者圆角图形,所以建议将背景用单色填充。" + appIconResolutionMustBe: "分辨率必须符合{resolution}。" _accountMigration: moveFrom: "从别的账号迁移到此账户" moveFromSub: "为另一个账户建立别名" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 4113cc0c8..6d3881a0f 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -15,7 +15,7 @@ gotIt: "知道了" cancel: "取消" noThankYou: "現在不要" enterUsername: "輸入使用者名稱" -renotedBy: "{user} 轉發了" +renotedBy: "{user} 轉傳了" noNotes: "無貼文" noNotifications: "沒有通知" instance: "伺服器" @@ -106,10 +106,10 @@ unfollow: "取消追隨" followRequestPending: "追隨許可待批准" enterEmoji: "輸入表情符號" renote: "轉發" -unrenote: "取消轉發" -renoted: "轉發成功" -cantRenote: "無法轉發此貼文。" -cantReRenote: "無法轉發之前已經轉發過的內容。" +unrenote: "取消轉傳" +renoted: "轉傳成功" +cantRenote: "無法轉傳此貼文。" +cantReRenote: "無法轉傳之前已經轉傳過的內容。" quote: "引用" inChannelRenote: "在頻道內轉發" inChannelQuote: "在頻道內引用" @@ -356,7 +356,6 @@ invite: "邀請" driveCapacityPerLocalAccount: "每個本地使用者的雲端硬碟容量" driveCapacityPerRemoteAccount: "每個非本地用戶的雲端空間大小" inMb: "以Mbps為單位" -iconUrl: "圖標 URL(例如 favicon)" bannerUrl: "橫幅圖片URL" backgroundImageUrl: "背景圖片的來源網址 " basicInfo: "基本資訊" @@ -1108,6 +1107,10 @@ currentAnnouncements: "最新公告" pastAnnouncements: "歷史公告" youHaveUnreadAnnouncements: "有未讀的公告。" useSecurityKey: "請按照瀏覽器或設備上的說明使用安全金鑰或 Passkey。" +replies: "回覆" +renotes: "轉發" +loadReplies: "閱覽回覆" +loadConversation: "閱覽對話" _announcement: forExistingUsers: "僅限既有的使用者" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" @@ -1134,6 +1137,13 @@ _initialAccountSetting: laterAreYouSure: "稍後再重新進行初始設定嗎?" _serverRules: description: "設定在註冊頁面顯示的伺服器簡要規則。建議是服務條款的摘要。" +_serverSettings: + iconUrl: "圖示的 URL" + appIconDescription: "指定顯示 {host} 為應用程式時的圖示。" + appIconUsageExample: "例如:漸進式網路應用程式(PWA)、於手機桌面新增書籤" + appIconStyleRecommendation: "因為可能會裁剪成圓形或圓角,所以建議用單色填滿邊框及背景。" + appIconResolutionMustBe: "解析度必須為 {resolution}。" + manifestJsonOverride: "覆寫 manifest.json" _accountMigration: moveFrom: "從其他帳戶遷移到這個帳戶" moveFromSub: "為另一個帳戶建立別名" From 60f3cc6f0710f0d1c123e59c2bda5cbdd1ae3f45 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 16:54:25 +0900 Subject: [PATCH 025/121] update aiscript to 0.16.0 --- CHANGELOG.md | 1 + packages/frontend/package.json | 2 +- packages/frontend/src/pages/flash/flash-edit.vue | 10 +++++----- pnpm-lock.yaml | 16 +++++----------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dacaee692..bbcbdf58d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - `$[rainbow ]`記法が、動きのあるMFMが無効になっていても使用できるようになりました - Playの操作を行うAPI TokenをAPIコンソールから発行できるように - リアクションの表示サイズをより大きくできるように +- AiScriptを0.16.0に更新 - タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 - 「Moderation note」、「Add moderation note」をローカライズできるように - 新しい実績を追加 diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 98117521f..d38611c2f 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -21,7 +21,7 @@ "@rollup/plugin-json": "6.0.0", "@rollup/plugin-replace": "5.0.2", "@rollup/pluginutils": "5.0.4", - "@syuilo/aiscript": "0.15.0", + "@syuilo/aiscript": "0.16.0", "@tabler/icons-webfont": "2.32.0", "@vitejs/plugin-vue": "4.3.4", "@vue-macros/reactivity-transform": "0.3.23", diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 414fe4836..7ffb9e419 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -44,7 +44,7 @@ import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import { useRouter } from '@/router'; -const PRESET_DEFAULT = `/// @ 0.15.0 +const PRESET_DEFAULT = `/// @ 0.16.0 var name = "" @@ -62,7 +62,7 @@ Ui:render([ ]) `; -const PRESET_OMIKUJI = `/// @ 0.15.0 +const PRESET_OMIKUJI = `/// @ 0.16.0 // ユーザーごとに日替わりのおみくじのプリセット // 選択肢 @@ -105,7 +105,7 @@ Ui:render([ ]) `; -const PRESET_SHUFFLE = `/// @ 0.15.0 +const PRESET_SHUFFLE = `/// @ 0.16.0 // 巻き戻し可能な文字シャッフルのプリセット let string = "ペペロンチーノ" @@ -184,7 +184,7 @@ var cursor = 0 do() `; -const PRESET_QUIZ = `/// @ 0.15.0 +const PRESET_QUIZ = `/// @ 0.16.0 let title = '地理クイズ' let qas = [{ @@ -297,7 +297,7 @@ qaEls.push(Ui:C:container({ Ui:render(qaEls) `; -const PRESET_TIMELINE = `/// @ 0.15.0 +const PRESET_TIMELINE = `/// @ 0.16.0 // APIリクエストを行いローカルタイムラインを表示するプリセット @fetch() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6976a67bf..61fcb6aef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -659,8 +659,8 @@ importers: specifier: 5.0.4 version: 5.0.4(rollup@3.29.1) '@syuilo/aiscript': - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.0 + version: 0.16.0 '@tabler/icons-webfont': specifier: 2.32.0 version: 2.32.0 @@ -7162,13 +7162,12 @@ packages: dev: false optional: true - /@syuilo/aiscript@0.15.0: - resolution: {integrity: sha512-vauMbqacuHufE4W7bAm5BDWlci3mWg1ZaerPNHKBMrRXDjEfvBkBGZNXKSWvi+zmhiYAbWyozPIlV6byttYpCw==} + /@syuilo/aiscript@0.16.0: + resolution: {integrity: sha512-CXvoWOq6kmOSUQtKv0IEf7Ebfkk5PO1LxAgLqgRRPgssPvDvINCXu/gFNXKdapkFMkmX+Gj8qjemKR1vnUS4ZA==} dependencies: - autobind-decorator: 2.4.0 seedrandom: 3.0.5 stringz: 2.1.0 - uuid: 9.0.0 + uuid: 9.0.1 dev: false /@szmarczak/http-timer@4.0.6: @@ -8914,11 +8913,6 @@ packages: engines: {node: '>=8.0.0'} dev: false - /autobind-decorator@2.4.0: - resolution: {integrity: sha512-OGYhWUO72V6DafbF8PM8rm3EPbfuyMZcJhtm5/n26IDwO18pohE4eNazLoCGhPiXOCD0gEGmrbU3849QvM8bbw==} - engines: {node: '>=8.10', npm: '>=6.4.1'} - dev: false - /autosize@6.0.1: resolution: {integrity: sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==} dev: false From 3d61ca818b80eb79476fbf449d9200d7a1bb12f9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 17:33:08 +0900 Subject: [PATCH 026/121] 2023.9.0-beta.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 431edce39..9fb67ea58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.6", + "version": "2023.9.0-beta.7", "codename": "nasubi", "repository": { "type": "git", From a2d58d9f48ff392fe61c99b90d7e5a0b3709f03d Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 17:41:24 +0900 Subject: [PATCH 027/121] =?UTF-8?q?fix(frontend):=20=E8=BF=94=E4=BF=A1?= =?UTF-8?q?=E3=81=A7=E3=81=AF=E3=81=AA=E3=81=84=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=81=AE=E8=A9=B3=E7=B4=B0=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=8C?= =?UTF-8?q?=E8=A6=8B=E3=82=8C=E3=81=AA=E3=81=84(2023.9.0-beta.6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkNoteDetailed.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 086667127..1e3374a48 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-hotkey="keymap" :class="$style.root" > -
+
{{ i18n.ts.loadConversation }}
From 1b463d9c315885d35213eebf51b9c7453e135c06 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 17 Sep 2023 17:42:12 +0900 Subject: [PATCH 028/121] 2023.9.0-beta.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9fb67ea58..ffb4596e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.7", + "version": "2023.9.0-beta.8", "codename": "nasubi", "repository": { "type": "git", From 350ebbadba3b1ce2f1f5b082fc62160ce8a72cad Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 18 Sep 2023 11:42:26 +0900 Subject: [PATCH 029/121] fix(frontend): prevent layout-shift of reactions viewer Fix #11842 --- packages/frontend/src/components/MkReactionsViewer.reaction.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 7da579012..4349b7999 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -158,7 +158,7 @@ useTooltip(buttonEl, async (showing) => { &.reacted, &.reacted:hover { background: var(--accentedBg); color: var(--accent); - border: 1px solid var(--accent); + box-shadow: 0 0 0px 1px var(--accent) inset; > .count { color: var(--accent); From 25ae4bca9cfc06828a83d3891d0c215c70032201 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 18 Sep 2023 13:25:49 +0900 Subject: [PATCH 030/121] tab --- .../frontend/src/components/MkReactionsViewer.reaction.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 4349b7999..7e2fbb161 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -156,9 +156,9 @@ useTooltip(buttonEl, async (showing) => { } &.reacted, &.reacted:hover { - background: var(--accentedBg); - color: var(--accent); - box-shadow: 0 0 0px 1px var(--accent) inset; + background: var(--accentedBg); + color: var(--accent); + box-shadow: 0 0 0px 1px var(--accent) inset; > .count { color: var(--accent); From aa80cfdb81061301c45ca22e045bcbcd8116f93b Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 18 Sep 2023 13:26:43 +0900 Subject: [PATCH 031/121] Update .eslintrc.js --- packages/shared/.eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index a02de4945..89d115168 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -71,7 +71,7 @@ module.exports = { '@typescript-eslint/no-inferrable-types': ['warn'], '@typescript-eslint/no-empty-function': ['off'], '@typescript-eslint/no-non-null-assertion': ['warn'], - '@typescript-eslint/explicit-function-return-type': ['warn'], + '@typescript-eslint/explicit-function-return-type': ['off'], '@typescript-eslint/no-misused-promises': ['error', { 'checksVoidReturn': false, }], From 44985ae8588a96968c1e6856083216b3d75b4c18 Mon Sep 17 00:00:00 2001 From: FineArchs <133759614+FineArchs@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:23:33 +0900 Subject: [PATCH 032/121] =?UTF-8?q?feat:=20Mk:api=E3=81=8C=E5=A4=B1?= =?UTF-8?q?=E6=95=97=E6=99=82=E3=82=A8=E3=83=A9=E3=83=BC=E5=9E=8B=E3=81=AE?= =?UTF-8?q?=E5=80=A4=E3=82=92=E8=BF=94=E3=81=99=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=20(#2)=20(#11843)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mk:apiが失敗時エラー型の値を返すように (#2) * Update CHANGELOG.md --- CHANGELOG.md | 1 + packages/frontend/src/scripts/aiscript/api.ts | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbcbdf58d..56c255214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ - Playの操作を行うAPI TokenをAPIコンソールから発行できるように - リアクションの表示サイズをより大きくできるように - AiScriptを0.16.0に更新 +- Mk:apiが失敗した時にエラー型の値(AiScript 0.16.0で追加)を返すように - タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 - 「Moderation note」、「Add moderation note」をローカライズできるように - 新しい実績を追加 diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts index c3acb6d14..dcb4a9105 100644 --- a/packages/frontend/src/scripts/aiscript/api.ts +++ b/packages/frontend/src/scripts/aiscript/api.ts @@ -39,8 +39,11 @@ export function createAiScriptEnv(opts) { // バグがあればundefinedもあり得るため念のため if (typeof token.value !== 'string') throw new Error('invalid token'); } - const res = await os.api(ep.value, utils.valToJs(param), token ? token.value : (opts.token ?? null)); - return utils.jsToVal(res); + return os.api(ep.value, utils.valToJs(param), token ? token.value : (opts.token ?? null)).then(res => { + return utils.jsToVal(res); + }, err => { + return values.ERROR('request_failed', utils.jsToVal(err)); + }); }), 'Mk:save': values.FN_NATIVE(([key, value]) => { utils.assertString(key); From 5c48878dc558ff873e1633b9ad7abd3bee6b2fcb Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 19 Sep 2023 09:36:07 +0900 Subject: [PATCH 033/121] =?UTF-8?q?enhance(dev):=20=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E5=91=BC=E3=81=B3=E5=87=BA=E3=81=97=E3=81=AE=E5=89=8D=E3=81=AB?= =?UTF-8?q?=E3=82=B9=E3=83=9A=E3=83=BC=E3=82=B9=E3=82=92=E5=85=A5=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E3=81=93=E3=81=A8=E3=82=92=E8=A8=B1=E5=8F=AF=E3=81=97?= =?UTF-8?q?=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/timeline.vue | 2 +- packages/shared/.eslintrc.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index f0ef2d10a..307d77882 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -58,7 +58,7 @@ let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); -watch ($$(src), () => queue = 0); +watch($$(src), () => queue = 0); function queueUpdated(q: number): void { queue = q; diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 89d115168..cb6bd56f9 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -64,6 +64,7 @@ module.exports = { 'object-curly-spacing': ['error', 'always'], 'space-infix-ops': ['error'], 'space-before-blocks': ['error', 'always'], + 'func-call-spacing': ['error', 'never'], '@typescript-eslint/no-explicit-any': ['warn'], '@typescript-eslint/no-unused-vars': ['warn'], '@typescript-eslint/no-unnecessary-condition': ['warn'], From bec338aa006719d6ef004b5b351eebdf47a62b50 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 19 Sep 2023 09:44:05 +0900 Subject: [PATCH 034/121] Update .eslintrc.js --- packages/shared/.eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index cb6bd56f9..1ecad7ab7 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -64,7 +64,7 @@ module.exports = { 'object-curly-spacing': ['error', 'always'], 'space-infix-ops': ['error'], 'space-before-blocks': ['error', 'always'], - 'func-call-spacing': ['error', 'never'], + '@typescript-eslint/func-call-spacing': ['error', 'never'], '@typescript-eslint/no-explicit-any': ['warn'], '@typescript-eslint/no-unused-vars': ['warn'], '@typescript-eslint/no-unnecessary-condition': ['warn'], From 299c9c41188df46b64b04ba53cbded7a89998b6c Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 19 Sep 2023 10:58:42 +0900 Subject: [PATCH 035/121] =?UTF-8?q?feat(frontend):=20=E4=BB=BB=E6=84=8F?= =?UTF-8?q?=E3=81=AE=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=83=AA=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AB=E3=83=94=E3=83=B3?= =?UTF-8?q?=E7=95=99=E3=82=81=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../frontend/src/pages/settings/general.vue | 23 +++++++++++++++++++ packages/frontend/src/pages/timeline.vue | 19 +++++++++++---- packages/frontend/src/store.ts | 10 +++++--- 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56c255214..683c0a85a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ - ローカリゼーションの更新 ### Client +- 任意のユーザーリストをタイムラインページにピン留めできるように + - 設定->クライアント設定->全般 から設定可能です - ノート詳細ページを改修 - 読み込み時のパフォーマンスが向上しました - リノート一覧、リアクション一覧がタブとして追加されました diff --git a/locales/index.d.ts b/locales/index.d.ts index ac714258e..bf7aff75d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1114,6 +1114,7 @@ export interface Locale { "renotes": string; "loadReplies": string; "loadConversation": string; + "pinnedList": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index d97b09f63..2f88128c9 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1111,6 +1111,7 @@ replies: "返信" renotes: "リノート" loadReplies: "返信を見る" loadConversation: "会話を見る" +pinnedList: "ピン留めされたリスト" _announcement: forExistingUsers: "既存ユーザーのみ" diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b486e6d80..ff498e416 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -30,6 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.showFixedPostForm }} {{ i18n.ts.showFixedPostFormInChannel }} {{ i18n.ts.flagShowTimelineReplies }} + + + + {{ i18n.ts.add }} + {{ i18n.ts.remove }} +
@@ -307,6 +313,23 @@ function removeEmojiIndex(lang: string) { os.promiseDialog(main()); } +async function setPinnedList() { + const lists = await os.api('users/lists/list'); + const { canceled, result: list } = await os.select({ + title: i18n.ts.selectList, + items: lists.map(x => ({ + value: x, text: x.name, + })), + }); + if (canceled) return; + + defaultStore.set('pinnedUserLists', [list]); +} + +function removePinnedList() { + defaultStore.set('pinnedUserLists', []); +} + let smashCount = 0; let smashTimer: number | null = null; function testNotification(): void { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 307d77882..c0b1f03ff 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -16,7 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -102,10 +103,15 @@ async function chooseChannel(ev: MouseEvent): Promise { os.popupMenu(items, ev.currentTarget ?? ev.target); } -function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global'): void { +function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | `list:${string}`): void { + let userList = null; + if (newSrc.startsWith('userList:')) { + const id = newSrc.substring('userList:'.length); + userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id); + } defaultStore.set('tl', { - ...defaultStore.state.tl, src: newSrc, + userList, }); srcWhenNotSignin = newSrc; } @@ -125,7 +131,12 @@ function focus(): void { const headerActions = $computed(() => []); -const headerTabs = $computed(() => [{ +const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({ + key: 'list:' + l.id, + title: l.name, + icon: 'ti ti-star', + iconOnly: true, +}))), { key: 'home', title: i18n.ts._timelines.home, icon: 'ti ti-home', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 787a584f8..4b8001bfb 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -4,7 +4,7 @@ */ import { markRaw, ref } from 'vue'; -import misskey from 'misskey-js'; +import * as Misskey from 'misskey-js'; import { Storage } from './pizzax'; interface PostFormAction { @@ -163,10 +163,14 @@ export const defaultStore = markRaw(new Storage('base', { tl: { where: 'deviceAccount', default: { - src: 'home' as 'home' | 'local' | 'social' | 'global', - arg: null, + src: 'home' as 'home' | 'local' | 'social' | 'global' | `list:${string}`, + userList: null as Misskey.entities.UserList | null, }, }, + pinnedUserLists: { + where: 'deviceAccount', + default: [] as Misskey.entities.UserList[], + }, overridedDeviceKind: { where: 'device', From b0f6c44f36f3173f5d232d1adb02fc09dfdcc003 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 19 Sep 2023 16:37:43 +0900 Subject: [PATCH 036/121] refactor(frontend): use ESM --- packages/frontend/@types/theme.d.ts | 2 +- packages/frontend/package.json | 1 + packages/frontend/src/_boot_.ts | 4 +- packages/frontend/src/account.ts | 16 ++++---- packages/frontend/src/boot/common.ts | 40 +++++++++---------- packages/frontend/src/boot/main-boot.ts | 30 +++++++------- packages/frontend/src/boot/sub-boot.ts | 2 +- packages/frontend/src/cache.ts | 4 +- .../frontend/src/components/MkAbuseReport.vue | 6 +-- .../src/components/MkAbuseReportWindow.vue | 4 +- .../src/components/MkAccountMoved.vue | 6 +-- .../components/MkAchievements.stories.impl.ts | 2 +- .../src/components/MkAchievements.vue | 6 +-- .../src/components/MkAnnouncementDialog.vue | 6 +-- packages/frontend/src/components/MkAsUi.vue | 4 +- .../components/MkAutocomplete.stories.impl.ts | 2 +- .../src/components/MkAutocomplete.vue | 20 +++++----- .../frontend/src/components/MkAvatars.vue | 2 +- .../frontend/src/components/MkCaptcha.vue | 4 +- .../src/components/MkChannelFollowButton.vue | 4 +- .../frontend/src/components/MkChannelList.vue | 4 +- .../src/components/MkChannelPreview.vue | 2 +- packages/frontend/src/components/MkChart.vue | 16 ++++---- .../frontend/src/components/MkClickerGame.vue | 10 ++--- .../frontend/src/components/MkClipPreview.vue | 2 +- .../frontend/src/components/MkColorInput.vue | 2 +- .../frontend/src/components/MkContainer.vue | 4 +- .../frontend/src/components/MkContextMenu.vue | 6 +-- .../src/components/MkCropperDialog.vue | 12 +++--- .../frontend/src/components/MkCwButton.vue | 4 +- .../src/components/MkDateSeparatedList.vue | 6 +-- packages/frontend/src/components/MkDialog.vue | 2 +- .../frontend/src/components/MkDonation.vue | 10 ++--- .../frontend/src/components/MkDrive.file.vue | 10 ++--- .../src/components/MkDrive.folder.vue | 10 ++--- .../src/components/MkDrive.navFolder.vue | 4 +- packages/frontend/src/components/MkDrive.vue | 12 +++--- .../src/components/MkDriveSelectDialog.vue | 4 +- .../frontend/src/components/MkDriveWindow.vue | 2 +- .../src/components/MkEmojiPicker.section.vue | 2 +- .../frontend/src/components/MkEmojiPicker.vue | 16 ++++---- .../src/components/MkEmojiPickerDialog.vue | 2 +- .../src/components/MkFeaturedPhotos.vue | 2 +- .../components/MkFileCaptionEditWindow.vue | 2 +- .../src/components/MkFileListForAdmin.vue | 6 +-- .../src/components/MkFlashPreview.vue | 2 +- .../src/components/MkFoldableSection.vue | 4 +- packages/frontend/src/components/MkFolder.vue | 2 +- .../src/components/MkFollowButton.vue | 10 ++--- .../src/components/MkForgotPassword.vue | 6 +-- .../frontend/src/components/MkFormDialog.vue | 2 +- .../src/components/MkGalleryPostPreview.vue | 2 +- packages/frontend/src/components/MkGoogle.vue | 2 +- .../frontend/src/components/MkHeatmap.vue | 10 ++--- .../src/components/MkImgWithBlurhash.vue | 6 +-- packages/frontend/src/components/MkInput.vue | 4 +- .../src/components/MkInstanceCardMini.vue | 4 +- .../src/components/MkInstanceStats.vue | 8 ++-- .../src/components/MkInstanceTicker.vue | 6 +-- .../frontend/src/components/MkInviteCode.vue | 6 +-- .../frontend/src/components/MkKeyValue.vue | 6 +-- .../frontend/src/components/MkLaunchPad.vue | 4 +- packages/frontend/src/components/MkLink.vue | 6 +-- .../frontend/src/components/MkMediaBanner.vue | 4 +- .../frontend/src/components/MkMediaImage.vue | 12 +++--- .../frontend/src/components/MkMediaList.vue | 6 +-- .../frontend/src/components/MkMediaVideo.vue | 6 +-- .../frontend/src/components/MkMention.vue | 6 +-- packages/frontend/src/components/MkMenu.vue | 8 ++-- .../frontend/src/components/MkMiniChart.vue | 2 +- packages/frontend/src/components/MkModal.vue | 8 ++-- packages/frontend/src/components/MkNote.vue | 36 ++++++++--------- .../src/components/MkNoteDetailed.vue | 32 +++++++-------- .../frontend/src/components/MkNoteHeader.vue | 6 +-- .../frontend/src/components/MkNotePreview.vue | 2 +- .../frontend/src/components/MkNoteSimple.vue | 2 +- .../frontend/src/components/MkNoteSub.vue | 8 ++-- packages/frontend/src/components/MkNotes.vue | 4 +- .../src/components/MkNotification.vue | 16 ++++---- .../MkNotificationSettingWindow.vue | 2 +- .../src/components/MkNotifications.vue | 8 ++-- packages/frontend/src/components/MkNumber.vue | 2 +- .../frontend/src/components/MkNumberDiff.vue | 2 +- .../src/components/MkObjectView.value.vue | 2 +- packages/frontend/src/components/MkOmit.vue | 2 +- .../frontend/src/components/MkPagePreview.vue | 2 +- .../frontend/src/components/MkPageWindow.vue | 20 +++++----- .../frontend/src/components/MkPagination.vue | 12 +++--- .../src/components/MkPlusOneEffect.vue | 2 +- packages/frontend/src/components/MkPoll.vue | 10 ++--- .../frontend/src/components/MkPollEditor.vue | 6 +-- .../frontend/src/components/MkPostForm.vue | 30 +++++++------- .../src/components/MkPostFormAttaches.vue | 4 +- .../MkPushNotificationAllowButton.vue | 8 ++-- packages/frontend/src/components/MkRange.vue | 2 +- .../src/components/MkReactionEffect.vue | 2 +- .../components/MkReactionsViewer.details.vue | 2 +- .../components/MkReactionsViewer.reaction.vue | 12 +++--- .../src/components/MkReactionsViewer.vue | 2 +- .../src/components/MkRemoteCaution.vue | 2 +- .../src/components/MkRetentionHeatmap.vue | 10 ++--- .../src/components/MkRetentionLineChart.vue | 12 +++--- .../src/components/MkRippleEffect.vue | 2 +- .../frontend/src/components/MkRolePreview.vue | 2 +- packages/frontend/src/components/MkSelect.vue | 6 +-- packages/frontend/src/components/MkSignin.vue | 10 ++--- .../src/components/MkSigninDialog.vue | 2 +- .../src/components/MkSignupDialog.form.vue | 10 ++--- .../MkSignupDialog.rules.stories.impl.ts | 4 +- .../src/components/MkSignupDialog.rules.vue | 6 +-- .../src/components/MkSignupDialog.vue | 4 +- .../src/components/MkSubNoteContent.vue | 6 +-- .../src/components/MkSwitch.button.vue | 2 +- .../frontend/src/components/MkTextarea.vue | 2 +- .../frontend/src/components/MkTimeline.vue | 8 ++-- packages/frontend/src/components/MkToast.vue | 4 +- .../src/components/MkTokenGenerateWindow.vue | 2 +- .../frontend/src/components/MkTooltip.vue | 6 +-- .../frontend/src/components/MkUpdated.vue | 6 +-- .../frontend/src/components/MkUrlPreview.vue | 12 +++--- .../src/components/MkUrlPreviewPopup.vue | 4 +- .../MkUserAnnouncementEditDialog.vue | 4 +- .../src/components/MkUserCardMini.vue | 4 +- .../frontend/src/components/MkUserInfo.vue | 10 ++--- .../frontend/src/components/MkUserList.vue | 4 +- .../src/components/MkUserOnlineIndicator.vue | 2 +- .../frontend/src/components/MkUserPopup.vue | 16 ++++---- .../src/components/MkUserSelectDialog.vue | 10 ++--- .../components/MkUserSetupDialog.Follow.vue | 8 ++-- .../components/MkUserSetupDialog.Privacy.vue | 8 ++-- .../components/MkUserSetupDialog.Profile.vue | 10 ++--- .../src/components/MkUserSetupDialog.User.vue | 6 +-- .../src/components/MkUserSetupDialog.vue | 10 ++--- .../src/components/MkVisibilityPicker.vue | 2 +- .../MkVisitorDashboard.ActiveUsersChart.vue | 10 ++--- .../src/components/MkVisitorDashboard.vue | 10 ++--- .../frontend/src/components/MkWidgets.vue | 4 +- packages/frontend/src/components/MkWindow.vue | 8 ++-- .../src/components/MkYouTubePlayer.vue | 4 +- .../frontend/src/components/form/suspense.vue | 4 +- .../src/components/global/MkA.stories.impl.ts | 2 +- .../frontend/src/components/global/MkA.vue | 12 +++--- .../frontend/src/components/global/MkAcct.vue | 4 +- .../components/global/MkAd.stories.impl.ts | 2 +- .../frontend/src/components/global/MkAd.vue | 12 +++--- .../src/components/global/MkAvatar.vue | 8 ++-- .../src/components/global/MkCustomEmoji.vue | 6 +-- .../src/components/global/MkEmoji.vue | 6 +-- .../src/components/global/MkError.vue | 6 +-- .../global/MkMisskeyFlavoredMarkdown.ts | 4 +- .../components/global/MkPageHeader.tabs.vue | 2 +- .../src/components/global/MkPageHeader.vue | 6 +-- .../src/components/global/MkSpacer.vue | 2 +- .../components/global/MkTime.stories.impl.ts | 4 +- .../frontend/src/components/global/MkTime.vue | 4 +- .../frontend/src/components/global/MkUrl.vue | 8 ++-- .../src/components/global/RouterView.vue | 2 +- .../src/components/page/page.note.vue | 2 +- .../src/components/page/page.text.vue | 4 +- packages/frontend/src/config.ts | 2 +- packages/frontend/src/custom-emojis.ts | 6 +-- .../frontend/src/directives/click-anime.ts | 2 +- .../frontend/src/directives/follow-append.ts | 2 +- packages/frontend/src/directives/ripple.ts | 2 +- packages/frontend/src/directives/tooltip.ts | 4 +- .../frontend/src/directives/user-preview.ts | 2 +- packages/frontend/src/filters/date.ts | 2 +- packages/frontend/src/filters/number.ts | 2 +- packages/frontend/src/filters/user.ts | 2 +- packages/frontend/src/i18n.ts | 6 +-- packages/frontend/src/instance.ts | 6 +-- packages/frontend/src/navbar.ts | 16 ++++---- packages/frontend/src/nirax.ts | 2 +- packages/frontend/src/os.ts | 10 ++--- packages/frontend/src/pages/_error_.vue | 16 ++++---- packages/frontend/src/pages/about-misskey.vue | 16 ++++---- packages/frontend/src/pages/about.emojis.vue | 6 +-- .../frontend/src/pages/about.federation.vue | 2 +- packages/frontend/src/pages/about.vue | 14 +++---- packages/frontend/src/pages/achievements.vue | 8 ++-- packages/frontend/src/pages/admin-file.vue | 10 ++--- packages/frontend/src/pages/admin-user.vue | 12 +++--- .../src/pages/admin/RolesEditorFormula.vue | 4 +- .../frontend/src/pages/admin/_header_.vue | 6 +-- packages/frontend/src/pages/admin/abuses.vue | 4 +- packages/frontend/src/pages/admin/ads.vue | 6 +-- .../src/pages/admin/announcements.vue | 6 +-- .../src/pages/admin/bot-protection.vue | 6 +-- .../frontend/src/pages/admin/branding.vue | 10 ++--- .../frontend/src/pages/admin/database.vue | 10 ++--- .../src/pages/admin/email-settings.vue | 8 ++-- .../frontend/src/pages/admin/federation.vue | 4 +- packages/frontend/src/pages/admin/files.vue | 6 +-- packages/frontend/src/pages/admin/index.vue | 12 +++--- .../src/pages/admin/instance-block.vue | 8 ++-- packages/frontend/src/pages/admin/invites.vue | 6 +-- .../frontend/src/pages/admin/moderation.vue | 8 ++-- .../src/pages/admin/object-storage.vue | 8 ++-- .../src/pages/admin/other-settings.vue | 8 ++-- .../src/pages/admin/overview.active-users.vue | 10 ++--- .../src/pages/admin/overview.ap-requests.vue | 12 +++--- .../src/pages/admin/overview.federation.vue | 8 ++-- .../src/pages/admin/overview.instances.vue | 6 +-- .../src/pages/admin/overview.moderators.vue | 4 +- .../frontend/src/pages/admin/overview.pie.vue | 4 +- .../src/pages/admin/overview.queue.chart.vue | 10 ++--- .../src/pages/admin/overview.queue.vue | 4 +- .../src/pages/admin/overview.stats.vue | 8 ++-- .../src/pages/admin/overview.users.vue | 6 +-- .../frontend/src/pages/admin/overview.vue | 8 ++-- .../src/pages/admin/proxy-account.vue | 8 ++-- .../src/pages/admin/queue.chart.chart.vue | 10 ++--- .../frontend/src/pages/admin/queue.chart.vue | 8 ++-- packages/frontend/src/pages/admin/queue.vue | 8 ++-- packages/frontend/src/pages/admin/relays.vue | 6 +-- .../frontend/src/pages/admin/roles.edit.vue | 8 ++-- .../frontend/src/pages/admin/roles.editor.vue | 6 +-- .../frontend/src/pages/admin/roles.role.vue | 10 ++--- packages/frontend/src/pages/admin/roles.vue | 10 ++--- .../frontend/src/pages/admin/security.vue | 8 ++-- .../frontend/src/pages/admin/server-rules.vue | 8 ++-- .../frontend/src/pages/admin/settings.vue | 8 ++-- packages/frontend/src/pages/admin/users.vue | 10 ++--- packages/frontend/src/pages/ads.vue | 8 ++-- packages/frontend/src/pages/announcements.vue | 8 ++-- .../frontend/src/pages/antenna-timeline.vue | 10 ++--- packages/frontend/src/pages/api-console.vue | 4 +- packages/frontend/src/pages/auth.form.vue | 4 +- packages/frontend/src/pages/auth.vue | 8 ++-- .../frontend/src/pages/channel-editor.vue | 10 ++--- packages/frontend/src/pages/channel.vue | 16 ++++---- packages/frontend/src/pages/channels.vue | 6 +-- packages/frontend/src/pages/clicker.vue | 2 +- packages/frontend/src/pages/clip.vue | 10 ++--- .../src/pages/custom-emojis-manager.vue | 8 ++-- packages/frontend/src/pages/drive.vue | 4 +- .../frontend/src/pages/emoji-edit-dialog.vue | 8 ++-- packages/frontend/src/pages/emojis.emoji.vue | 6 +-- .../frontend/src/pages/explore.featured.vue | 2 +- packages/frontend/src/pages/explore.roles.vue | 2 +- packages/frontend/src/pages/explore.users.vue | 4 +- packages/frontend/src/pages/explore.vue | 4 +- packages/frontend/src/pages/favorites.vue | 6 +-- .../frontend/src/pages/flash/flash-edit.vue | 8 ++-- .../frontend/src/pages/flash/flash-index.vue | 6 +-- packages/frontend/src/pages/flash/flash.vue | 16 ++++---- .../frontend/src/pages/follow-requests.vue | 10 ++--- packages/frontend/src/pages/follow.vue | 6 +-- packages/frontend/src/pages/gallery/edit.vue | 10 ++--- packages/frontend/src/pages/gallery/index.vue | 6 +-- packages/frontend/src/pages/gallery/post.vue | 14 +++---- packages/frontend/src/pages/instance-info.vue | 14 +++---- packages/frontend/src/pages/invite.vue | 10 ++--- packages/frontend/src/pages/list.vue | 10 ++--- packages/frontend/src/pages/miauth.vue | 8 ++-- .../frontend/src/pages/my-antennas/create.vue | 6 +-- .../frontend/src/pages/my-antennas/edit.vue | 8 ++-- .../frontend/src/pages/my-antennas/editor.vue | 4 +- .../frontend/src/pages/my-antennas/index.vue | 6 +-- .../frontend/src/pages/my-clips/index.vue | 6 +-- .../frontend/src/pages/my-lists/index.vue | 10 ++--- packages/frontend/src/pages/my-lists/list.vue | 14 +++---- packages/frontend/src/pages/not-found.vue | 8 ++-- packages/frontend/src/pages/note.vue | 10 ++--- packages/frontend/src/pages/notifications.vue | 6 +-- packages/frontend/src/pages/oauth.vue | 6 +-- .../page-editor/els/page-editor.el.image.vue | 4 +- .../page-editor/els/page-editor.el.note.vue | 4 +- .../els/page-editor.el.section.vue | 6 +-- .../page-editor/els/page-editor.el.text.vue | 2 +- .../page-editor/page-editor.container.vue | 2 +- .../src/pages/page-editor/page-editor.vue | 14 +++---- packages/frontend/src/pages/page.vue | 14 +++---- packages/frontend/src/pages/pages.vue | 6 +-- packages/frontend/src/pages/registry.keys.vue | 6 +-- .../frontend/src/pages/registry.value.vue | 6 +-- packages/frontend/src/pages/registry.vue | 6 +-- .../frontend/src/pages/reset-password.vue | 8 ++-- packages/frontend/src/pages/role.vue | 10 ++--- packages/frontend/src/pages/scratchpad.vue | 16 ++++---- packages/frontend/src/pages/search.note.vue | 10 ++--- packages/frontend/src/pages/search.user.vue | 10 ++--- packages/frontend/src/pages/search.vue | 10 ++--- .../src/pages/settings/2fa.qrdialog.vue | 6 +-- packages/frontend/src/pages/settings/2fa.vue | 6 +-- .../frontend/src/pages/settings/accounts.vue | 8 ++-- packages/frontend/src/pages/settings/api.vue | 6 +-- packages/frontend/src/pages/settings/apps.vue | 8 ++-- .../src/pages/settings/custom-css.vue | 10 ++--- packages/frontend/src/pages/settings/deck.vue | 6 +-- .../src/pages/settings/drive-cleaner.vue | 12 +++--- .../frontend/src/pages/settings/drive.vue | 12 +++--- .../frontend/src/pages/settings/email.vue | 10 ++--- .../frontend/src/pages/settings/general.vue | 16 ++++---- .../src/pages/settings/import-export.vue | 10 ++--- .../frontend/src/pages/settings/index.vue | 18 ++++----- .../src/pages/settings/instance-mute.vue | 8 ++-- .../frontend/src/pages/settings/migration.vue | 10 ++--- .../src/pages/settings/mute-block.vue | 10 ++--- .../frontend/src/pages/settings/navbar.vue | 12 +++--- .../src/pages/settings/notifications.vue | 8 ++-- .../frontend/src/pages/settings/other.vue | 12 +++--- .../src/pages/settings/plugin.install.vue | 10 ++--- .../frontend/src/pages/settings/plugin.vue | 10 ++--- .../pages/settings/preferences-backups.vue | 18 ++++----- .../frontend/src/pages/settings/privacy.vue | 10 ++--- .../frontend/src/pages/settings/profile.vue | 16 ++++---- .../frontend/src/pages/settings/reaction.vue | 10 ++--- .../frontend/src/pages/settings/roles.vue | 10 ++--- .../frontend/src/pages/settings/security.vue | 6 +-- .../src/pages/settings/sounds.sound.vue | 4 +- .../frontend/src/pages/settings/sounds.vue | 6 +-- .../pages/settings/statusbar.statusbar.vue | 6 +-- .../frontend/src/pages/settings/statusbar.vue | 8 ++-- .../src/pages/settings/theme.install.vue | 8 ++-- .../src/pages/settings/theme.manage.vue | 10 ++--- .../frontend/src/pages/settings/theme.vue | 18 ++++----- .../src/pages/settings/webhook.edit.vue | 8 ++-- .../src/pages/settings/webhook.new.vue | 6 +-- .../frontend/src/pages/settings/webhook.vue | 4 +- .../frontend/src/pages/settings/word-mute.vue | 12 +++--- packages/frontend/src/pages/share.vue | 8 ++-- .../frontend/src/pages/signup-complete.vue | 6 +-- packages/frontend/src/pages/tag.vue | 10 ++--- packages/frontend/src/pages/theme-editor.vue | 16 ++++---- .../frontend/src/pages/timeline.tutorial.vue | 8 ++-- packages/frontend/src/pages/timeline.vue | 16 ++++---- .../frontend/src/pages/user-list-timeline.vue | 10 ++--- packages/frontend/src/pages/user-tag.vue | 4 +- .../frontend/src/pages/user/achievements.vue | 4 +- .../src/pages/user/activity.following.vue | 12 +++--- .../src/pages/user/activity.heatmap.vue | 10 ++--- .../src/pages/user/activity.notes.vue | 12 +++--- .../frontend/src/pages/user/activity.pv.vue | 12 +++--- .../frontend/src/pages/user/followers.vue | 6 +-- .../frontend/src/pages/user/following.vue | 6 +-- packages/frontend/src/pages/user/home.vue | 24 +++++------ .../src/pages/user/index.activity.vue | 4 +- .../frontend/src/pages/user/index.photos.vue | 10 ++--- .../src/pages/user/index.timeline.vue | 2 +- packages/frontend/src/pages/user/index.vue | 10 ++--- .../frontend/src/pages/welcome.entrance.a.vue | 10 ++--- packages/frontend/src/pages/welcome.setup.vue | 8 ++-- .../frontend/src/pages/welcome.timeline.vue | 6 +-- packages/frontend/src/pages/welcome.vue | 6 +-- packages/frontend/src/pizzax.ts | 12 +++--- packages/frontend/src/plugin.ts | 6 +-- packages/frontend/src/router.ts | 2 +- packages/frontend/src/scripts/achievements.ts | 4 +- packages/frontend/src/scripts/aiscript/api.ts | 10 ++--- packages/frontend/src/scripts/api.ts | 4 +- packages/frontend/src/scripts/array.ts | 2 +- packages/frontend/src/scripts/autocomplete.ts | 2 +- packages/frontend/src/scripts/clicker-game.ts | 2 +- packages/frontend/src/scripts/collapsed.ts | 2 +- packages/frontend/src/scripts/confetti.ts | 2 +- packages/frontend/src/scripts/device-kind.ts | 2 +- .../src/scripts/extract-url-from-mfm.ts | 2 +- .../frontend/src/scripts/gen-search-query.ts | 2 +- .../src/scripts/get-account-from-id.ts | 2 +- .../src/scripts/get-drive-file-menu.ts | 10 ++--- .../frontend/src/scripts/get-note-menu.ts | 18 ++++----- .../frontend/src/scripts/get-note-summary.ts | 2 +- .../frontend/src/scripts/get-user-menu.ts | 18 ++++----- packages/frontend/src/scripts/hotkey.ts | 2 +- packages/frontend/src/scripts/init-chart.ts | 2 +- .../frontend/src/scripts/initialize-sw.ts | 2 +- packages/frontend/src/scripts/intl-const.ts | 2 +- .../frontend/src/scripts/isFfVisibleForMe.ts | 2 +- packages/frontend/src/scripts/lookup-user.ts | 4 +- packages/frontend/src/scripts/lookup.ts | 8 ++-- packages/frontend/src/scripts/media-proxy.ts | 6 +-- packages/frontend/src/scripts/please-login.ts | 6 +-- packages/frontend/src/scripts/popout.ts | 4 +- .../frontend/src/scripts/reaction-picker.ts | 2 +- packages/frontend/src/scripts/select-file.ts | 10 ++--- .../frontend/src/scripts/show-moved-dialog.ts | 6 +-- .../src/scripts/show-suspended-dialog.ts | 4 +- packages/frontend/src/scripts/sound.ts | 2 +- packages/frontend/src/scripts/theme-editor.ts | 2 +- packages/frontend/src/scripts/theme.ts | 2 +- packages/frontend/src/scripts/touch.ts | 2 +- packages/frontend/src/scripts/upload.ts | 12 +++--- .../frontend/src/scripts/use-chart-tooltip.ts | 2 +- .../frontend/src/scripts/use-note-capture.ts | 4 +- packages/frontend/src/store.ts | 2 +- packages/frontend/src/stream.ts | 4 +- packages/frontend/src/theme-store.ts | 8 ++-- .../src/ui/_common_/announcements.vue | 2 +- packages/frontend/src/ui/_common_/common.ts | 10 ++--- packages/frontend/src/ui/_common_/common.vue | 14 +++---- .../src/ui/_common_/navbar-for-mobile.vue | 10 ++--- packages/frontend/src/ui/_common_/navbar.vue | 10 ++--- .../src/ui/_common_/statusbar-federation.vue | 6 +-- .../src/ui/_common_/statusbar-rss.vue | 4 +- .../src/ui/_common_/statusbar-user-list.vue | 8 ++-- .../frontend/src/ui/_common_/statusbars.vue | 2 +- .../src/ui/_common_/stream-indicator.vue | 8 ++-- .../frontend/src/ui/_common_/sw-inject.ts | 10 ++--- packages/frontend/src/ui/_common_/upload.vue | 6 +-- packages/frontend/src/ui/classic.header.vue | 10 ++--- packages/frontend/src/ui/classic.sidebar.vue | 20 +++++----- packages/frontend/src/ui/classic.vue | 16 ++++---- packages/frontend/src/ui/deck.vue | 20 +++++----- .../frontend/src/ui/deck/antenna-column.vue | 6 +-- .../frontend/src/ui/deck/channel-column.vue | 6 +-- packages/frontend/src/ui/deck/column.vue | 6 +-- packages/frontend/src/ui/deck/deck-store.ts | 6 +-- .../frontend/src/ui/deck/direct-column.vue | 2 +- packages/frontend/src/ui/deck/list-column.vue | 4 +- packages/frontend/src/ui/deck/main-column.vue | 12 +++--- .../frontend/src/ui/deck/mentions-column.vue | 2 +- .../src/ui/deck/notifications-column.vue | 6 +-- .../src/ui/deck/role-timeline-column.vue | 6 +-- packages/frontend/src/ui/deck/tl-column.vue | 10 ++--- .../frontend/src/ui/deck/widgets-column.vue | 4 +- packages/frontend/src/ui/minimum.vue | 6 +-- packages/frontend/src/ui/universal.vue | 24 +++++------ .../frontend/src/ui/universal.widgets.vue | 4 +- packages/frontend/src/ui/visitor.vue | 14 +++---- packages/frontend/src/ui/zen.vue | 8 ++-- .../frontend/src/widgets/WidgetActivity.vue | 8 ++-- .../frontend/src/widgets/WidgetAichan.vue | 2 +- .../frontend/src/widgets/WidgetAiscript.vue | 10 ++--- .../src/widgets/WidgetAiscriptApp.vue | 10 ++--- .../frontend/src/widgets/WidgetButton.vue | 8 ++-- .../frontend/src/widgets/WidgetCalendar.vue | 6 +-- .../frontend/src/widgets/WidgetClicker.vue | 2 +- packages/frontend/src/widgets/WidgetClock.vue | 6 +-- .../src/widgets/WidgetDigitalClock.vue | 4 +- .../frontend/src/widgets/WidgetFederation.vue | 12 +++--- .../src/widgets/WidgetInstanceCloud.vue | 8 ++-- .../src/widgets/WidgetInstanceInfo.vue | 6 +-- .../frontend/src/widgets/WidgetJobQueue.vue | 10 ++--- packages/frontend/src/widgets/WidgetMemo.vue | 6 +-- .../src/widgets/WidgetNotifications.vue | 6 +-- .../src/widgets/WidgetOnlineUsers.vue | 10 ++--- .../frontend/src/widgets/WidgetPhotos.vue | 12 +++--- .../frontend/src/widgets/WidgetPostForm.vue | 2 +- .../frontend/src/widgets/WidgetProfile.vue | 6 +-- packages/frontend/src/widgets/WidgetRss.vue | 10 ++--- .../frontend/src/widgets/WidgetRssTicker.vue | 8 ++-- .../frontend/src/widgets/WidgetSlideshow.vue | 8 ++-- .../frontend/src/widgets/WidgetTimeline.vue | 10 ++--- .../frontend/src/widgets/WidgetTrends.vue | 10 ++--- .../frontend/src/widgets/WidgetUnixClock.vue | 2 +- .../frontend/src/widgets/WidgetUserList.vue | 8 ++-- .../src/widgets/server-metric/disk.vue | 2 +- .../src/widgets/server-metric/index.vue | 8 ++-- .../src/widgets/server-metric/mem.vue | 2 +- .../src/widgets/server-metric/net.vue | 2 +- packages/frontend/src/widgets/widget.ts | 6 +-- packages/frontend/test/init.ts | 2 +- packages/frontend/test/scroll.test.ts | 2 +- packages/frontend/tsconfig.json | 2 +- .../sw/src/scripts/create-notification.ts | 10 ++--- packages/sw/src/scripts/lang.ts | 2 +- packages/sw/src/scripts/operations.ts | 4 +- packages/sw/src/sw.ts | 6 +-- 459 files changed, 1643 insertions(+), 1642 deletions(-) diff --git a/packages/frontend/@types/theme.d.ts b/packages/frontend/@types/theme.d.ts index f4ba42b89..376bbb0e9 100644 --- a/packages/frontend/@types/theme.d.ts +++ b/packages/frontend/@types/theme.d.ts @@ -4,7 +4,7 @@ */ declare module '@/themes/*.json5' { - import { Theme } from '@/scripts/theme'; + import { Theme } from '@/scripts/theme.js'; const theme: Theme; diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d38611c2f..a89bd84ff 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -1,6 +1,7 @@ { "name": "frontend", "private": true, + "type": "module", "scripts": { "watch": "vite", "build": "vite build", diff --git a/packages/frontend/src/_boot_.ts b/packages/frontend/src/_boot_.ts index a397e57ad..efb78fe44 100644 --- a/packages/frontend/src/_boot_.ts +++ b/packages/frontend/src/_boot_.ts @@ -7,8 +7,8 @@ import 'vite/modulepreload-polyfill'; import '@/style.scss'; -import { mainBoot } from './boot/main-boot'; -import { subBoot } from './boot/sub-boot'; +import { mainBoot } from '@/boot/main-boot.js'; +import { subBoot } from '@/boot/sub-boot.js'; const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete']; diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 43bda37b6..0e4e4b50f 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -5,14 +5,14 @@ import { defineAsyncComponent, reactive, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import { showSuspendedDialog } from './scripts/show-suspended-dialog'; -import { i18n } from './i18n'; -import { miLocalStorage } from './local-storage'; -import { MenuButton } from './types/menu'; -import { del, get, set } from '@/scripts/idb-proxy'; -import { apiUrl } from '@/config'; -import { waiting, api, popup, popupMenu, success, alert } from '@/os'; -import { unisonReload, reloadChannel } from '@/scripts/unison-reload'; +import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js'; +import { i18n } from '@/i18n.js'; +import { miLocalStorage } from '@/local-storage.js'; +import { MenuButton } from '@/types/menu.js'; +import { del, get, set } from '@/scripts/idb-proxy.js'; +import { apiUrl } from '@/config.js'; +import { waiting, api, popup, popupMenu, success, alert } from '@/os.js'; +import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js'; // TODO: 他のタブと永続化されたstateを同期 diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 8f5de88cc..2175bd1a7 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -5,26 +5,26 @@ import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent, App } from 'vue'; import { compareVersions } from 'compare-versions'; -import widgets from '@/widgets'; -import directives from '@/directives'; -import components from '@/components'; -import { version, ui, lang, updateLocale } from '@/config'; -import { applyTheme } from '@/scripts/theme'; -import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; -import { i18n, updateI18n } from '@/i18n'; -import { confirm, alert, post, popup, toast } from '@/os'; -import { $i, refreshAccount, login, updateAccount, signout } from '@/account'; -import { defaultStore, ColdDeviceStorage } from '@/store'; -import { fetchInstance, instance } from '@/instance'; -import { deviceKind } from '@/scripts/device-kind'; -import { reloadChannel } from '@/scripts/unison-reload'; -import { reactionPicker } from '@/scripts/reaction-picker'; -import { getUrlWithoutLoginId } from '@/scripts/login-id'; -import { getAccountFromId } from '@/scripts/get-account-from-id'; -import { deckStore } from '@/ui/deck/deck-store'; -import { miLocalStorage } from '@/local-storage'; -import { fetchCustomEmojis } from '@/custom-emojis'; -import { mainRouter } from '@/router'; +import widgets from '@/widgets/index.js'; +import directives from '@/directives/index.js'; +import components from '@/components/index.js'; +import { version, ui, lang, updateLocale } from '@/config.js'; +import { applyTheme } from '@/scripts/theme.js'; +import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; +import { i18n, updateI18n } from '@/i18n.js'; +import { confirm, alert, post, popup, toast } from '@/os.js'; +import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js'; +import { defaultStore, ColdDeviceStorage } from '@/store.js'; +import { fetchInstance, instance } from '@/instance.js'; +import { deviceKind } from '@/scripts/device-kind.js'; +import { reloadChannel } from '@/scripts/unison-reload.js'; +import { reactionPicker } from '@/scripts/reaction-picker.js'; +import { getUrlWithoutLoginId } from '@/scripts/login-id.js'; +import { getAccountFromId } from '@/scripts/get-account-from-id.js'; +import { deckStore } from '@/ui/deck/deck-store.js'; +import { miLocalStorage } from '@/local-storage.js'; +import { fetchCustomEmojis } from '@/custom-emojis.js'; +import { mainRouter } from '@/router.js'; export async function common(createVue: () => App) { console.info(`Misskey v${version}`); diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 9ab1f6e14..f2af951d6 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -4,21 +4,21 @@ */ import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue'; -import { common } from './common'; -import { version, ui, lang, updateLocale } from '@/config'; -import { i18n, updateI18n } from '@/i18n'; -import { confirm, alert, post, popup, toast } from '@/os'; -import { useStream } from '@/stream'; -import * as sound from '@/scripts/sound'; -import { $i, refreshAccount, login, updateAccount, signout } from '@/account'; -import { defaultStore, ColdDeviceStorage } from '@/store'; -import { makeHotkey } from '@/scripts/hotkey'; -import { reactionPicker } from '@/scripts/reaction-picker'; -import { miLocalStorage } from '@/local-storage'; -import { claimAchievement, claimedAchievements } from '@/scripts/achievements'; -import { mainRouter } from '@/router'; -import { initializeSw } from '@/scripts/initialize-sw'; -import { deckStore } from '@/ui/deck/deck-store'; +import { common } from './common.js'; +import { version, ui, lang, updateLocale } from '@/config.js'; +import { i18n, updateI18n } from '@/i18n.js'; +import { confirm, alert, post, popup, toast } from '@/os.js'; +import { useStream } from '@/stream.js'; +import * as sound from '@/scripts/sound.js'; +import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js'; +import { defaultStore, ColdDeviceStorage } from '@/store.js'; +import { makeHotkey } from '@/scripts/hotkey.js'; +import { reactionPicker } from '@/scripts/reaction-picker.js'; +import { miLocalStorage } from '@/local-storage.js'; +import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js'; +import { mainRouter } from '@/router.js'; +import { initializeSw } from '@/scripts/initialize-sw.js'; +import { deckStore } from '@/ui/deck/deck-store.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => createApp( diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts index 2cc19f2df..9b4670e13 100644 --- a/packages/frontend/src/boot/sub-boot.ts +++ b/packages/frontend/src/boot/sub-boot.ts @@ -4,7 +4,7 @@ */ import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue'; -import { common } from './common'; +import { common } from './common.js'; export async function subBoot() { const { isClientUpdated } = await common(() => createApp( diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts index 1f3d28ba5..25d2b3c15 100644 --- a/packages/frontend/src/cache.ts +++ b/packages/frontend/src/cache.ts @@ -4,8 +4,8 @@ */ import * as Misskey from 'misskey-js'; -import { Cache } from '@/scripts/cache'; -import { api } from '@/os'; +import { Cache } from '@/scripts/cache.js'; +import { api } from '@/os.js'; export const clipsCache = new Cache(1000 * 60 * 30, () => api('clips/list')); export const rolesCache = new Cache(1000 * 60 * 30, () => api('admin/roles/list')); diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index cb97875bc..66114b873 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -44,9 +44,9 @@ SPDX-License-Identifier: AGPL-3.0-only import MkButton from '@/components/MkButton.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; -import * as os from '@/os'; -import { i18n } from '@/i18n'; -import { dateString } from '@/filters/date'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; +import { dateString } from '@/filters/date.js'; const props = defineProps<{ report: any; diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index 6e0dee697..7814681ea 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -35,8 +35,8 @@ import * as Misskey from 'misskey-js'; import MkWindow from '@/components/MkWindow.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkButton from '@/components/MkButton.vue'; -import * as os from '@/os'; -import { i18n } from '@/i18n'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; const props = defineProps<{ user: Misskey.entities.User; diff --git a/packages/frontend/src/components/MkAccountMoved.vue b/packages/frontend/src/components/MkAccountMoved.vue index e68efb1a3..155d9fe3a 100644 --- a/packages/frontend/src/components/MkAccountMoved.vue +++ b/packages/frontend/src/components/MkAccountMoved.vue @@ -15,9 +15,9 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkMention from './MkMention.vue'; -import { i18n } from '@/i18n'; -import { host as localHost } from '@/config'; -import { api } from '@/os'; +import { i18n } from '@/i18n.js'; +import { host as localHost } from '@/config.js'; +import { api } from '@/os.js'; const user = ref(); diff --git a/packages/frontend/src/components/MkAchievements.stories.impl.ts b/packages/frontend/src/components/MkAchievements.stories.impl.ts index fd971e5ac..a67e1def1 100644 --- a/packages/frontend/src/components/MkAchievements.stories.impl.ts +++ b/packages/frontend/src/components/MkAchievements.stories.impl.ts @@ -9,7 +9,7 @@ import { rest } from 'msw'; import { userDetailed } from '../../.storybook/fakes'; import { commonHandlers } from '../../.storybook/mocks'; import MkAchievements from './MkAchievements.vue'; -import { ACHIEVEMENT_TYPES } from '@/scripts/achievements'; +import { ACHIEVEMENT_TYPES } from '@/scripts/achievements.js'; export const Empty = { render(args) { return { diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index f9d62773f..bea0ed26d 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -54,9 +54,9 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue index eee1e1039..8d2a14730 100644 --- a/packages/frontend/src/components/MkMiniChart.vue +++ b/packages/frontend/src/components/MkMiniChart.vue @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { watch } from 'vue'; import { v4 as uuid } from 'uuid'; import tinycolor from 'tinycolor2'; -import { useInterval } from '@/scripts/use-interval'; +import { useInterval } from '@/scripts/use-interval.js'; const props = defineProps<{ src: number[]; diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue index 4ba078d4e..ec5039c50 100644 --- a/packages/frontend/src/components/MkModal.vue +++ b/packages/frontend/src/components/MkModal.vue @@ -43,10 +43,10 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 99b3852b0..fd2d0ced0 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2778,6 +2778,7 @@ type UserDetailed = UserLite & { name: string; value: string; }[]; + verifiedLinks: string[]; followersCount: number; followingCount: number; hasPendingFollowRequestFromYou: boolean; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 287633910..018210c96 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -38,6 +38,7 @@ export type UserDetailed = UserLite & { description: string | null; ffVisibility: 'public' | 'followers' | 'private'; fields: {name: string; value: string}[]; + verifiedLinks: string[]; followersCount: number; followingCount: number; hasPendingFollowRequestFromYou: boolean; From b654446f937531f1d635da44397f11c507b4da69 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 21 Sep 2023 15:14:08 +0900 Subject: [PATCH 055/121] enhance(frontend): tweak ui --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/pages/settings/profile.vue | 3 +++ 3 files changed, 5 insertions(+) diff --git a/locales/index.d.ts b/locales/index.d.ts index bd1f10d86..c4379409f 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2021,6 +2021,7 @@ export interface Locale { "metadataContent": string; "changeAvatar": string; "changeBanner": string; + "verifiedLinkDescription": string; }; "_exportOrImport": { "allNotes": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 8e684111f..f39bc5282 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1936,6 +1936,7 @@ _profile: metadataContent: "内容" changeAvatar: "アイコン画像を変更" changeBanner: "バナー画像を変更" + verifiedLinkDescription: "内容にURLを設定すると、リンク先のWebサイトに自分のプロフィールへのリンクが含まれている場合に所有者確認済みアイコンを表示させることができます。" _exportOrImport: allNotes: "全てのノート" diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index e045009c1..5e4889f61 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -76,6 +76,8 @@ SPDX-License-Identifier: AGPL-3.0-only
+ + {{ i18n.ts._profile.verifiedLinkDescription }} @@ -119,6 +121,7 @@ import { langmap } from '@/scripts/langmap.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { defaultStore } from '@/store.js'; +import MkInfo from '@/components/MkInfo.vue'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); From 51c3ef556154116e0da3ecc89f75e33d44c197ef Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 21 Sep 2023 15:24:47 +0900 Subject: [PATCH 056/121] update deps --- packages/frontend/package.json | 4 +- pnpm-lock.yaml | 103 +++++++++------------------------ 2 files changed, 29 insertions(+), 78 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 1b3f78c2b..8c3b49f18 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -59,13 +59,13 @@ "querystring": "0.2.1", "rollup": "3.29.2", "sanitize-html": "2.11.0", - "sass": "1.67.0", + "sass": "1.68.0", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", "three": "0.156.1", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", - "tsc-alias": "1.8.7", + "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", "twemoji-parser": "14.0.0", "typescript": "5.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1fe5c9e2..8649cd581 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -770,8 +770,8 @@ importers: specifier: 2.11.0 version: 2.11.0 sass: - specifier: 1.67.0 - version: 1.67.0 + specifier: 1.68.0 + version: 1.68.0 strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 @@ -788,8 +788,8 @@ importers: specifier: 1.6.0 version: 1.6.0 tsc-alias: - specifier: 1.8.7 - version: 1.8.7 + specifier: 1.8.8 + version: 1.8.8 tsconfig-paths: specifier: 4.2.0 version: 4.2.0 @@ -807,7 +807,7 @@ importers: version: 1.8.1 vite: specifier: 4.4.9 - version: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + version: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) vue: specifier: 3.3.4 version: 3.3.4 @@ -976,7 +976,7 @@ importers: version: 7.4.2 storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.2)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0) + version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.3)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0) summaly: specifier: github:misskey-dev/summaly version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7 @@ -985,7 +985,7 @@ importers: version: 1.0.3 vitest: specifier: 0.34.4 - version: 0.34.4(happy-dom@10.0.3)(sass@1.67.0)(terser@5.20.0) + version: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) vitest-fetch-mock: specifier: 0.2.2 version: 0.2.2(vitest@0.34.4) @@ -4262,7 +4262,7 @@ packages: magic-string: 0.27.0 react-docgen-typescript: 2.2.2(typescript@5.2.2) typescript: 5.2.2 - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) dev: true /@jridgewell/gen-mapping@0.3.2: @@ -6300,7 +6300,7 @@ packages: remark-slug: 6.1.0 rollup: 3.29.2 typescript: 5.2.2 - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) transitivePeerDependencies: - encoding - supports-color @@ -6413,29 +6413,6 @@ packages: - supports-color dev: true - /@storybook/components@7.4.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ecbDzSUd74vf6IwHsbQr+9mVRxKWLmwd9zJ8RHMcR8UejTRAAR/eVvYoCG331TQ8TrhTmHTy5xCVv47pm6ORkQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.4.2 - '@storybook/csf': 0.1.0 - '@storybook/global': 5.0.0 - '@storybook/theming': 7.4.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.2 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - dev: true - /@storybook/components@7.4.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-qwRW8wGUuM+H6oKUXXoIDrZECXh/lzowrWXFAzZiocovYEhPtZfl/yvJLWHjOwtka3n7lA7J7EtcjWe8/tueJQ==} peerDependencies: @@ -6861,7 +6838,7 @@ packages: react: 18.2.0 react-docgen: 6.0.0-alpha.3 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -6979,20 +6956,6 @@ packages: ts-dedent: 2.2.0 dev: true - /@storybook/theming@7.4.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-wVmxZHVCqDoZgUOXTS4HRV4UClLtCydRNOEuUZ7X08QIPSA1FVL3gEpTQJfgCsyBX/cwSSofAMUbzAGEVNo+9g==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) - '@storybook/client-logger': 7.4.2 - '@storybook/global': 5.0.0 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@storybook/theming@7.4.3(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==} peerDependencies: @@ -7040,7 +7003,7 @@ packages: magic-string: 0.30.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) vue-docgen-api: 4.64.1(vue@3.3.4) transitivePeerDependencies: - '@preact/preset-vite' @@ -7524,7 +7487,7 @@ packages: dom-accessibility-api: 0.5.16 lodash: 4.17.21 redent: 3.0.0 - vitest: 0.34.4(happy-dom@10.0.3)(sass@1.67.0)(terser@5.20.0) + vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) dev: true /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0): @@ -8322,7 +8285,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.22.11) magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) transitivePeerDependencies: - supports-color dev: true @@ -8334,7 +8297,7 @@ packages: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) vue: 3.3.4 /@vitest/coverage-v8@0.34.4(vitest@0.34.4): @@ -8353,7 +8316,7 @@ packages: std-env: 3.3.3 test-exclude: 6.0.0 v8-to-istanbul: 9.1.0 - vitest: 0.34.4(happy-dom@10.0.3)(sass@1.67.0)(terser@5.20.0) + vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) transitivePeerDependencies: - supports-color dev: true @@ -17407,8 +17370,8 @@ packages: postcss: 8.4.30 dev: false - /sass@1.67.0: - resolution: {integrity: sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A==} + /sass@1.68.0: + resolution: {integrity: sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -18622,18 +18585,6 @@ packages: resolution: {integrity: sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==} dev: true - /tsc-alias@1.8.7: - resolution: {integrity: sha512-59Q/zUQa3miTf99mLbSqaW0hi1jt4WoG8Uhe5hSZJHQpSoFW9eEwvW7jlKMHXWvT+zrzy3SN9PE/YBhQ+WVydA==} - hasBin: true - dependencies: - chokidar: 3.5.3 - commander: 9.5.0 - globby: 11.1.0 - mylas: 2.1.13 - normalize-path: 3.0.0 - plimit-lit: 1.5.0 - dev: false - /tsc-alias@1.8.8: resolution: {integrity: sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==} hasBin: true @@ -19183,7 +19134,7 @@ packages: core-util-is: 1.0.2 extsprintf: 1.3.0 - /vite-node@0.34.4(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0): + /vite-node@0.34.4(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0): resolution: {integrity: sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19193,7 +19144,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) transitivePeerDependencies: - '@types/node' - less @@ -19209,7 +19160,7 @@ packages: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true - /vite@4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0): + /vite@4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0): resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -19241,7 +19192,7 @@ packages: esbuild: 0.18.17 postcss: 8.4.30 rollup: 3.29.2 - sass: 1.67.0 + sass: 1.68.0 terser: 5.20.0 optionalDependencies: fsevents: 2.3.2 @@ -19253,12 +19204,12 @@ packages: vitest: '>=0.16.0' dependencies: cross-fetch: 3.1.5 - vitest: 0.34.4(happy-dom@10.0.3)(sass@1.67.0)(terser@5.20.0) + vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0) transitivePeerDependencies: - encoding dev: true - /vitest@0.34.4(happy-dom@10.0.3)(sass@1.67.0)(terser@5.20.0): + /vitest@0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0): resolution: {integrity: sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==} engines: {node: '>=v14.18.0'} hasBin: true @@ -19311,8 +19262,8 @@ packages: strip-literal: 1.0.1 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.4.9(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) - vite-node: 0.34.4(@types/node@20.6.3)(sass@1.67.0)(terser@5.20.0) + vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) + vite-node: 0.34.4(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -19855,7 +19806,7 @@ packages: sharp: 0.31.3 dev: false - github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.2)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0): + github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.3)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0): resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640} id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640 name: storybook-addon-misskey-theme @@ -19877,7 +19828,7 @@ packages: optional: true dependencies: '@storybook/blocks': 7.4.3(react-dom@18.2.0)(react@18.2.0) - '@storybook/components': 7.4.2(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0) '@storybook/core-events': 7.4.3 '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0) '@storybook/preview-api': 7.4.3 From f195fa4ab91d94072b31de0fdd6b488361db266c Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 21 Sep 2023 15:24:50 +0900 Subject: [PATCH 057/121] 2023.9.0-beta.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index efb463a1d..8b5f57494 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.9", + "version": "2023.9.0-beta.10", "codename": "nasubi", "repository": { "type": "git", From e3f151e2307e4c0d7b9cdfc7deba2ff028adce03 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 21 Sep 2023 18:48:15 +0900 Subject: [PATCH 058/121] =?UTF-8?q?feat:=20=E6=8C=87=E5=AE=9A=E3=81=97?= =?UTF-8?q?=E3=81=9F=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E6=8A=95?= =?UTF-8?q?=E7=A8=BF=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #11499 --- CHANGELOG.md | 1 + locales/index.d.ts | 3 + locales/ja-JP.yml | 3 + .../1695288787870-following-notify.js | 13 +++ .../backend/src/core/NoteCreateService.ts | 19 +++- .../entities/NotificationEntityService.ts | 2 +- .../src/core/entities/UserEntityService.ts | 14 +-- packages/backend/src/models/Following.ts | 7 ++ packages/backend/src/models/Notification.ts | 12 -- .../backend/src/models/json-schema/user.ts | 4 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../server/api/endpoints/following/create.ts | 2 +- .../api/endpoints/following/invalidate.ts | 4 +- .../server/api/endpoints/following/update.ts | 107 ++++++++++++++++++ packages/backend/src/types.ts | 17 ++- packages/backend/test/e2e/users.ts | 2 +- packages/frontend/.storybook/fakes.ts | 1 + .../src/components/MkNotification.vue | 5 + packages/frontend/src/const.ts | 2 +- .../frontend/src/scripts/get-user-menu.ts | 18 +++ packages/misskey-js/etc/misskey-js.api.md | 10 +- packages/misskey-js/src/consts.ts | 2 +- packages/misskey-js/src/entities.ts | 8 +- .../sw/src/scripts/create-notification.ts | 7 ++ 25 files changed, 238 insertions(+), 31 deletions(-) create mode 100644 packages/backend/migration/1695288787870-following-notify.js create mode 100644 packages/backend/src/server/api/endpoints/following/update.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 18c5d5f55..082b448c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - Feat: 二要素認証のバックアップコードが生成されるようになりました - ref. https://github.com/MisskeyIO/misskey/pull/121 - Feat: 二要素認証でパスキーをサポートするようになりました +- Feat: 指定したユーザーが投稿したときに通知できるようになりました - Feat: プロフィールでのリンク検証 - Feat: 通知をテストできるようになりました - Feat: PWAのアイコンが設定できるようになりました diff --git a/locales/index.d.ts b/locales/index.d.ts index c4379409f..784f53355 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1117,6 +1117,8 @@ export interface Locale { "pinnedList": string; "keepScreenOn": string; "verifiedLink": string; + "notifyNotes": string; + "unnotifyNotes": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; @@ -2150,6 +2152,7 @@ export interface Locale { "youReceivedFollowRequest": string; "yourFollowRequestAccepted": string; "pollEnded": string; + "newNote": string; "unreadAntennaNote": string; "emptyPushNotificationMessage": string; "achievementEarned": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f39bc5282..a7a620047 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1114,6 +1114,8 @@ loadConversation: "会話を見る" pinnedList: "ピン留めされたリスト" keepScreenOn: "デバイスの画面を常にオンにする" verifiedLink: "このリンク先の所有者であることが確認されました" +notifyNotes: "投稿を通知" +unnotifyNotes: "投稿の通知を解除" _announcement: forExistingUsers: "既存ユーザーのみ" @@ -2064,6 +2066,7 @@ _notification: youReceivedFollowRequest: "フォローリクエストが来ました" yourFollowRequestAccepted: "フォローリクエストが承認されました" pollEnded: "アンケートの結果が出ました" + newNote: "新しい投稿" unreadAntennaNote: "アンテナ {name}" emptyPushNotificationMessage: "プッシュ通知の更新をしました" achievementEarned: "実績を獲得" diff --git a/packages/backend/migration/1695288787870-following-notify.js b/packages/backend/migration/1695288787870-following-notify.js new file mode 100644 index 000000000..e7e2194b1 --- /dev/null +++ b/packages/backend/migration/1695288787870-following-notify.js @@ -0,0 +1,13 @@ +export class FollowingNotify1695288787870 { + name = 'FollowingNotify1695288787870' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "following" ADD "notify" character varying(32)`); + await queryRunner.query(`CREATE INDEX "IDX_5108098457488634a4768e1d12" ON "following" ("notify") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_5108098457488634a4768e1d12"`); + await queryRunner.query(`ALTER TABLE "following" DROP COLUMN "notify"`); + } +} diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 4b0d81886..972319ddc 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -14,7 +14,7 @@ import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mf import { extractHashtags } from '@/misc/extract-hashtags.js'; import type { IMentionedRemoteUsers } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js'; -import type { ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import type { ChannelsRepository, FollowingsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiApp } from '@/models/App.js'; import { concat } from '@/misc/prelude/array.js'; @@ -185,6 +185,9 @@ export class NoteCreateService implements OnApplicationShutdown { @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: NoteThreadMutingsRepository, + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private idService: IdService, @@ -505,6 +508,20 @@ export class NoteCreateService implements OnApplicationShutdown { this.saveReply(data.reply, note); } + if (data.reply == null) { + this.followingsRepository.findBy({ + followeeId: user.id, + notify: 'normal', + }).then(followings => { + for (const following of followings) { + this.notificationService.createNotification(following.followerId, 'note', { + notifierId: user.id, + noteId: note.id, + }); + } + }); + } + // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき if (data.renote && (await this.noteEntityService.countSameRenotes(user.id, data.renote.id, note.id) === 0)) { if (!user.isBot) this.incRenoteCount(data.renote); diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 75a2f62f6..3ee7c91f3 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -20,7 +20,7 @@ import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; -const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded'] as (typeof notificationTypes[number])[]); +const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded'] as (typeof notificationTypes[number])[]); @Injectable() export class NotificationEntityService implements OnModuleInit { diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 7bef410bf..3dd64ce62 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -146,15 +146,14 @@ export class UserEntityService implements OnModuleInit { @bindThis public async getRelation(me: MiUser['id'], target: MiUser['id']) { + const following = await this.followingsRepository.findOneBy({ + followerId: me, + followeeId: target, + }); return awaitAll({ id: target, - isFollowing: this.followingsRepository.count({ - where: { - followerId: me, - followeeId: target, - }, - take: 1, - }).then(n => n > 0), + following, + isFollowing: following != null, isFollowed: this.followingsRepository.count({ where: { followerId: target, @@ -486,6 +485,7 @@ export class UserEntityService implements OnModuleInit { isBlocked: relation.isBlocked, isMuted: relation.isMuted, isRenoteMuted: relation.isRenoteMuted, + notify: relation.following?.notify ?? 'none', } : {}), } as Promiseable> as Promiseable>; diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts index 05b729412..8c9f965fa 100644 --- a/packages/backend/src/models/Following.ts +++ b/packages/backend/src/models/Following.ts @@ -45,6 +45,13 @@ export class MiFollowing { @JoinColumn() public follower: MiUser | null; + @Index() + @Column('varchar', { + length: 32, + nullable: true, + }) + public notify: 'normal' | null; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts index fb7f67dfd..c0a9df2e2 100644 --- a/packages/backend/src/models/Notification.ts +++ b/packages/backend/src/models/Notification.ts @@ -22,18 +22,6 @@ export type MiNotification = { /** * 通知の種類。 - * follow - フォローされた - * mention - 投稿で自分が言及された - * reply - 投稿に返信された - * renote - 投稿がRenoteされた - * quote - 投稿が引用Renoteされた - * reaction - 投稿にリアクションされた - * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した - * receiveFollowRequest - フォローリクエストされた - * followRequestAccepted - 自分の送ったフォローリクエストが承認された - * achievementEarned - 実績を獲得 - * app - アプリ通知 - * test - テスト通知(サーバー側) */ type: typeof notificationTypes[number]; diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 8d0e4e72e..f15b225a3 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -273,6 +273,10 @@ export const packedUserDetailedNotMeOnlySchema = { type: 'string', nullable: false, optional: true, }, + notify: { + type: 'string', + nullable: false, optional: true, + }, //#endregion }, } as const; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 7b9fa6c3b..41a11bfb1 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -160,6 +160,7 @@ import * as ep___federation_users from './endpoints/federation/users.js'; import * as ep___federation_stats from './endpoints/federation/stats.js'; import * as ep___following_create from './endpoints/following/create.js'; import * as ep___following_delete from './endpoints/following/delete.js'; +import * as ep___following_update from './endpoints/following/update.js'; import * as ep___following_invalidate from './endpoints/following/invalidate.js'; import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; @@ -507,6 +508,7 @@ const $federation_users: Provider = { provide: 'ep:federation/users', useClass: const $federation_stats: Provider = { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }; const $following_create: Provider = { provide: 'ep:following/create', useClass: ep___following_create.default }; const $following_delete: Provider = { provide: 'ep:following/delete', useClass: ep___following_delete.default }; +const $following_update: Provider = { provide: 'ep:following/update', useClass: ep___following_update.default }; const $following_invalidate: Provider = { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }; const $following_requests_accept: Provider = { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }; const $following_requests_cancel: Provider = { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }; @@ -858,6 +860,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $federation_stats, $following_create, $following_delete, + $following_update, $following_invalidate, $following_requests_accept, $following_requests_cancel, @@ -1203,6 +1206,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $federation_stats, $following_create, $following_delete, + $following_update, $following_invalidate, $following_requests_accept, $following_requests_cancel, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index a9cb7c341..ab20a708e 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -160,6 +160,7 @@ import * as ep___federation_users from './endpoints/federation/users.js'; import * as ep___federation_stats from './endpoints/federation/stats.js'; import * as ep___following_create from './endpoints/following/create.js'; import * as ep___following_delete from './endpoints/following/delete.js'; +import * as ep___following_update from './endpoints/following/update.js'; import * as ep___following_invalidate from './endpoints/following/invalidate.js'; import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; @@ -505,6 +506,7 @@ const eps = [ ['federation/stats', ep___federation_stats], ['following/create', ep___following_create], ['following/delete', ep___following_delete], + ['following/update', ep___following_update], ['following/invalidate', ep___following_invalidate], ['following/requests/accept', ep___following_requests_accept], ['following/requests/cancel', ep___following_requests_cancel], diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index d80c85367..e0e7fed87 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -19,7 +19,7 @@ export const meta = { limit: { duration: ms('1hour'), - max: 50, + max: 100, }, requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 147d16556..53ef925b2 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -29,7 +29,7 @@ export const meta = { noSuchUser: { message: 'No such user.', code: 'NO_SUCH_USER', - id: '5b12c78d-2b28-4dca-99d2-f56139b42ff8', + id: 'b77e6ae6-a3e5-40da-9cc8-c240115479cc', }, followerIsYourself: { @@ -41,7 +41,7 @@ export const meta = { notFollowing: { message: 'The other use is not following you.', code: 'NOT_FOLLOWING', - id: '5dbf82f5-c92b-40b1-87d1-6c8c0741fd09', + id: '918faac3-074f-41ae-9c43-ed5d2946770d', }, }, diff --git a/packages/backend/src/server/api/endpoints/following/update.ts b/packages/backend/src/server/api/endpoints/following/update.ts new file mode 100644 index 000000000..25f393e51 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/following/update.ts @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { FollowingsRepository } from '@/models/_.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { UserFollowingService } from '@/core/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['following', 'users'], + + limit: { + duration: ms('1hour'), + max: 100, + }, + + requireCredential: true, + + kind: 'write:following', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '14318698-f67e-492a-99da-5353a5ac52be', + }, + + followeeIsYourself: { + message: 'Followee is yourself.', + code: 'FOLLOWEE_IS_YOURSELF', + id: '4c4cbaf9-962a-463b-8418-a5e365dbf2eb', + }, + + notFollowing: { + message: 'You are not following that user.', + code: 'NOT_FOLLOWING', + id: 'b8dc75cf-1cb5-46c9-b14b-5f1ffbd782c9', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'UserLite', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + notify: { type: 'string', enum: ['normal', 'none'] }, + }, + required: ['userId', 'notify'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userFollowingService: UserFollowingService, + ) { + super(meta, paramDef, async (ps, me) => { + const follower = me; + + // Check if the follower is yourself + if (me.id === ps.userId) { + throw new ApiError(meta.errors.followeeIsYourself); + } + + // Get followee + const followee = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; + }); + + // Check not following + const exist = await this.followingsRepository.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notFollowing); + } + + await this.followingsRepository.update({ + id: exist.id, + }, { + notify: ps.notify === 'none' ? null : ps.notify, + }); + + return await this.userEntityService.pack(follower.id, me); + }); + } +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 024ba01e3..0a28d88d0 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -3,7 +3,22 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app', 'test'] as const; +/** + * note - 通知オンにしているユーザーが投稿した + * follow - フォローされた + * mention - 投稿で自分が言及された + * reply - 投稿に返信された + * renote - 投稿がRenoteされた + * quote - 投稿が引用Renoteされた + * reaction - 投稿にリアクションされた + * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した + * receiveFollowRequest - フォローリクエストされた + * followRequestAccepted - 自分の送ったフォローリクエストが承認された + * achievementEarned - 実績を獲得 + * app - アプリ通知 + * test - テスト通知(サーバー側) + */ +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app', 'test'] as const; export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index 13cea0cfc..e4ecd6e39 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -494,7 +494,7 @@ describe('ユーザー', () => { { parameters: (): object => ({ mutedWords: [] }) }, { parameters: (): object => ({ mutedInstances: ['xxxx.xxxxx'] }) }, { parameters: (): object => ({ mutedInstances: [] }) }, - { parameters: (): object => ({ mutingNotificationTypes: ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] }) }, + { parameters: (): object => ({ mutingNotificationTypes: ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] }) }, { parameters: (): object => ({ mutingNotificationTypes: [] }) }, { parameters: (): object => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) }, { parameters: (): object => ({ emailNotificationTypes: [] }) }, diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index 2bda89196..811c24392 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -120,6 +120,7 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi updatedAt: null, uri: null, url: null, + notify: 'none', }; } diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index f71834b09..99443a640 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+ @@ -47,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._notification.pollEnded }} + {{ i18n.ts._notification.newNote }}: {{ notification.note.user.name ?? notification.note.user.username }} {{ i18n.ts._notification.achievementEarned }} {{ i18n.ts._notification.testNotification }} @@ -73,6 +75,9 @@ SPDX-License-Identifier: AGPL-3.0-only + + + diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 19bad95c8..15038b106 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -54,7 +54,7 @@ https://github.com/sindresorhus/file-type/blob/main/core.js https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers */ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const; export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; export const ROLE_POLICIES = [ diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 1561daa6d..128cbafb1 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -80,6 +80,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router }); } + async function toggleNotify() { + os.apiWithDialog('following/update', { + userId: user.id, + notify: user.notify === 'normal' ? 'none' : 'normal', + }).then(() => { + user.notify = user.notify === 'normal' ? 'none' : 'normal'; + }); + } + function reportAbuse() { os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { user: user, @@ -270,6 +279,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router }]); } + // フォローしたとしても user.isFollowing はリアルタイム更新されないので不便なため + //if (user.isFollowing) { + menu = menu.concat([{ + icon: user.notify === 'none' ? 'ti ti-bell' : 'ti ti-bell-off', + text: user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes, + action: toggleNotify, + }]); + //} + menu = menu.concat([null, { icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off', text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute, diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index fd2d0ced0..d72652bd9 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2609,7 +2609,12 @@ type Notification_2 = { userId: User['id']; note: Note; } | { - type: 'pollVote'; + type: 'note'; + user: User; + userId: User['id']; + note: Note; +} | { + type: 'pollEnded'; user: User; userId: User['id']; note: Note; @@ -2640,7 +2645,7 @@ type Notification_2 = { }); // @public (undocumented) -export const notificationTypes: readonly ["follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app"]; +export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app"]; // @public (undocumented) type OriginType = 'combined' | 'local' | 'remote'; @@ -2810,6 +2815,7 @@ type UserDetailed = UserLite & { updatedAt: DateString | null; uri: string | null; url: string | null; + notify: 'normal' | 'none'; }; // @public (undocumented) diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index 2b144ab4a..6cf6dc07e 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; +export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 018210c96..9a0114d71 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -70,6 +70,7 @@ export type UserDetailed = UserLite & { updatedAt: DateString | null; uri: string | null; url: string | null; + notify: 'normal' | 'none'; }; export type UserGroup = TODO; @@ -233,7 +234,12 @@ export type Notification = { userId: User['id']; note: Note; } | { - type: 'pollVote'; + type: 'note'; + user: User; + userId: User['id']; + note: Note; +} | { + type: 'pollEnded'; user: User; userId: User['id']; note: Note; diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 2b7f91a74..f33ab1c33 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -134,6 +134,13 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif ], }]; + case 'note': + return [t('_notification.newNote') + ': ' + getUserName(data.body.user), { + body: data.body.note.text ?? '', + icon: data.body.user.avatarUrl, + data, + }]; + case 'reaction': { let reaction = data.body.reaction; let badge: string | undefined; From 03b5acf17f43af75754a10002819372438df51a3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 21 Sep 2023 19:39:42 +0900 Subject: [PATCH 059/121] fix test --- packages/backend/test/e2e/users.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index e4ecd6e39..0b3f20026 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -132,6 +132,7 @@ describe('ユーザー', () => { isBlocked: user.isBlocked ?? false, isMuted: user.isMuted ?? false, isRenoteMuted: user.isRenoteMuted ?? false, + notify: user.notify ?? 'none', }); }; From eca8c7a52f9d190a89b380290b5b50d0cb96c750 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 22 Sep 2023 10:01:34 +0900 Subject: [PATCH 060/121] tweak ui --- packages/frontend/src/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index b4713a323..8a43ba892 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -118,7 +118,7 @@ export const defaultStore = markRaw(new Storage('base', { where: 'deviceAccount', default: [ 'notifications', - 'favorites', + 'clips', 'drive', 'followRequests', '-', From c836157edb869e80b15f51bb8f48725e3b898b9a Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 22 Sep 2023 14:12:33 +0900 Subject: [PATCH 061/121] =?UTF-8?q?enhance:=20=E4=BA=8C=E8=A6=81=E7=B4=A0?= =?UTF-8?q?=E8=AA=8D=E8=A8=BC=E8=A8=AD=E5=AE=9A=E6=99=82=E3=81=AE=E3=82=BB?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=AA=E3=83=86=E3=82=A3=E3=82=92=E5=BC=B7?= =?UTF-8?q?=E5=8C=96=20(#11863)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance: 二要素認証設定時のセキュリティを強化 パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要にする * Update CoreModule.ts * Update 2fa.ts * wip * wip * Update 2fa.ts * tweak --- CHANGELOG.md | 9 +-- locales/index.d.ts | 3 +- locales/ja-JP.yml | 3 +- packages/backend/src/core/CoreModule.ts | 6 ++ packages/backend/src/core/UserAuthService.ts | 45 ++++++++++++ .../src/server/api/SigninApiService.ts | 28 +++----- .../src/server/api/endpoints/i/2fa/done.ts | 2 +- .../server/api/endpoints/i/2fa/key-done.ts | 20 +++++- .../api/endpoints/i/2fa/register-key.ts | 20 +++++- .../server/api/endpoints/i/2fa/register.ts | 21 +++++- .../server/api/endpoints/i/2fa/remove-key.ts | 20 +++++- .../server/api/endpoints/i/2fa/unregister.ts | 20 +++++- .../server/api/endpoints/i/change-password.ts | 22 +++++- .../server/api/endpoints/i/delete-account.ts | 23 ++++-- .../server/api/endpoints/i/update-email.ts | 20 +++++- packages/backend/test/e2e/2fa.ts | 49 +++++++++++++ packages/frontend/src/components/MkInput.vue | 4 ++ .../src/components/MkPasswordDialog.vue | 70 +++++++++++++++++++ packages/frontend/src/os.ts | 13 ++++ packages/frontend/src/pages/settings/2fa.vue | 65 ++++++++--------- .../frontend/src/pages/settings/email.vue | 20 +++--- .../frontend/src/pages/settings/other.vue | 10 ++- .../frontend/src/pages/settings/security.vue | 29 ++++---- 23 files changed, 400 insertions(+), 122 deletions(-) create mode 100644 packages/backend/src/core/UserAuthService.ts create mode 100644 packages/frontend/src/components/MkPasswordDialog.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 082b448c2..92bb2c816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ - Feat: プロフィールでのリンク検証 - Feat: 通知をテストできるようになりました - Feat: PWAのアイコンが設定できるようになりました +- Enhance: 二要素認証設定時のセキュリティを強化 + - パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要になりました - Enhance: manifest.jsonをオーバーライド可能に - Enhance: 依存関係の更新 - Enhance: ローカリゼーションの更新 @@ -40,10 +42,8 @@ - Feat: Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`) - Feat: クライアントを起動している間、デバイスの画面が自動でオフになるのを防ぐオプションを追加 - Feat: 新しい実績を追加 -- Enhance: ノート詳細ページを改修 - - 読み込み時のパフォーマンスが向上しました - - リノート一覧、リアクション一覧がタブとして追加されました - - ノートのメニューからは当該項目は消えました +- Enhance: ノート詳細ページでリノート一覧、リアクション一覧タブを追加 + - ノートのメニューからは当該項目は消えました - Enhance: プロフィールにその人が作ったPlayの一覧出せるように - Enhance: メニューのスイッチの動作を改善 - Enhance: 絵文字ピッカーの検索の表示件数を100件に増加 @@ -62,6 +62,7 @@ - Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように - Enhance: Mk:apiが失敗した時にエラー型の値(AiScript 0.16.0で追加)を返すように - Enhance: ScratchpadでAsync:系関数やボタンのコールバックなどのエラーにもダイアログを出すように(試験的なためPlayなどには未実装) +- Enhance: ノート詳細ページ読み込み時のパフォーマンスが向上しました - Enhance: タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 - Enhance: 「Moderation note」、「Add moderation note」をローカライズできるように - Enhance: 細かなデザインの調整 diff --git a/locales/index.d.ts b/locales/index.d.ts index 784f53355..f7bc350e2 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1119,6 +1119,8 @@ export interface Locale { "verifiedLink": string; "notifyNotes": string; "unnotifyNotes": string; + "authentication": string; + "authenticationRequiredToContinue": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; @@ -1833,7 +1835,6 @@ export interface Locale { "_2fa": { "alreadyRegistered": string; "registerTOTP": string; - "passwordToTOTP": string; "step1": string; "step2": string; "step2Click": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index a7a620047..5436cf049 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1116,6 +1116,8 @@ keepScreenOn: "デバイスの画面を常にオンにする" verifiedLink: "このリンク先の所有者であることが確認されました" notifyNotes: "投稿を通知" unnotifyNotes: "投稿の通知を解除" +authentication: "認証" +authenticationRequiredToContinue: "続けるには認証を行ってください" _announcement: forExistingUsers: "既存ユーザーのみ" @@ -1750,7 +1752,6 @@ _timelineTutorial: _2fa: alreadyRegistered: "既に設定は完了しています。" registerTOTP: "認証アプリの設定を開始" - passwordToTOTP: "パスワードを入力してください" step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。" step2: "次に、表示されているQRコードをアプリでスキャンします。" step2Click: "QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。" diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 18271ee34..78333e70a 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -51,6 +51,7 @@ import { UserKeypairService } from './UserKeypairService.js'; import { UserListService } from './UserListService.js'; import { UserMutingService } from './UserMutingService.js'; import { UserSuspendService } from './UserSuspendService.js'; +import { UserAuthService } from './UserAuthService.js'; import { VideoProcessingService } from './VideoProcessingService.js'; import { WebhookService } from './WebhookService.js'; import { ProxyAccountService } from './ProxyAccountService.js'; @@ -177,6 +178,7 @@ const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisti const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService }; const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService }; const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService }; +const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService }; const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService }; const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService }; const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService }; @@ -306,6 +308,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserListService, UserMutingService, UserSuspendService, + UserAuthService, VideoProcessingService, WebhookService, UtilityService, @@ -428,6 +431,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserListService, $UserMutingService, $UserSuspendService, + $UserAuthService, $VideoProcessingService, $WebhookService, $UtilityService, @@ -551,6 +555,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserListService, UserMutingService, UserSuspendService, + UserAuthService, VideoProcessingService, WebhookService, UtilityService, @@ -672,6 +677,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserListService, $UserMutingService, $UserSuspendService, + $UserAuthService, $VideoProcessingService, $WebhookService, $UtilityService, diff --git a/packages/backend/src/core/UserAuthService.ts b/packages/backend/src/core/UserAuthService.ts new file mode 100644 index 000000000..ccf4dfc6b --- /dev/null +++ b/packages/backend/src/core/UserAuthService.ts @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { QueryFailedError } from 'typeorm'; +import * as OTPAuth from 'otpauth'; +import { DI } from '@/di-symbols.js'; +import type { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import { bindThis } from '@/decorators.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import type { MiLocalUser } from '@/models/User.js'; + +@Injectable() +export class UserAuthService { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + ) { + } + + @bindThis + public async twoFactorAuthenticate(profile: MiUserProfile, token: string): Promise { + if (profile.twoFactorBackupSecret?.includes(token)) { + await this.userProfilesRepository.update({ userId: profile.userId }, { + twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token), + }); + } else { + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!), + digits: 6, + token, + window: 5, + }); + + if (delta === null) { + throw new Error('authentication failed'); + } + } + } +} diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 48d74e2b0..150f3f24d 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -19,6 +19,7 @@ import type { MiLocalUser } from '@/models/User.js'; import { IdService } from '@/core/IdService.js'; import { bindThis } from '@/decorators.js'; import { WebAuthnService } from '@/core/WebAuthnService.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; import type { AuthenticationResponseJSON } from '@simplewebauthn/typescript-types'; @@ -42,6 +43,7 @@ export class SigninApiService { private idService: IdService, private rateLimiterService: RateLimiterService, private signinService: SigninService, + private userAuthService: UserAuthService, private webAuthnService: WebAuthnService, ) { } @@ -124,7 +126,7 @@ export class SigninApiService { const same = await bcrypt.compare(password, profile.password!); const fail = async (status?: number, failure?: { id: string }) => { - // Append signin history + // Append signin history await this.signinsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), @@ -154,27 +156,15 @@ export class SigninApiService { }); } - if (profile.twoFactorBackupSecret?.includes(token)) { - await this.userProfilesRepository.update({ userId: profile.userId }, { - twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token), - }); - return this.signinService.signin(request, reply, user); - } - - const delta = OTPAuth.TOTP.validate({ - secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!), - digits: 6, - token, - window: 1, - }); - - if (delta === null) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { return await fail(403, { id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f', }); - } else { - return this.signinService.signin(request, reply, user); } + + return this.signinService.signin(request, reply, user); } else if (body.credential) { if (!same && !profile.usePasswordLessLogin) { return await fail(403, { @@ -203,6 +193,6 @@ export class SigninApiService { reply.code(200); return authRequest; } - // never get here + // never get here } } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index c6a193fbb..9f8e2894b 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -47,7 +47,7 @@ export default class extends Endpoint { // eslint- secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret), digits: 6, token, - window: 1, + window: 5, }); if (delta === null) { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 4b0e761bb..6d530aba3 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -12,6 +12,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js'; import { WebAuthnService } from '@/core/WebAuthnService.js'; import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -37,6 +38,7 @@ export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, name: { type: 'string', minLength: 1, maxLength: 30 }, credential: { type: 'object' }, }, @@ -54,16 +56,28 @@ export default class extends Endpoint { private userSecurityKeysRepository: UserSecurityKeysRepository, private webAuthnService: WebAuthnService, + private userAuthService: UserAuthService, private userEntityService: UserEntityService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password ?? ''); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - if (!same) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index b4d523794..c39005f2d 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -10,6 +10,7 @@ import type { UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { WebAuthnService } from '@/core/WebAuthnService.js'; import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -41,6 +42,7 @@ export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; @@ -53,8 +55,10 @@ export default class extends Endpoint { private userProfilesRepository: UserProfilesRepository, private webAuthnService: WebAuthnService, + private userAuthService: UserAuthService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOne({ where: { userId: me.id, @@ -66,10 +70,20 @@ export default class extends Endpoint { throw new ApiError(meta.errors.userNotFound); } - // Compare password - const same = await bcrypt.compare(ps.password, profile.password ?? ''); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - if (!same) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index 9d027b25b..b358c812e 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -12,6 +12,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -31,6 +32,7 @@ export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; @@ -43,14 +45,27 @@ export default class extends Endpoint { // eslint- @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userAuthService: UserAuthService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password ?? ''); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - if (!same) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index ad2cb8c20..da8ac9855 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -11,6 +11,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -30,6 +31,7 @@ export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, credentialId: { type: 'string' }, }, required: ['password', 'credentialId'], @@ -45,15 +47,27 @@ export default class extends Endpoint { // eslint- private userProfilesRepository: UserProfilesRepository, private userEntityService: UserEntityService, + private userAuthService: UserAuthService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password ?? ''); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - if (!same) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index b834dfff4..338f12c5c 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -11,6 +11,7 @@ import type { UserProfilesRepository } from '@/models/_.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '@/server/api/error.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -30,6 +31,7 @@ export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; @@ -41,15 +43,27 @@ export default class extends Endpoint { // eslint- private userProfilesRepository: UserProfilesRepository, private userEntityService: UserEntityService, + private userAuthService: UserAuthService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password ?? ''); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - if (!same) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? ''); + if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index 868cff8ad..a3c37ffdb 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -20,6 +21,7 @@ export const paramDef = { properties: { currentPassword: { type: 'string' }, newPassword: { type: 'string', minLength: 1 }, + token: { type: 'string', nullable: true }, }, required: ['currentPassword', 'newPassword'], } as const; @@ -29,14 +31,28 @@ export default class extends Endpoint { // eslint- constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + + private userAuthService: UserAuthService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.currentPassword, profile.password!); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - if (!same) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.currentPassword, profile.password!); + + if (!passwordMatched) { throw new Error('incorrect password'); } diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index f318d9cda..fbac845fd 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -9,6 +9,7 @@ import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; export const meta = { requireCredential: true, @@ -20,6 +21,7 @@ export const paramDef = { type: 'object', properties: { password: { type: 'string' }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; @@ -33,19 +35,32 @@ export default class extends Endpoint { // eslint- @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + private userAuthService: UserAuthService, private deleteAccountService: DeleteAccountService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); + + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } + + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + const userDetailed = await this.usersRepository.findOneByOrFail({ id: me.id }); if (userDetailed.isDeleted) { return; } - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { + const passwordMatched = await bcrypt.compare(ps.password, profile.password!); + if (!passwordMatched) { throw new Error('incorrect password'); } diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 77135bf85..a36b3a732 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -14,6 +14,7 @@ import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js'; +import { UserAuthService } from '@/core/UserAuthService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -46,6 +47,7 @@ export const paramDef = { properties: { password: { type: 'string' }, email: { type: 'string', nullable: true }, + token: { type: 'string', nullable: true }, }, required: ['password'], } as const; @@ -61,15 +63,27 @@ export default class extends Endpoint { // eslint- private userEntityService: UserEntityService, private emailService: EmailService, + private userAuthService: UserAuthService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { + const token = ps.token; const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + if (profile.twoFactorEnabled) { + if (token == null) { + throw new Error('authentication failed'); + } - if (!same) { + try { + await this.userAuthService.twoFactorAuthenticate(profile, token); + } catch (e) { + throw new Error('authentication failed'); + } + } + + const passwordMatched = await bcrypt.compare(ps.password, profile.password!); + if (!passwordMatched) { throw new ApiError(meta.errors.incorrectPassword); } diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts index 80d2e9d35..ed967d262 100644 --- a/packages/backend/test/e2e/2fa.ts +++ b/packages/backend/test/e2e/2fa.ts @@ -60,10 +60,12 @@ describe('2要素認証', () => { }; const keyDoneParam = (param: { + token: string, keyName: string, credentialId: Buffer, creationOptions: PublicKeyCredentialCreationOptionsJSON, }): { + token: string, password: string, name: string, credential: RegistrationResponseJSON, @@ -94,6 +96,7 @@ describe('2要素認証', () => { return { password, + token: param.token, name: param.keyName, credential: { id: param.credentialId.toString('base64url'), @@ -218,6 +221,12 @@ describe('2要素認証', () => { }); assert.strictEqual(signinResponse.status, 200); assert.notEqual(signinResponse.body.i, undefined); + + // 後片付け + await api('/i/2fa/unregister', { + password, + token: otpToken(registerResponse.body.secret), + }, alice); }); test('が設定でき、セキュリティキーでログインできる。', async () => { @@ -233,6 +242,7 @@ describe('2要素認証', () => { const registerKeyResponse = await api('/i/2fa/register-key', { password, + token: otpToken(registerResponse.body.secret), }, alice); assert.strictEqual(registerKeyResponse.status, 200); assert.notEqual(registerKeyResponse.body.rp, undefined); @@ -241,6 +251,7 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, @@ -271,6 +282,12 @@ describe('2要素認証', () => { })); assert.strictEqual(signinResponse2.status, 200); assert.notEqual(signinResponse2.body.i, undefined); + + // 後片付け + await api('/i/2fa/unregister', { + password, + token: otpToken(registerResponse.body.secret), + }, alice); }); test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => { @@ -285,6 +302,7 @@ describe('2要素認証', () => { assert.strictEqual(doneResponse.status, 200); const registerKeyResponse = await api('/i/2fa/register-key', { + token: otpToken(registerResponse.body.secret), password, }, alice); assert.strictEqual(registerKeyResponse.status, 200); @@ -292,6 +310,7 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, @@ -326,6 +345,12 @@ describe('2要素認証', () => { }); assert.strictEqual(signinResponse2.status, 200); assert.notEqual(signinResponse2.body.i, undefined); + + // 後片付け + await api('/i/2fa/unregister', { + password, + token: otpToken(registerResponse.body.secret), + }, alice); }); test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => { @@ -340,6 +365,7 @@ describe('2要素認証', () => { assert.strictEqual(doneResponse.status, 200); const registerKeyResponse = await api('/i/2fa/register-key', { + token: otpToken(registerResponse.body.secret), password, }, alice); assert.strictEqual(registerKeyResponse.status, 200); @@ -347,6 +373,7 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, @@ -367,6 +394,12 @@ describe('2要素認証', () => { assert.strictEqual(securityKeys.length, 1); assert.strictEqual(securityKeys[0].name, renamedKey); assert.notEqual(securityKeys[0].lastUsed, undefined); + + // 後片付け + await api('/i/2fa/unregister', { + password, + token: otpToken(registerResponse.body.secret), + }, alice); }); test('が設定でき、設定したセキュリティキーを削除できる。', async () => { @@ -381,6 +414,7 @@ describe('2要素認証', () => { assert.strictEqual(doneResponse.status, 200); const registerKeyResponse = await api('/i/2fa/register-key', { + token: otpToken(registerResponse.body.secret), password, }, alice); assert.strictEqual(registerKeyResponse.status, 200); @@ -388,6 +422,7 @@ describe('2要素認証', () => { const keyName = 'example-key'; const credentialId = crypto.randomBytes(0x41); const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({ + token: otpToken(registerResponse.body.secret), keyName, credentialId, creationOptions: registerKeyResponse.body, @@ -400,6 +435,7 @@ describe('2要素認証', () => { assert.strictEqual(iResponse.status, 200); for (const key of iResponse.body.securityKeysList) { const removeKeyResponse = await api('/i/2fa/remove-key', { + token: otpToken(registerResponse.body.secret), password, credentialId: key.id, }, alice); @@ -418,6 +454,12 @@ describe('2要素認証', () => { }); assert.strictEqual(signinResponse.status, 200); assert.notEqual(signinResponse.body.i, undefined); + + // 後片付け + await api('/i/2fa/unregister', { + password, + token: otpToken(registerResponse.body.secret), + }, alice); }); test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => { @@ -438,6 +480,7 @@ describe('2要素認証', () => { assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true); const unregisterResponse = await api('/i/2fa/unregister', { + token: otpToken(registerResponse.body.secret), password, }, alice); assert.strictEqual(unregisterResponse.status, 204); @@ -447,5 +490,11 @@ describe('2要素認証', () => { }); assert.strictEqual(signinResponse.status, 200); assert.notEqual(signinResponse.body.i, undefined); + + // 後片付け + await api('/i/2fa/unregister', { + password, + token: otpToken(registerResponse.body.secret), + }, alice); }); }); diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue index e9397ce86..315ce958c 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -155,6 +155,10 @@ onMounted(() => { } }); }); + +defineExpose({ + focus, +}); From 1924bd20bb294bf2fdec6df922b3ec663102ae7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sat, 23 Sep 2023 09:08:14 +0900 Subject: [PATCH 070/121] =?UTF-8?q?enhance(frontend):=20=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E3=81=AE=E3=82=BD=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E7=A2=BA=E8=AA=8D=E3=83=BB?= =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=BC=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=20(#11873)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (add) plugin: view and copy source code * (fix) plugin permission ui * Update Changelog --- CHANGELOG.md | 2 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../frontend/src/pages/settings/plugin.vue | 61 ++++++++++++++----- packages/frontend/src/store.ts | 3 + 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6c66f085..7c4134bde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ - Enhance: ノート詳細ページ読み込み時のパフォーマンスが向上しました - Enhance: タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 - Enhance: 「Moderation note」、「Add moderation note」をローカライズできるように +- Enhance: プラグインのソースコードを確認・コピーできるように - Enhance: 細かなデザインの調整 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正 @@ -76,6 +77,7 @@ - Fix: Misskeyプラグインをインストールする際のAiScriptバージョンのチェックが0.14.0以降に対応していない問題を修正 - Fix: 他のサーバーのユーザーへ「メッセージを送信」した時の初期テキストのメンションが間違っている問題を修正 - Fix: 環境によってはMisskey Webが開けない問題を修正 +- Fix: プラグインの権限リストが見れない問題を修正 ### Server - Change: cacheRemoteFilesの初期値はfalseになりました diff --git a/locales/index.d.ts b/locales/index.d.ts index f6b6daae8..256f17812 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1632,6 +1632,7 @@ export interface Locale { "install": string; "installWarn": string; "manage": string; + "viewSource": string; }; "_preferencesBackups": { "list": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 82ced0aa3..90d025d80 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1549,6 +1549,7 @@ _plugin: install: "プラグインのインストール" installWarn: "信頼できないプラグインはインストールしないでください。" manage: "プラグインの管理" + viewSource: "ソースを表示" _preferencesBackups: list: "作成したバックアップ" diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue index e9bc8573b..4a2d8d600 100644 --- a/packages/frontend/src/pages/settings/plugin.vue +++ b/packages/frontend/src/pages/settings/plugin.vue @@ -10,28 +10,49 @@ SPDX-License-Identifier: AGPL-3.0-only
-
- {{ plugin.name }}v{{ plugin.version }} +
+
+ {{ plugin.name }}v{{ plugin.version }} + {{ i18n.ts.makeActive }} +
- {{ i18n.ts.makeActive }} - - - - - - - - - - - - - +
+ + + + + + + + + + + + +
{{ i18n.ts.settings }} {{ i18n.ts.uninstall }}
+ + + + + +
+
+ {{ i18n.ts.copy }} +
+ + +
+
@@ -44,8 +65,11 @@ import FormLink from '@/components/form/link.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import FormSection from '@/components/form/section.vue'; import MkButton from '@/components/MkButton.vue'; +import MkCode from '@/components/MkCode.vue'; +import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import * as os from '@/os.js'; +import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { ColdDeviceStorage } from '@/store.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; @@ -61,6 +85,11 @@ function uninstall(plugin) { }); } +function copy(plugin) { + copyToClipboard(plugin.src ?? ''); + os.success(); +} + // TODO: この処理をstore側にactionとして移動し、設定画面を開くAiScriptAPIを実装できるようにする async function config(plugin) { const config = plugin.config; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 16483f0cf..8a7ee62ef 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -381,6 +381,9 @@ export type Plugin = { src: string | null; version: string; ast: any[]; + author?: string; + description?: string; + permissions?: string[]; }; interface Watcher { From 98209be01ae4ec408e84df41c8c6f9d270792b68 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 23 Sep 2023 09:37:26 +0900 Subject: [PATCH 071/121] Update ja-JP.yml Fix #11870 --- locales/ja-JP.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 82ced0aa3..287895959 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2080,6 +2080,7 @@ _notification: _types: all: "すべて" + note: "ユーザーの新規投稿" follow: "フォロー" mention: "メンション" reply: "リプライ" From e8a098af62c7bd2f9953a814f7c43a64e6602343 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 23 Sep 2023 09:59:45 +0900 Subject: [PATCH 072/121] Update index.d.ts --- locales/index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/index.d.ts b/locales/index.d.ts index 256f17812..dbd485379 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2166,6 +2166,7 @@ export interface Locale { "notificationWillBeDisplayedLikeThis": string; "_types": { "all": string; + "note": string; "follow": string; "mention": string; "reply": string; From ad8ddbf12f9750446bb46cdb9a671f069f5bfd9f Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 23 Sep 2023 09:59:50 +0900 Subject: [PATCH 073/121] :art: --- packages/frontend/src/components/MkNoteDetailed.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 94c6833b1..2b61240b9 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -143,7 +143,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +