From f3a4434830ac4cc2d12f814f880d41ba7b81b87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B4=87=E5=B3=B0=20=E6=9C=94=E8=8F=AF?= <160555157+sakuhanight@users.noreply.github.com> Date: Sun, 16 Feb 2025 18:41:33 +0900 Subject: [PATCH 001/405] =?UTF-8?q?fix(backend):=20=E3=83=A1=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9=E3=81=AE=E5=BD=A2?= =?UTF-8?q?=E5=BC=8F=E3=81=8C=E6=AD=A3=E3=81=97=E3=81=8F=E3=81=AA=E3=81=91?= =?UTF-8?q?=E3=82=8C=E3=81=B0=E4=BB=A5=E9=99=8D=E3=81=AE=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=82=92=E8=A1=8C=E3=82=8F=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=20(#15320)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mod: バリデーションを追加 * 条件の修正 notつけわすれ * Update CHANGELOG.md --- CHANGELOG.md | 2 +- packages/backend/src/core/EmailService.ts | 7 +++++++ packages/backend/src/core/UtilityService.ts | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aebf6f0fd5..4146423c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ - Fix: オブジェクトストレージの設定でPrefixを設定していなかった場合nullまたは空文字になる問題を修正 - Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正 (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/886) - +- Fix: メールアドレスの形式が正しくなければ以降の処理を行わないように ## 2025.2.0 diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index da198d0e42..45d7ea11e4 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -164,6 +164,13 @@ export class EmailService { available: boolean; reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned' | 'network' | 'blacklist'; }> { + if (!this.utilityService.validateEmailFormat(emailAddress)) { + return { + available: false, + reason: 'format', + }; + } + const exist = await this.userProfilesRepository.countBy({ emailVerified: true, email: emailAddress, diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index fcb750d3bf..23fb928ac9 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -38,6 +38,14 @@ export class UtilityService { return this.punyHost(uri) === this.toPuny(this.config.host); } + // メールアドレスのバリデーションを行う + // https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address + @bindThis + public validateEmailFormat(email: string): boolean { + const regexp = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; + return regexp.test(email); + } + @bindThis public isBlockedHost(blockedHosts: string[], host: string | null): boolean { if (host == null) return false; From 5991282e23358cd2da6c4ab359b5ad18aee5243e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 16 Feb 2025 09:45:52 +0000 Subject: [PATCH 002/405] Bump version to 2025.2.1-alpha.0 --- CHANGELOG.md | 2 +- package.json | 2 +- packages/misskey-js/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4146423c9d..885c8270da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## 2025.2.1 ### General - Feat: アクセストークン発行時に通知するように diff --git a/package.json b/package.json index e84b2dcf02..222ca2e455 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.2.0", + "version": "2025.2.1-alpha.0", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index 1bf42b8e4b..d0db5904d2 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.2.0", + "version": "2025.2.1-alpha.0", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From bacc212f8191f7f523a882f1bd8077780cb2311c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 16 Feb 2025 18:58:13 +0900 Subject: [PATCH 003/405] fix(deps): update dependency bullmq to v5.41.1 (#15503) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/backend/package.json | 2 +- pnpm-lock.yaml | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 56cb906822..cee5c7205b 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -103,7 +103,7 @@ "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.3", - "bullmq": "5.41.0", + "bullmq": "5.41.1", "cacheable-lookup": "7.0.0", "cbor": "9.0.2", "chalk": "5.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b84559bc3..b69ec20dfa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -192,8 +192,8 @@ importers: specifier: 1.20.3 version: 1.20.3 bullmq: - specifier: 5.41.0 - version: 5.41.0 + specifier: 5.41.1 + version: 5.41.1 cacheable-lookup: specifier: 7.0.0 version: 7.0.0 @@ -5309,8 +5309,8 @@ packages: resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} engines: {node: '>=6.14.2'} - bullmq@5.41.0: - resolution: {integrity: sha512-GGfKu2DHGIvbnMtQjR/82wvWsdCaGxN5JGR3pvKd1mkDI9DsWn8r0+pAzZ6Y4ImWXFaetaAqywOhv2Ik0R2m3g==} + bullmq@5.41.1: + resolution: {integrity: sha512-bVQGR4ARM+wRJSw66114AKwO8SwS2ZF5TIvwQ9NL6Iepq6f8jnG8EjMMXL8J1pyR1eNz5bOikPeED/8ErLU6FQ==} buraha@0.0.1: resolution: {integrity: sha512-G563A0mTbzknm2jDaNxfZuNKIdeArs8T+XQN6t+KbmgnOoevXSXhKDkyf8Md/36Jrx99ikwbCag37VGe3myExQ==} @@ -10720,6 +10720,9 @@ packages: vue-component-type-helpers@2.2.2: resolution: {integrity: sha512-6lLY+n2xz2kCYshl59mL6gy8OUUTmkscmDFMO8i7Lj+QKwgnIFUZmM1i/iTYObtrczZVdw7UakPqDTGwVSGaRg==} + vue-component-type-helpers@2.2.2: + resolution: {integrity: sha512-6lLY+n2xz2kCYshl59mL6gy8OUUTmkscmDFMO8i7Lj+QKwgnIFUZmM1i/iTYObtrczZVdw7UakPqDTGwVSGaRg==} + vue-demi@0.14.7: resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} @@ -15792,7 +15795,7 @@ snapshots: node-gyp-build: 4.6.0 optional: true - bullmq@5.41.0: + bullmq@5.41.1: dependencies: cron-parser: 4.9.0 ioredis: 5.5.0 @@ -22113,6 +22116,8 @@ snapshots: vue-component-type-helpers@2.2.2: {} + vue-component-type-helpers@2.2.2: {} + vue-demi@0.14.7(vue@3.5.13(typescript@5.7.3)): dependencies: vue: 3.5.13(typescript@5.7.3) From b965240da4bf1228fa15d1060fb3c67d6df7f910 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: Sun, 16 Feb 2025 19:08:48 +0900 Subject: [PATCH 004/405] fix(deps): broken lockfile (#15508) --- pnpm-lock.yaml | 175 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 158 insertions(+), 17 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b69ec20dfa..7524559633 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -274,7 +274,7 @@ importers: version: 4.1.0 jsdom: specifier: 26.0.0 - version: 26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + version: 26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5) json5: specifier: 2.2.3 version: 2.2.3 @@ -958,7 +958,7 @@ importers: version: 8.24.0(eslint@9.20.1)(typescript@5.7.3) '@vitest/coverage-v8': specifier: 3.0.5 - version: 3.0.5(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)) + version: 3.0.5(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)) '@vue/runtime-core': specifier: 3.5.13 version: 3.5.13 @@ -1024,10 +1024,10 @@ importers: version: 1.0.3 vitest: specifier: 3.0.5 - version: 3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2) + version: 3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2) vitest-fetch-mock: specifier: 0.4.3 - version: 0.4.3(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)) + version: 0.4.3(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)) vue-component-type-helpers: specifier: 2.2.2 version: 2.2.2 @@ -1151,7 +1151,7 @@ importers: version: 8.24.0(eslint@9.20.1)(typescript@5.7.3) '@vitest/coverage-v8': specifier: 3.0.5 - version: 3.0.5(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)) + version: 3.0.5(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)) '@vue/runtime-core': specifier: 3.5.13 version: 3.5.13 @@ -5232,6 +5232,9 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} @@ -5392,6 +5395,10 @@ packages: canvas-confetti@1.9.3: resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==} + canvas@3.1.0: + resolution: {integrity: sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg==} + engines: {node: ^18.12.0 || >= 20.9.0} + caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -5490,6 +5497,9 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -5944,6 +5954,10 @@ packages: deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -6443,6 +6457,10 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expect-type@1.1.0: resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} engines: {node: '>=12.0.0'} @@ -6688,6 +6706,9 @@ packages: from@0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -6795,6 +6816,9 @@ packages: getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -8220,6 +8244,9 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -8310,6 +8337,9 @@ packages: engines: {node: ^18 || >=20} hasBin: true + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -9063,6 +9093,11 @@ packages: postgres-range@1.1.3: resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==} + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -9283,6 +9318,10 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + rdf-canonize@3.4.0: resolution: {integrity: sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==} engines: {node: '>=12'} @@ -9662,6 +9701,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-oauth2@5.1.0: resolution: {integrity: sha512-gWDa38Ccm4MwlG5U7AlcJxPv3lvr80dU7ARJWrGdgvOKyzSj1gr3GBPN1rABTedAYvC/LsGYoFuFxwDBPtGEbw==} @@ -10024,6 +10069,10 @@ packages: resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} engines: {node: '>=12'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -10079,6 +10128,13 @@ packages: os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true + tar-fs@2.1.2: + resolution: {integrity: sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} @@ -10720,9 +10776,6 @@ packages: vue-component-type-helpers@2.2.2: resolution: {integrity: sha512-6lLY+n2xz2kCYshl59mL6gy8OUUTmkscmDFMO8i7Lj+QKwgnIFUZmM1i/iTYObtrczZVdw7UakPqDTGwVSGaRg==} - vue-component-type-helpers@2.2.2: - resolution: {integrity: sha512-6lLY+n2xz2kCYshl59mL6gy8OUUTmkscmDFMO8i7Lj+QKwgnIFUZmM1i/iTYObtrczZVdw7UakPqDTGwVSGaRg==} - vue-demi@0.14.7: resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} @@ -14990,7 +15043,7 @@ snapshots: vite: 6.1.0(@types/node@22.13.4)(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2) vue: 3.5.13(typescript@5.7.3) - '@vitest/coverage-v8@3.0.5(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2))': + '@vitest/coverage-v8@3.0.5(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -15004,7 +15057,7 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2) + vitest: 3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2) transitivePeerDependencies: - supports-color @@ -15698,6 +15751,13 @@ snapshots: binary-extensions@2.2.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.0 + optional: true + blob-util@2.0.2: {} bluebird@3.7.2: {} @@ -15903,6 +15963,12 @@ snapshots: canvas-confetti@1.9.3: {} + canvas@3.1.0: + dependencies: + node-addon-api: 7.1.1 + prebuild-install: 7.1.3 + optional: true + caseless@0.12.0: {} cbor@9.0.2: @@ -16023,6 +16089,9 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chownr@1.1.4: + optional: true + chownr@2.0.0: {} chromatic@11.25.2: {} @@ -16523,6 +16592,9 @@ snapshots: which-collection: 1.0.1 which-typed-array: 1.1.11 + deep-extend@0.6.0: + optional: true + deep-is@0.1.4: {} deepmerge@4.2.2: {} @@ -17238,6 +17310,9 @@ snapshots: exit@0.1.2: {} + expand-template@2.0.3: + optional: true + expect-type@1.1.0: {} expect@29.7.0: @@ -17572,6 +17647,9 @@ snapshots: from@0.1.7: {} + fs-constants@1.0.0: + optional: true + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -17705,6 +17783,9 @@ snapshots: dependencies: assert-plus: 1.0.0 + github-from-package@0.0.0: + optional: true + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -18765,7 +18846,7 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} - jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): + jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5): dependencies: cssstyle: 4.2.1 data-urls: 5.0.0 @@ -18788,6 +18869,8 @@ snapshots: whatwg-url: 14.1.0 ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) xml-name-validator: 5.0.0 + optionalDependencies: + canvas: 3.1.0 transitivePeerDependencies: - bufferutil - supports-color @@ -19524,6 +19607,9 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + mkdirp-classic@0.5.3: + optional: true + mkdirp@0.5.6: dependencies: minimist: 1.2.8 @@ -19622,6 +19708,9 @@ snapshots: nanoid@5.1.0: {} + napi-build-utils@2.0.0: + optional: true + natural-compare@1.4.0: {} ncp@2.0.0: {} @@ -20353,6 +20442,22 @@ snapshots: postgres-range@1.1.3: {} + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.62.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.2 + tunnel-agent: 0.6.0 + optional: true + prelude-ls@1.2.1: {} prettier@3.5.1: {} @@ -20586,6 +20691,14 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + optional: true + rdf-canonize@3.4.0: dependencies: setimmediate: 1.0.5 @@ -21076,6 +21189,16 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: + optional: true + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + optional: true + simple-oauth2@5.1.0: dependencies: '@hapi/hoek': 11.0.4 @@ -21456,6 +21579,9 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-json-comments@2.0.1: + optional: true + strip-json-comments@3.1.1: {} strnum@1.0.5: {} @@ -21506,6 +21632,23 @@ snapshots: systeminformation@5.25.11: {} + tar-fs@2.1.2: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + optional: true + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + optional: true + tar-stream@3.1.6: dependencies: b4a: 1.6.4 @@ -22031,11 +22174,11 @@ snapshots: terser: 5.39.0 tsx: 4.19.2 - vitest-fetch-mock@0.4.3(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)): + vitest-fetch-mock@0.4.3(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)): dependencies: - vitest: 3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2) + vitest: 3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2) - vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2): + vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.13.4)(happy-dom@17.1.0)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2): dependencies: '@vitest/expect': 3.0.5 '@vitest/mocker': 3.0.5(msw@2.7.0(@types/node@22.13.4)(typescript@5.7.3))(vite@6.1.0(@types/node@22.13.4)(sass@1.85.0)(terser@5.39.0)(tsx@4.19.2)) @@ -22061,7 +22204,7 @@ snapshots: '@types/debug': 4.1.12 '@types/node': 22.13.4 happy-dom: 17.1.0 - jsdom: 26.0.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) + jsdom: 26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5) transitivePeerDependencies: - jiti - less @@ -22116,8 +22259,6 @@ snapshots: vue-component-type-helpers@2.2.2: {} - vue-component-type-helpers@2.2.2: {} - vue-demi@0.14.7(vue@3.5.13(typescript@5.7.3)): dependencies: vue: 3.5.13(typescript@5.7.3) From 9ffe504c7f75490822a3efdb39b70a8f0d046bcf Mon Sep 17 00:00:00 2001 From: claustra01 <108509532+claustra01@users.noreply.github.com> Date: Sun, 16 Feb 2025 19:34:50 +0900 Subject: [PATCH 005/405] =?UTF-8?q?enhance(frontend):=20CW=E3=81=AE?= =?UTF-8?q?=E6=B3=A8=E9=87=88=E3=81=A7=E5=85=A5=E5=8A=9B=E6=B8=88=E3=81=BF?= =?UTF-8?q?=E3=81=AE=E6=96=87=E5=AD=97=E6=95=B0=E3=82=92=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=99=E3=82=8B=20(#15070)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance: CW注釈の文字数表示 * update: CHANGELOG.md * chore: maxCwTextLengthをただのconstにする * fix: 投稿ボタンのdisable判定条件 --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --- CHANGELOG.md | 2 ++ .../frontend/src/components/MkPostForm.vue | 34 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 885c8270da..57100aaf3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,12 @@ - Enhance: 開発者モードでメニューからファイルIDをコピー出来るように `#15441' - Enhance: ノートに埋め込まれたメディアのコンテキストメニューから管理者用のファイル管理画面を開けるように ( #15440 ) - Enhance: リアクションする際に確認ダイアログを表示できるように +- Enhance: CWの注釈で入力済みの文字数を表示 - Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529` - Fix: 埋め込みプレイヤーから外部ページに移動できない問題を修正 - Fix: Play の再読込時に UI が以前の状態を引き継いでしまう問題を修正 `#14378` - Fix: カスタム絵文字管理画面(beta)にてisSensitive/localOnlyの絞り込みが上手くいかない問題の修正 ( #15445 ) +- Fix: CWの注釈が100文字を超えている場合、ノート投稿ボタンを非アクティブに ### Server - Fix: `following/invalidate`でフォロワーを解除しようとしているユーザーの情報を返すように diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 49ed4197de..ad0a332f99 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -65,7 +65,10 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.notSpecifiedMentionWarning }} - - +
+ +
{{ maxCwTextLength - cwTextLength }}
+
+
+
{{ file.name }}
+
+ + + +
+
+ +
+ + + + + diff --git a/packages/frontend/src/pages/chat/room.info.vue b/packages/frontend/src/pages/chat/room.info.vue new file mode 100644 index 0000000000..7d38d07b3a --- /dev/null +++ b/packages/frontend/src/pages/chat/room.info.vue @@ -0,0 +1,87 @@ + + + + + + + diff --git a/packages/frontend/src/pages/chat/room.members.vue b/packages/frontend/src/pages/chat/room.members.vue new file mode 100644 index 0000000000..d20216a81c --- /dev/null +++ b/packages/frontend/src/pages/chat/room.members.vue @@ -0,0 +1,73 @@ + + + + + + + diff --git a/packages/frontend/src/pages/chat/room.search.vue b/packages/frontend/src/pages/chat/room.search.vue new file mode 100644 index 0000000000..de5e7156ca --- /dev/null +++ b/packages/frontend/src/pages/chat/room.search.vue @@ -0,0 +1,68 @@ + + + + + + + diff --git a/packages/frontend/src/pages/chat/room.vue b/packages/frontend/src/pages/chat/room.vue new file mode 100644 index 0000000000..15e9f43db2 --- /dev/null +++ b/packages/frontend/src/pages/chat/room.vue @@ -0,0 +1,426 @@ + + + + + + + diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 530b63b701..93a41e9ddd 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -38,7 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.markAsReadAllNotifications }} - {{ i18n.ts.markAsReadAllUnreadNotes }}
@@ -93,10 +92,6 @@ const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrat const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadMessage || false); const userLists = await misskeyApi('users/lists/list'); -async function readAllUnreadNotes() { - await os.apiWithDialog('i/read-all-unread-notes'); -} - async function readAllNotifications() { await os.apiWithDialog('notifications/mark-all-as-read'); } diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index f6eb203095..2f8a697d74 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -78,6 +78,20 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + + + + + + + @@ -208,6 +222,7 @@ const hideOnlineStatus = ref($i.hideOnlineStatus); const publicReactions = ref($i.publicReactions); const followingVisibility = ref($i.followingVisibility); const followersVisibility = ref($i.followersVisibility); +const chatScope = ref($i.chatScope); const makeNotesFollowersOnlyBefore_type = computed(() => { if (makeNotesFollowersOnlyBefore.value == null) { @@ -260,6 +275,7 @@ function save() { publicReactions: !!publicReactions.value, followingVisibility: followingVisibility.value, followersVisibility: followersVisibility.value, + chatScope: chatScope.value, }); } diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue index 9e5c82a266..4461ee1ab1 100644 --- a/packages/frontend/src/pages/settings/sounds.vue +++ b/packages/frontend/src/pages/settings/sounds.vue @@ -85,6 +85,7 @@ const sounds = ref>>({ noteMy: prefer.r['sound.on.noteMy'], notification: prefer.r['sound.on.notification'], reaction: prefer.r['sound.on.reaction'], + chatMessage: prefer.r['sound.on.chatMessage'], }); function getSoundTypeName(f: SoundType): string { diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index c3c37553d7..127ebeef0c 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -136,6 +136,7 @@ export const PREF_DEF = { 'clips', 'drive', 'followRequests', + 'chat', '-', 'explore', 'announcements', @@ -331,6 +332,7 @@ export const PREF_DEF = { plugins: { default: [] as Plugin[], }, + 'sound.masterVolume': { default: 0.3, }, @@ -352,6 +354,10 @@ export const PREF_DEF = { 'sound.on.reaction': { default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore, }, + 'sound.on.chatMessage': { + default: { type: 'syuilo/waon', volume: 1 } as SoundStore, + }, + 'deck.alwaysShowMainColumn': { default: true, }, @@ -364,6 +370,7 @@ export const PREF_DEF = { 'deck.columnAlign': { default: 'left' as 'left' | 'right' | 'center', }, + 'game.dropAndFusion': { default: { bgmVolume: 0.25, diff --git a/packages/frontend/src/router.definition.ts b/packages/frontend/src/router.definition.ts index 3b60ee68e3..0585a31fd1 100644 --- a/packages/frontend/src/router.definition.ts +++ b/packages/frontend/src/router.definition.ts @@ -40,6 +40,22 @@ export const ROUTE_DEF = [{ }, { path: '/clips/:clipId', component: page(() => import('@/pages/clip.vue')), +}, { + path: '/chat', + component: page(() => import('@/pages/chat/home.vue')), + loginRequired: true, +}, { + path: '/chat/user/:userId', + component: page(() => import('@/pages/chat/room.vue')), + loginRequired: true, +}, { + path: '/chat/room/:roomId', + component: page(() => import('@/pages/chat/room.vue')), + loginRequired: true, +}, { + path: '/chat/messages/:messageId', + component: page(() => import('@/pages/chat/message.vue')), + loginRequired: true, }, { path: '/instance-info/:host', component: page(() => import('@/pages/instance-info.vue')), diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index 8c5617b72e..f122f47c06 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -113,10 +113,6 @@ a { outline-offset: 2px; } - &:hover { - text-decoration: underline; - } - &[target="_blank"] { -webkit-touch-callout: default; } @@ -335,13 +331,13 @@ rt { ._gaps_m { display: flex; flex-direction: column; - gap: 1.5em; + gap: 21px; } ._gaps_s { display: flex; flex-direction: column; - gap: 0.75em; + gap: 10px; } ._gaps { diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts index 5d1fc1fe72..820759ce61 100644 --- a/packages/frontend/src/types/menu.ts +++ b/packages/frontend/src/types/menu.ts @@ -15,16 +15,16 @@ export type MenuAction = (ev: MouseEvent) => void; export type MenuDivider = { type: 'divider' }; export type MenuNull = undefined; -export type MenuLabel = { type: 'label', text: string }; -export type MenuLink = { type: 'link', to: string, text: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User }; -export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, icon?: string, indicate?: boolean }; +export type MenuLabel = { type: 'label', text: string, caption?: string }; +export type MenuLink = { type: 'link', to: string, text: string, caption?: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User }; +export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, caption?: string, icon?: string, indicate?: boolean }; export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction }; -export type MenuSwitch = { type: 'switch', ref: Ref, text: string, icon?: string, disabled?: boolean | Ref }; -export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean | ComputedRef, avatar?: Misskey.entities.User; action: MenuAction }; -export type MenuRadio = { type: 'radio', text: string, icon?: string, ref: Ref, options: MenuRadioOptionsDef, disabled?: boolean | Ref }; -export type MenuRadioOption = { type: 'radioOption', text: string, action: MenuAction; active?: boolean | ComputedRef }; +export type MenuSwitch = { type: 'switch', ref: Ref, text: string, caption?: string, icon?: string, disabled?: boolean | Ref }; +export type MenuButton = { type?: 'button', text: string, caption?: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean | ComputedRef, avatar?: Misskey.entities.User; action: MenuAction }; +export type MenuRadio = { type: 'radio', text: string, caption?: string, icon?: string, ref: Ref, options: MenuRadioOptionsDef, disabled?: boolean | Ref }; +export type MenuRadioOption = { type: 'radioOption', text: string, caption?: string, action: MenuAction; active?: boolean | ComputedRef }; export type MenuComponent = { type: 'component', component: T, props?: ComponentProps }; -export type MenuParent = { type: 'parent', text: string, icon?: string, children: MenuItem[] | (() => Promise | MenuItem[]) }; +export type MenuParent = { type: 'parent', text: string, caption?: string, icon?: string, children: MenuItem[] | (() => Promise | MenuItem[]) }; export type MenuPending = { type: 'pending' }; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 98d6f329ab..88ce3a96e2 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> -
+
@@ -74,9 +74,9 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
+
+
+
diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index fd92876880..64fe328478 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -240,20 +240,25 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['explore', i18n.ts.makeExplorableDescription], }, { - id: '7vr04wKol', + id: 'xEYlOghao', + label: i18n.ts._chat.chatAllowedUsers, + keywords: ['chat'], + }, + { + id: 'BnOtlyaAh', children: [ { - id: 'Av7fAaHv8', + id: 'BzMIVBpL0', label: i18n.ts._accountSettings.requireSigninToViewContents, keywords: ['login', 'signin'], }, { - id: '5RbESWefG', + id: 'jJUqPqBAv', label: i18n.ts._accountSettings.makeNotesFollowersOnlyBefore, keywords: ['follower', i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription], }, { - id: 'hdzwDs3qd', + id: 'ra10txIFV', label: i18n.ts._accountSettings.makeNotesHiddenBefore, keywords: ['hidden', i18n.ts._accountSettings.makeNotesHiddenBeforeDescription], }, diff --git a/packages/frontend/src/utility/get-user-menu.ts b/packages/frontend/src/utility/get-user-menu.ts index de20f2678e..37c88c9665 100644 --- a/packages/frontend/src/utility/get-user-menu.ts +++ b/packages/frontend/src/utility/get-user-menu.ts @@ -151,6 +151,16 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router const menuItems: MenuItem[] = []; + if (iAmModerator) { + menuItems.push({ + icon: 'ti ti-user-exclamation', + text: i18n.ts.moderation, + action: () => { + router.push(`/admin/user/${user.id}`); + }, + }, { type: 'divider' }); + } + menuItems.push({ icon: 'ti ti-at', text: i18n.ts.copyUsername, @@ -159,25 +169,14 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router }, }); - if (notesSearchAvailable && (user.host == null || canSearchNonLocalNotes)) { - menuItems.push({ - icon: 'ti ti-search', - text: i18n.ts.searchThisUsersNotes, - action: () => { - router.push(`/search?username=${encodeURIComponent(user.username)}${user.host != null ? '&host=' + encodeURIComponent(user.host) : ''}`); - }, - }); - } - - if (iAmModerator) { - menuItems.push({ - icon: 'ti ti-user-exclamation', - text: i18n.ts.moderation, - action: () => { - router.push(`/admin/user/${user.id}`); - }, - }); - } + menuItems.push({ + icon: 'ti ti-share', + text: i18n.ts.copyProfileUrl, + action: () => { + const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`; + copyToClipboard(`${url}/${canonical}`); + }, + }); menuItems.push({ icon: 'ti ti-rss', @@ -210,24 +209,18 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router }); } - menuItems.push({ - icon: 'ti ti-share', - text: i18n.ts.copyProfileUrl, - action: () => { - const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`; - copyToClipboard(`${url}/${canonical}`); - }, - }); + if (notesSearchAvailable && (user.host == null || canSearchNonLocalNotes)) { + menuItems.push({ + icon: 'ti ti-search', + text: i18n.ts.searchThisUsersNotes, + action: () => { + router.push(`/search?username=${encodeURIComponent(user.username)}${user.host != null ? '&host=' + encodeURIComponent(user.host) : ''}`); + }, + }); + } if ($i) { - menuItems.push({ - icon: 'ti ti-mail', - text: i18n.ts.sendMessage, - action: () => { - const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`; - os.post({ specified: user, initialText: `${canonical} ` }); - }, - }, { type: 'divider' }, { + menuItems.push({ type: 'divider' }, { icon: 'ti ti-pencil', text: i18n.ts.editMemo, action: editMemo, @@ -363,6 +356,18 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router //} menuItems.push({ type: 'divider' }, { + icon: 'ti ti-mail', + text: i18n.ts.sendMessage, + action: () => { + const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`; + os.post({ specified: user, initialText: `${canonical} ` }); + }, + }, { + type: 'link', + icon: 'ti ti-messages', + text: i18n.ts._chat.chatWithThisUser, + to: `/chat/user/${user.id}`, + }, { type: 'divider' }, { icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off', text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute, action: toggleMute, diff --git a/packages/frontend/src/utility/sound.ts b/packages/frontend/src/utility/sound.ts index 796af0e5ca..f217bdfcd5 100644 --- a/packages/frontend/src/utility/sound.ts +++ b/packages/frontend/src/utility/sound.ts @@ -77,6 +77,7 @@ export const operationTypes = [ 'note', 'notification', 'reaction', + 'chatMessage', ] as const; /** サウンドの種類 */ diff --git a/packages/frontend/src/utility/upload.ts b/packages/frontend/src/utility/upload.ts index eb3cbd3dfa..e13d793ffb 100644 --- a/packages/frontend/src/utility/upload.ts +++ b/packages/frontend/src/utility/upload.ts @@ -32,7 +32,7 @@ const mimeTypeMap = { export function uploadFile( file: File, - folder?: string | Misskey.entities.DriveFolder, + folder?: string | Misskey.entities.DriveFolder | null, name?: string, keepOriginal: boolean = prefer.s.keepOriginalUploading, ): Promise { diff --git a/packages/misskey-js/README.md b/packages/misskey-js/README.md index 4753e2434b..5ab2787c47 100644 --- a/packages/misskey-js/README.md +++ b/packages/misskey-js/README.md @@ -83,8 +83,8 @@ const mainChannel = stream.useChannel('main'); ``` ts const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' }); -const messagingChannel = stream.useChannel('messaging', { - otherparty: 'xxxxxxxxxx', +const chatChannel = stream.useChannel('chat', { + other: 'xxxxxxxxxx', }); ``` @@ -115,11 +115,11 @@ mainChannel.on('notification', notification => { ``` ts const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' }); -const messagingChannel = stream.useChannel('messaging', { - otherparty: 'xxxxxxxxxx', +const chatChannel = stream.useChannel('chat', { + other: 'xxxxxxxxxx', }); -messagingChannel.send('read', { +chatChannel.send('read', { id: 'xxxxxxxxxx' }); ``` diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 6060812b53..e79cd794a6 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -619,13 +619,9 @@ export type Channels = { }) => void; readAllNotifications: () => void; unreadNotification: (payload: Notification_2) => void; - unreadMention: (payload: Note['id']) => void; - readAllUnreadMentions: () => void; notificationFlushed: () => void; - unreadSpecifiedNote: (payload: Note['id']) => void; - readAllUnreadSpecifiedNotes: () => void; - readAllAntennas: () => void; unreadAntenna: (payload: Antenna) => void; + newChatMessage: (payload: ChatMessage) => void; readAllAnnouncements: () => void; myTokenRegenerated: () => void; signin: (payload: Signin) => void; @@ -951,6 +947,153 @@ type ChartsUsersRequest = operations['charts___users']['requestBody']['content'] // @public (undocumented) type ChartsUsersResponse = operations['charts___users']['responses']['200']['content']['application/json']; +// @public (undocumented) +type ChatHistoryRequest = operations['chat___history']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatHistoryResponse = operations['chat___history']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessage = components['schemas']['ChatMessage']; + +// @public (undocumented) +type ChatMessageLite = components['schemas']['ChatMessageLite']; + +// @public (undocumented) +type ChatMessagesCreateToRoomRequest = operations['chat___messages___create-to-room']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesCreateToRoomResponse = operations['chat___messages___create-to-room']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesCreateToUserRequest = operations['chat___messages___create-to-user']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-user']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesRoomTimelineResponse = operations['chat___messages___room-timeline']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesSearchRequest = operations['chat___messages___search']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesSearchResponse = operations['chat___messages___search']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatMessagesUserTimelineResponse = operations['chat___messages___user-timeline']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoom = components['schemas']['ChatRoom']; + +// @public (undocumented) +type ChatRoomInvitation = components['schemas']['ChatRoomInvitation']; + +// @public (undocumented) +type ChatRoomMembership = components['schemas']['ChatRoomMembership']; + +// @public (undocumented) +type ChatRoomsCreateRequest = operations['chat___rooms___create']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsOwnedResponse = operations['chat___rooms___owned']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsShowRequest = operations['chat___rooms___show']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsShowResponse = operations['chat___rooms___show']['responses']['200']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsUpdateRequest = operations['chat___rooms___update']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsUpdateResponse = operations['chat___rooms___update']['responses']['200']['content']['application/json']; + // @public (undocumented) type Clip = components['schemas']['Clip']; @@ -1448,6 +1591,50 @@ declare namespace entities { ChartsUserReactionsResponse, ChartsUsersRequest, ChartsUsersResponse, + ChatHistoryRequest, + ChatHistoryResponse, + ChatMessagesCreateToRoomRequest, + ChatMessagesCreateToRoomResponse, + ChatMessagesCreateToUserRequest, + ChatMessagesCreateToUserResponse, + ChatMessagesDeleteRequest, + ChatMessagesDeleteResponse, + ChatMessagesReactRequest, + ChatMessagesReactResponse, + ChatMessagesRoomTimelineRequest, + ChatMessagesRoomTimelineResponse, + ChatMessagesSearchRequest, + ChatMessagesSearchResponse, + ChatMessagesShowRequest, + ChatMessagesShowResponse, + ChatMessagesUserTimelineRequest, + ChatMessagesUserTimelineResponse, + ChatRoomsCreateRequest, + ChatRoomsCreateResponse, + ChatRoomsDeleteRequest, + ChatRoomsDeleteResponse, + ChatRoomsInvitationsCreateRequest, + ChatRoomsInvitationsCreateResponse, + ChatRoomsInvitationsIgnoreRequest, + ChatRoomsInvitationsIgnoreResponse, + ChatRoomsInvitationsInboxRequest, + ChatRoomsInvitationsInboxResponse, + ChatRoomsJoinRequest, + ChatRoomsJoinResponse, + ChatRoomsJoiningRequest, + ChatRoomsJoiningResponse, + ChatRoomsLeaveRequest, + ChatRoomsLeaveResponse, + ChatRoomsMembersRequest, + ChatRoomsMembersResponse, + ChatRoomsMuteRequest, + ChatRoomsMuteResponse, + ChatRoomsOwnedRequest, + ChatRoomsOwnedResponse, + ChatRoomsShowRequest, + ChatRoomsShowResponse, + ChatRoomsUpdateRequest, + ChatRoomsUpdateResponse, ClipsAddNoteRequest, ClipsCreateRequest, ClipsCreateResponse, @@ -1880,7 +2067,12 @@ declare namespace entities { MetaDetailedOnly, MetaDetailed, SystemWebhook, - AbuseReportNotificationRecipient + AbuseReportNotificationRecipient, + ChatMessage, + ChatMessageLite, + ChatRoom, + ChatRoomInvitation, + ChatRoomMembership } } export { entities } @@ -2903,7 +3095,7 @@ type PartialRolePolicyOverride = Partial<{ }>; // @public (undocumented) -export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"]; +export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse", "write:chat", "read:chat"]; // @public (undocumented) type PingResponse = operations['ping']['responses']['200']['content']['application/json']; @@ -3444,8 +3636,8 @@ type V2AdminEmojiListResponse = operations['v2___admin___emoji___list']['respons // // src/entities.ts:50:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.ts:57:3 - (ae-forgotten-export) The symbol "ReconnectingWebSocket" needs to be exported by the entry point index.d.ts -// src/streaming.types.ts:220:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts -// src/streaming.types.ts:230:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts +// src/streaming.types.ts:217:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts +// src/streaming.types.ts:227:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index a1543952fc..e12ba0d6b2 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1534,6 +1534,248 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * @@ -2827,17 +3069,6 @@ declare module '../api.js' { credential?: string | null, ): Promise>; - /** - * No description provided. - * - * **Credential required**: *Yes* / **Permission**: *write:account* - */ - request( - endpoint: E, - params: P, - credential?: string | null, - ): Promise>; - /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 9bb8fb2225..558f4ab514 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -207,6 +207,50 @@ import type { ChartsUserReactionsResponse, ChartsUsersRequest, ChartsUsersResponse, + ChatHistoryRequest, + ChatHistoryResponse, + ChatMessagesCreateToRoomRequest, + ChatMessagesCreateToRoomResponse, + ChatMessagesCreateToUserRequest, + ChatMessagesCreateToUserResponse, + ChatMessagesDeleteRequest, + ChatMessagesDeleteResponse, + ChatMessagesReactRequest, + ChatMessagesReactResponse, + ChatMessagesRoomTimelineRequest, + ChatMessagesRoomTimelineResponse, + ChatMessagesSearchRequest, + ChatMessagesSearchResponse, + ChatMessagesShowRequest, + ChatMessagesShowResponse, + ChatMessagesUserTimelineRequest, + ChatMessagesUserTimelineResponse, + ChatRoomsCreateRequest, + ChatRoomsCreateResponse, + ChatRoomsDeleteRequest, + ChatRoomsDeleteResponse, + ChatRoomsInvitationsCreateRequest, + ChatRoomsInvitationsCreateResponse, + ChatRoomsInvitationsIgnoreRequest, + ChatRoomsInvitationsIgnoreResponse, + ChatRoomsInvitationsInboxRequest, + ChatRoomsInvitationsInboxResponse, + ChatRoomsJoinRequest, + ChatRoomsJoinResponse, + ChatRoomsJoiningRequest, + ChatRoomsJoiningResponse, + ChatRoomsLeaveRequest, + ChatRoomsLeaveResponse, + ChatRoomsMembersRequest, + ChatRoomsMembersResponse, + ChatRoomsMuteRequest, + ChatRoomsMuteResponse, + ChatRoomsOwnedRequest, + ChatRoomsOwnedResponse, + ChatRoomsShowRequest, + ChatRoomsShowResponse, + ChatRoomsUpdateRequest, + ChatRoomsUpdateResponse, ClipsAddNoteRequest, ClipsCreateRequest, ClipsCreateResponse, @@ -726,6 +770,28 @@ export type Endpoints = { 'charts/user/pv': { req: ChartsUserPvRequest; res: ChartsUserPvResponse }; 'charts/user/reactions': { req: ChartsUserReactionsRequest; res: ChartsUserReactionsResponse }; 'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse }; + 'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse }; + 'chat/messages/create-to-room': { req: ChatMessagesCreateToRoomRequest; res: ChatMessagesCreateToRoomResponse }; + 'chat/messages/create-to-user': { req: ChatMessagesCreateToUserRequest; res: ChatMessagesCreateToUserResponse }; + 'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse }; + 'chat/messages/react': { req: ChatMessagesReactRequest; res: ChatMessagesReactResponse }; + 'chat/messages/room-timeline': { req: ChatMessagesRoomTimelineRequest; res: ChatMessagesRoomTimelineResponse }; + 'chat/messages/search': { req: ChatMessagesSearchRequest; res: ChatMessagesSearchResponse }; + 'chat/messages/show': { req: ChatMessagesShowRequest; res: ChatMessagesShowResponse }; + 'chat/messages/user-timeline': { req: ChatMessagesUserTimelineRequest; res: ChatMessagesUserTimelineResponse }; + 'chat/rooms/create': { req: ChatRoomsCreateRequest; res: ChatRoomsCreateResponse }; + 'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: ChatRoomsDeleteResponse }; + 'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse }; + 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: ChatRoomsInvitationsIgnoreResponse }; + 'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse }; + 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse }; + 'chat/rooms/joining': { req: ChatRoomsJoiningRequest; res: ChatRoomsJoiningResponse }; + 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse }; + 'chat/rooms/members': { req: ChatRoomsMembersRequest; res: ChatRoomsMembersResponse }; + 'chat/rooms/mute': { req: ChatRoomsMuteRequest; res: ChatRoomsMuteResponse }; + 'chat/rooms/owned': { req: ChatRoomsOwnedRequest; res: ChatRoomsOwnedResponse }; + 'chat/rooms/show': { req: ChatRoomsShowRequest; res: ChatRoomsShowResponse }; + 'chat/rooms/update': { req: ChatRoomsUpdateRequest; res: ChatRoomsUpdateResponse }; 'clips/add-note': { req: ClipsAddNoteRequest; res: EmptyResponse }; 'clips/create': { req: ClipsCreateRequest; res: ClipsCreateResponse }; 'clips/delete': { req: ClipsDeleteRequest; res: EmptyResponse }; @@ -841,7 +907,6 @@ export type Endpoints = { 'i/page-likes': { req: IPageLikesRequest; res: IPageLikesResponse }; 'i/pages': { req: IPagesRequest; res: IPagesResponse }; 'i/pin': { req: IPinRequest; res: IPinResponse }; - 'i/read-all-unread-notes': { req: EmptyRequest; res: EmptyResponse }; 'i/read-announcement': { req: IReadAnnouncementRequest; res: EmptyResponse }; 'i/regenerate-token': { req: IRegenerateTokenRequest; res: EmptyResponse }; 'i/registry/get': { req: IRegistryGetRequest; res: IRegistryGetResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index f3d26efa69..bb21272e01 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -210,6 +210,50 @@ export type ChartsUserReactionsRequest = operations['charts___user___reactions'] export type ChartsUserReactionsResponse = operations['charts___user___reactions']['responses']['200']['content']['application/json']; export type ChartsUsersRequest = operations['charts___users']['requestBody']['content']['application/json']; export type ChartsUsersResponse = operations['charts___users']['responses']['200']['content']['application/json']; +export type ChatHistoryRequest = operations['chat___history']['requestBody']['content']['application/json']; +export type ChatHistoryResponse = operations['chat___history']['responses']['200']['content']['application/json']; +export type ChatMessagesCreateToRoomRequest = operations['chat___messages___create-to-room']['requestBody']['content']['application/json']; +export type ChatMessagesCreateToRoomResponse = operations['chat___messages___create-to-room']['responses']['200']['content']['application/json']; +export type ChatMessagesCreateToUserRequest = operations['chat___messages___create-to-user']['requestBody']['content']['application/json']; +export type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-user']['responses']['200']['content']['application/json']; +export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json']; +export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json']; +export type ChatMessagesReactRequest = operations['chat___messages___react']['requestBody']['content']['application/json']; +export type ChatMessagesReactResponse = operations['chat___messages___react']['responses']['200']['content']['application/json']; +export type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json']; +export type ChatMessagesRoomTimelineResponse = operations['chat___messages___room-timeline']['responses']['200']['content']['application/json']; +export type ChatMessagesSearchRequest = operations['chat___messages___search']['requestBody']['content']['application/json']; +export type ChatMessagesSearchResponse = operations['chat___messages___search']['responses']['200']['content']['application/json']; +export type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json']; +export type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json']; +export type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json']; +export type ChatMessagesUserTimelineResponse = operations['chat___messages___user-timeline']['responses']['200']['content']['application/json']; +export type ChatRoomsCreateRequest = operations['chat___rooms___create']['requestBody']['content']['application/json']; +export type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses']['200']['content']['application/json']; +export type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json']; +export type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json']; +export type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json']; +export type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json']; +export type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json']; +export type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; +export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; +export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; +export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; +export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; +export type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; +export type ChatRoomsJoiningResponse = operations['chat___rooms___joining']['responses']['200']['content']['application/json']; +export type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json']; +export type ChatRoomsLeaveResponse = operations['chat___rooms___leave']['responses']['200']['content']['application/json']; +export type ChatRoomsMembersRequest = operations['chat___rooms___members']['requestBody']['content']['application/json']; +export type ChatRoomsMembersResponse = operations['chat___rooms___members']['responses']['200']['content']['application/json']; +export type ChatRoomsMuteRequest = operations['chat___rooms___mute']['requestBody']['content']['application/json']; +export type ChatRoomsMuteResponse = operations['chat___rooms___mute']['responses']['200']['content']['application/json']; +export type ChatRoomsOwnedRequest = operations['chat___rooms___owned']['requestBody']['content']['application/json']; +export type ChatRoomsOwnedResponse = operations['chat___rooms___owned']['responses']['200']['content']['application/json']; +export type ChatRoomsShowRequest = operations['chat___rooms___show']['requestBody']['content']['application/json']; +export type ChatRoomsShowResponse = operations['chat___rooms___show']['responses']['200']['content']['application/json']; +export type ChatRoomsUpdateRequest = operations['chat___rooms___update']['requestBody']['content']['application/json']; +export type ChatRoomsUpdateResponse = operations['chat___rooms___update']['responses']['200']['content']['application/json']; export type ClipsAddNoteRequest = operations['clips___add-note']['requestBody']['content']['application/json']; export type ClipsCreateRequest = operations['clips___create']['requestBody']['content']['application/json']; export type ClipsCreateResponse = operations['clips___create']['responses']['200']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts index 1a30da4437..0ff9749602 100644 --- a/packages/misskey-js/src/autogen/models.ts +++ b/packages/misskey-js/src/autogen/models.ts @@ -54,3 +54,8 @@ export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly']; export type MetaDetailed = components['schemas']['MetaDetailed']; export type SystemWebhook = components['schemas']['SystemWebhook']; export type AbuseReportNotificationRecipient = components['schemas']['AbuseReportNotificationRecipient']; +export type ChatMessage = components['schemas']['ChatMessage']; +export type ChatMessageLite = components['schemas']['ChatMessageLite']; +export type ChatRoom = components['schemas']['ChatRoom']; +export type ChatRoomInvitation = components['schemas']['ChatRoomInvitation']; +export type ChatRoomMembership = components['schemas']['ChatRoomMembership']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 743aaf1608..31bc34e473 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -1358,6 +1358,204 @@ export type paths = { */ post: operations['charts___users']; }; + '/chat/history': { + /** + * chat/history + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___history']; + }; + '/chat/messages/create-to-room': { + /** + * chat/messages/create-to-room + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___messages___create-to-room']; + }; + '/chat/messages/create-to-user': { + /** + * chat/messages/create-to-user + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___messages___create-to-user']; + }; + '/chat/messages/delete': { + /** + * chat/messages/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___messages___delete']; + }; + '/chat/messages/react': { + /** + * chat/messages/react + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___messages___react']; + }; + '/chat/messages/room-timeline': { + /** + * chat/messages/room-timeline + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___messages___room-timeline']; + }; + '/chat/messages/search': { + /** + * chat/messages/search + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___messages___search']; + }; + '/chat/messages/show': { + /** + * chat/messages/show + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___messages___show']; + }; + '/chat/messages/user-timeline': { + /** + * chat/messages/user-timeline + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___messages___user-timeline']; + }; + '/chat/rooms/create': { + /** + * chat/rooms/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___create']; + }; + '/chat/rooms/delete': { + /** + * chat/rooms/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___delete']; + }; + '/chat/rooms/invitations/create': { + /** + * chat/rooms/invitations/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___invitations___create']; + }; + '/chat/rooms/invitations/ignore': { + /** + * chat/rooms/invitations/ignore + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___invitations___ignore']; + }; + '/chat/rooms/invitations/inbox': { + /** + * chat/rooms/invitations/inbox + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___rooms___invitations___inbox']; + }; + '/chat/rooms/join': { + /** + * chat/rooms/join + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___join']; + }; + '/chat/rooms/joining': { + /** + * chat/rooms/joining + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___rooms___joining']; + }; + '/chat/rooms/leave': { + /** + * chat/rooms/leave + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___leave']; + }; + '/chat/rooms/members': { + /** + * chat/rooms/members + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___members']; + }; + '/chat/rooms/mute': { + /** + * chat/rooms/mute + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___mute']; + }; + '/chat/rooms/owned': { + /** + * chat/rooms/owned + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___rooms___owned']; + }; + '/chat/rooms/show': { + /** + * chat/rooms/show + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___rooms___show']; + }; + '/chat/rooms/update': { + /** + * chat/rooms/update + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + post: operations['chat___rooms___update']; + }; '/clips/add-note': { /** * clips/add-note @@ -2470,15 +2668,6 @@ export type paths = { */ post: operations['i___pin']; }; - '/i/read-all-unread-notes': { - /** - * i/read-all-unread-notes - * @description No description provided. - * - * **Credential required**: *Yes* / **Permission**: *write:account* - */ - post: operations['i___read-all-unread-notes']; - }; '/i/read-announcement': { /** * i/read-announcement @@ -3848,6 +4037,8 @@ export type components = { followingVisibility: 'public' | 'followers' | 'private'; /** @enum {string} */ followersVisibility: 'public' | 'followers' | 'private'; + /** @enum {string} */ + chatScope: 'everyone' | 'following' | 'followers' | 'mutual' | 'none'; roles: components['schemas']['RoleLite'][]; followedMessage?: string | null; memo: string | null; @@ -3894,6 +4085,7 @@ export type components = { unreadAnnouncements: components['schemas']['Announcement'][]; hasUnreadAntenna: boolean; hasUnreadChannel: boolean; + hasUnreadChatMessages: boolean; hasUnreadNotification: boolean; hasPendingReceivedFollowRequest: boolean; unreadNotificationsCount: number; @@ -4947,6 +5139,7 @@ export type components = { canImportFollowing: boolean; canImportMuting: boolean; canImportUserLists: boolean; + canChat: boolean; }; ReversiGameLite: { /** Format: id */ @@ -5146,6 +5339,69 @@ export type components = { systemWebhookId?: string; systemWebhook?: components['schemas']['SystemWebhook']; }; + ChatMessage: { + id: string; + /** Format: date-time */ + createdAt: string; + fromUserId: string; + fromUser: components['schemas']['UserLite']; + toUserId?: string | null; + toUser?: components['schemas']['UserLite'] | null; + toRoomId?: string | null; + toRoom?: components['schemas']['ChatRoom'] | null; + text?: string | null; + fileId?: string | null; + file?: components['schemas']['DriveFile'] | null; + isRead?: boolean; + reactions: ({ + reaction: string; + user?: components['schemas']['UserLite'] | null; + })[]; + }; + ChatMessageLite: { + id: string; + /** Format: date-time */ + createdAt: string; + fromUserId: string; + fromUser?: components['schemas']['UserLite']; + toUserId?: string | null; + toRoomId?: string | null; + text?: string | null; + fileId?: string | null; + file?: components['schemas']['DriveFile'] | null; + reactions: ({ + reaction: string; + user?: components['schemas']['UserLite'] | null; + })[]; + }; + ChatRoom: { + id: string; + /** Format: date-time */ + createdAt: string; + ownerId: string; + owner: components['schemas']['UserLite']; + name: string; + description: string; + isMuted?: boolean; + }; + ChatRoomInvitation: { + id: string; + /** Format: date-time */ + createdAt: string; + userId: string; + user: components['schemas']['UserLite']; + roomId: string; + room: components['schemas']['ChatRoom']; + }; + ChatRoomMembership: { + id: string; + /** Format: date-time */ + createdAt: string; + userId: string; + user?: components['schemas']['UserLite']; + roomId: string; + room?: components['schemas']['ChatRoom']; + }; }; responses: never; parameters: never; @@ -13669,6 +13925,1267 @@ export type operations = { }; }; }; + /** + * chat/history + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___history: { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** @default false */ + room?: boolean; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatMessage'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/create-to-room + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + 'chat___messages___create-to-room': { + requestBody: { + content: { + 'application/json': { + text?: string | null; + /** Format: misskey:id */ + fileId?: string; + /** Format: misskey:id */ + toRoomId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatMessageLite']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Too many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/create-to-user + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + 'chat___messages___create-to-user': { + requestBody: { + content: { + 'application/json': { + text?: string | null; + /** Format: misskey:id */ + fileId?: string; + /** Format: misskey:id */ + toUserId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatMessageLite']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Too many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___messages___delete: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + messageId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/react + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___messages___react: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + messageId: string; + reaction: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/room-timeline + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + 'chat___messages___room-timeline': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** Format: misskey:id */ + roomId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatMessageLite'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/search + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___messages___search: { + requestBody: { + content: { + 'application/json': { + query: string; + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + userId?: string | null; + /** Format: misskey:id */ + roomId?: string | null; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatMessage'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/show + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___messages___show: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + messageId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatMessage']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/messages/user-timeline + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + 'chat___messages___user-timeline': { + requestBody: { + content: { + 'application/json': { + /** @default 10 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + /** Format: misskey:id */ + userId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatMessageLite'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___create: { + requestBody: { + content: { + 'application/json': { + name: string; + description?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoom']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Too many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/delete + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___delete: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/invitations/create + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___invitations___create: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + /** Format: misskey:id */ + userId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoomInvitation']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Too many requests */ + 429: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/invitations/ignore + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___invitations___ignore: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/invitations/inbox + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___rooms___invitations___inbox: { + requestBody: { + content: { + 'application/json': { + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoomInvitation'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/join + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___join: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/joining + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___rooms___joining: { + requestBody: { + content: { + 'application/json': { + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoomMembership'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/leave + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___leave: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/members + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___members: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoomMembership'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/mute + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___mute: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + mute: boolean; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': unknown; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/owned + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___rooms___owned: { + requestBody: { + content: { + 'application/json': { + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoom'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/show + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___rooms___show: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoom']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; + /** + * chat/rooms/update + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:chat* + */ + chat___rooms___update: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + name?: string; + description?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoom']; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * clips/add-note * @description No description provided. @@ -20279,50 +21796,6 @@ export type operations = { }; }; }; - /** - * i/read-all-unread-notes - * @description No description provided. - * - * **Credential required**: *Yes* / **Permission**: *write:account* - */ - 'i___read-all-unread-notes': { - responses: { - /** @description OK (without any results) */ - 204: { - content: never; - }; - /** @description Client error */ - 400: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Authentication error */ - 401: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Forbidden error */ - 403: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description I'm Ai */ - 418: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description Internal server error */ - 500: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - }; - }; /** * i/read-announcement * @description No description provided. @@ -21089,6 +22562,8 @@ export type operations = { followingVisibility?: 'public' | 'followers' | 'private'; /** @enum {string} */ followersVisibility?: 'public' | 'followers' | 'private'; + /** @enum {string} */ + chatScope?: 'everyone' | 'followers' | 'following' | 'mutual' | 'none'; /** Format: misskey:id */ pinnedPageId?: string | null; mutedWords?: (string[] | string)[]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index c5911a70eb..9d0fe5e781 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -37,8 +37,8 @@ export const permissions = [ 'write:favorites', 'read:following', 'write:following', - 'read:messaging', - 'write:messaging', + 'read:messaging', // deprecated + 'write:messaging', // deprecated 'read:mutes', 'write:mutes', 'write:notes', @@ -110,6 +110,8 @@ export const permissions = [ 'read:clip-favorite', 'read:federation', 'write:report-abuse', + 'write:chat', + 'read:chat', ] as const; export const moderationLogTypes = [ diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts index 26a50f9fa4..8e5438eeb6 100644 --- a/packages/misskey-js/src/streaming.types.ts +++ b/packages/misskey-js/src/streaming.types.ts @@ -1,5 +1,6 @@ import { Antenna, + ChatMessage, DriveFile, DriveFolder, Note, @@ -46,13 +47,9 @@ export type Channels = { urlUploadFinished: (payload: { marker: string; file: DriveFile; }) => void; readAllNotifications: () => void; unreadNotification: (payload: Notification) => void; - unreadMention: (payload: Note['id']) => void; - readAllUnreadMentions: () => void; notificationFlushed: () => void; - unreadSpecifiedNote: (payload: Note['id']) => void; - readAllUnreadSpecifiedNotes: () => void; - readAllAntennas: () => void; unreadAntenna: (payload: Antenna) => void; + newChatMessage: (payload: ChatMessage) => void; readAllAnnouncements: () => void; myTokenRegenerated: () => void; signin: (payload: Signin) => void; diff --git a/packages/misskey-js/test/streaming.ts b/packages/misskey-js/test/streaming.ts index 06b55cd8af..7e784cd20c 100644 --- a/packages/misskey-js/test/streaming.ts +++ b/packages/misskey-js/test/streaming.ts @@ -42,26 +42,26 @@ describe('Streaming', () => { test('useChannel with parameters', async () => { const server = new WS('wss://misskey.test/streaming'); const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); - const messagingChannelReceived: any[] = []; - const messaging = stream.useChannel('messaging', { otherparty: 'aaa' }); - messaging.on('message', payload => { - messagingChannelReceived.push(payload); + const chatChannelReceived: any[] = []; + const chat = stream.useChannel('chat', { other: 'aaa' }); + chat.on('message', payload => { + chatChannelReceived.push(payload); }); const ws = await server.connected; expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); const msg = JSON.parse(await server.nextMessage as string); - const messagingChannelId = msg.body.id; + const chatChannelId = msg.body.id; expect(msg.type).toEqual('connect'); - expect(msg.body.channel).toEqual('messaging'); - expect(msg.body.params).toEqual({ otherparty: 'aaa' }); - expect(messagingChannelId != null).toEqual(true); + expect(msg.body.channel).toEqual('chat'); + expect(msg.body.params).toEqual({ other: 'aaa' }); + expect(chatChannelId != null).toEqual(true); server.send(JSON.stringify({ type: 'channel', body: { - id: messagingChannelId, + id: chatChannelId, type: 'message', body: { id: 'foo' @@ -69,7 +69,7 @@ describe('Streaming', () => { } })); - expect(messagingChannelReceived[0]).toEqual({ + expect(chatChannelReceived[0]).toEqual({ id: 'foo' }); @@ -81,20 +81,20 @@ describe('Streaming', () => { const server = new WS('wss://misskey.test/streaming'); const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); - stream.useChannel('messaging', { otherparty: 'aaa' }); - stream.useChannel('messaging', { otherparty: 'bbb' }); + stream.useChannel('chat', { other: 'aaa' }); + stream.useChannel('chat', { other: 'bbb' }); const ws = await server.connected; expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); const msg = JSON.parse(await server.nextMessage as string); - const messagingChannelId = msg.body.id; + const chatChannelId = msg.body.id; const msg2 = JSON.parse(await server.nextMessage as string); - const messagingChannelId2 = msg2.body.id; + const chatChannelId2 = msg2.body.id; - expect(messagingChannelId != null).toEqual(true); - expect(messagingChannelId2 != null).toEqual(true); - expect(messagingChannelId).not.toEqual(messagingChannelId2); + expect(chatChannelId != null).toEqual(true); + expect(chatChannelId2 != null).toEqual(true); + expect(chatChannelId).not.toEqual(chatChannelId2); stream.close(); server.close(); @@ -104,8 +104,8 @@ describe('Streaming', () => { const server = new WS('wss://misskey.test/streaming'); const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); - const messaging = stream.useChannel('messaging', { otherparty: 'aaa' }); - messaging.send('read', { id: 'aaa' }); + const chat = stream.useChannel('chat', { other: 'aaa' }); + chat.send('read', { id: 'aaa' }); const ws = await server.connected; expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); From 3f7a2bf5b5346582ce495d5fbdb244b453377cc5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 24 Mar 2025 12:34:25 +0000 Subject: [PATCH 272/405] Bump version to 2025.3.2-beta.10 --- package.json | 2 +- packages/misskey-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8f8972d927..942921a148 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.3.2-beta.9", + "version": "2025.3.2-beta.10", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index fc052f47c0..48b757728c 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.3.2-beta.9", + "version": "2025.3.2-beta.10", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 5513337198efdc3fed95a9a20d2da4d0d1af61c1 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, 25 Mar 2025 08:02:06 +0900 Subject: [PATCH 273/405] =?UTF-8?q?fix(frontend):=20=E6=9C=AC=E7=95=AA?= =?UTF-8?q?=E7=92=B0=E5=A2=83=E3=81=A7=E4=B8=8D=E5=BF=85=E8=A6=81=E3=81=AA?= =?UTF-8?q?console.log=E3=82=92=E5=87=BA=E3=81=95=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#15702)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkPagination.vue | 2 +- packages/frontend/src/preferences/manager.ts | 8 ++++---- packages/frontend/src/preferences/utility.ts | 4 ++-- packages/frontend/src/router.ts | 2 +- packages/frontend/src/tab-id.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index a729619180..d90db1748c 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -380,7 +380,7 @@ function prepend(item: MisskeyEntity): void { return; } - console.log(isHead(), isPausingUpdate); + if (_DEV_) console.log(isHead(), isPausingUpdate); if (isHead() && !isPausingUpdate) unshiftItems([item]); else prependQueue(item); diff --git a/packages/frontend/src/preferences/manager.ts b/packages/frontend/src/preferences/manager.ts index 037d6a6bd1..f96aa2f368 100644 --- a/packages/frontend/src/preferences/manager.ts +++ b/packages/frontend/src/preferences/manager.ts @@ -142,11 +142,11 @@ export class PreferencesManager { const v = JSON.parse(JSON.stringify(value)); // deep copy 兼 vueのプロキシ解除 if (deepEqual(this.s[key], v)) { - console.log('(skip) prefer:commit', key, v); + if (_DEV_) console.log('(skip) prefer:commit', key, v); return; } - console.log('prefer:commit', key, v); + if (_DEV_) console.log('prefer:commit', key, v); this.rewriteRawState(key, v); @@ -250,13 +250,13 @@ export class PreferencesManager { if (!deepEqual(cloudValue, record[1])) { this.rewriteRawState(key, cloudValue); record[1] = cloudValue; - console.log('cloud fetched', key, cloudValue); + if (_DEV_) console.log('cloud fetched', key, cloudValue); } } } this.save(); - console.log('cloud fetch completed'); + if (_DEV_) console.log('cloud fetch completed'); } public static newProfile(): PreferencesProfile { diff --git a/packages/frontend/src/preferences/utility.ts b/packages/frontend/src/preferences/utility.ts index 7229b7348e..adba908c3c 100644 --- a/packages/frontend/src/preferences/utility.ts +++ b/packages/frontend/src/preferences/utility.ts @@ -153,7 +153,7 @@ export async function restoreFromCloudBackup() { scope: ['client', 'preferences', 'backups'], }); - console.log(keys); + if (_DEV_) console.log(keys); if (keys.length === 0) { os.alert({ @@ -179,7 +179,7 @@ export async function restoreFromCloudBackup() { key: select.result, }); - console.log(profile); + if (_DEV_) console.log(profile); miLocalStorage.setItem('preferences', JSON.stringify(profile)); miLocalStorage.setItem('hidePreferencesRestoreSuggestion', 'true'); diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 3b79569995..d702da80fa 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -32,7 +32,7 @@ mainRouter.addListener('replace', ctx => { }); mainRouter.addListener('change', ctx => { - console.log('mainRouter: change', ctx.fullPath); + if (_DEV_) console.log('mainRouter: change', ctx.fullPath); analytics.page({ path: ctx.fullPath, title: ctx.fullPath, diff --git a/packages/frontend/src/tab-id.ts b/packages/frontend/src/tab-id.ts index e2e4f69eea..49b69f72d2 100644 --- a/packages/frontend/src/tab-id.ts +++ b/packages/frontend/src/tab-id.ts @@ -8,4 +8,4 @@ import { v4 as uuid } from 'uuid'; // HMR有効時にバグか知らんけど複数回実行されるのでその対策 export const TAB_ID = window.sessionStorage.getItem('TAB_ID') ?? uuid(); window.sessionStorage.setItem('TAB_ID', TAB_ID); -console.log('TAB_ID', TAB_ID); +if (_DEV_) console.log('TAB_ID', TAB_ID); From 8da1368a0b1be76dcf128e486f197f4a0654f941 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 08:05:59 +0900 Subject: [PATCH 274/405] =?UTF-8?q?fix(backend):=20=E6=97=A2=E5=AD=98?= =?UTF-8?q?=E3=81=AE=E3=83=AB=E3=83=BC=E3=83=A0=E3=83=A1=E3=83=B3=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=82=92=E6=94=B9=E3=82=81=E3=81=A6=E6=8B=9B=E5=BE=85?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=81=AA=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 --- packages/backend/src/core/ChatService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index eece5d0da3..57e33af107 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -518,6 +518,10 @@ export class ChatService { const room = await this.chatRoomsRepository.findOneByOrFail({ id: roomId, ownerId: inviterId }); + if (await this.isRoomMember(room, inviteeId)) { + throw new Error('already member'); + } + const existingInvitation = await this.chatRoomInvitationsRepository.findOneBy({ roomId, userId: inviteeId }); if (existingInvitation) { throw new Error('already invited'); From 8d6573fbec5f179e18e2522fe86f8c998e39ad75 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 08:38:53 +0900 Subject: [PATCH 275/405] =?UTF-8?q?enhance(frontend):=20=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=AE=E7=A7=BB=E8=A1=8C=E3=82=92=E6=89=8B=E5=8B=95=E3=81=A7?= =?UTF-8?q?=E3=83=88=E3=83=AA=E3=82=AC=E3=83=BC=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 8 + locales/ja-JP.yml | 2 + packages/frontend/src/boot/main-boot.ts | 137 +---------------- .../frontend/src/pages/settings/other.vue | 14 ++ packages/frontend/src/pref-migrate.ts | 142 ++++++++++++++++++ 5 files changed, 170 insertions(+), 133 deletions(-) create mode 100644 packages/frontend/src/pref-migrate.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index 9ef6f9aa39..560335bf35 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5350,6 +5350,14 @@ export interface Locale extends ILocale { * チャット */ "chat": string; + /** + * 旧設定情報を移行 + */ + "migrateOldSettings": string; + /** + * 通常これは自動で行われていますが、何らかの理由により上手く移行されなかった場合は手動で移行処理をトリガーできます。現在の設定情報は上書きされます。 + */ + "migrateOldSettings_description": string; "_chat": { /** * まだメッセージはありません diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fccf24e759..9b4d74fbfb 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1333,6 +1333,8 @@ postForm: "投稿フォーム" textCount: "文字数" information: "情報" chat: "チャット" +migrateOldSettings: "旧設定情報を移行" +migrateOldSettings_description: "通常これは自動で行われていますが、何らかの理由により上手く移行されなかった場合は手動で移行処理をトリガーできます。現在の設定情報は上書きされます。" _chat: noMessagesYet: "まだメッセージはありません" diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 19371dff0e..58873f684e 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -6,19 +6,17 @@ import { createApp, defineAsyncComponent, markRaw } from 'vue'; import { ui } from '@@/js/config.js'; import * as Misskey from 'misskey-js'; -import { v4 as uuid } from 'uuid'; import { compareVersions } from 'compare-versions'; import { common } from './common.js'; import type { Component } from 'vue'; import type { Keymap } from '@/utility/hotkey.js'; -import type { DeckProfile } from '@/deck.js'; import { i18n } from '@/i18n.js'; -import { alert, confirm, popup, post, toast } from '@/os.js'; +import { alert, confirm, popup, post } from '@/os.js'; import { useStream } from '@/stream.js'; import * as sound from '@/utility/sound.js'; import { $i } from '@/i.js'; import { instance } from '@/instance.js'; -import { ColdDeviceStorage, store } from '@/store.js'; +import { store } from '@/store.js'; import { reactionPicker } from '@/utility/reaction-picker.js'; import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement, claimedAchievements } from '@/utility/achievements.js'; @@ -28,12 +26,10 @@ import { mainRouter } from '@/router.js'; import { makeHotkey } from '@/utility/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; import { prefer } from '@/preferences.js'; -import { misskeyApi } from '@/utility/misskey-api.js'; -import { deckStore } from '@/ui/deck/deck-store.js'; import { launchPlugins } from '@/plugin.js'; -import { unisonReload } from '@/utility/unison-reload.js'; import { updateCurrentAccountPartial } from '@/accounts.js'; import { signout } from '@/signout.js'; +import { migrateOldSettings } from '@/pref-migrate.js'; export async function mainBoot() { const { isClientUpdated, lastVersion } = await common(() => { @@ -82,132 +78,7 @@ export async function mainBoot() { if (lastVersion && (compareVersions('2025.3.2-alpha.0', lastVersion) === 1)) { console.log('Preferences migration'); - store.loaded.then(async () => { - const themes = await misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }).catch(() => []); - if (themes.length > 0) { - prefer.commit('themes', themes); - } - - const plugins = ColdDeviceStorage.get('plugins'); - prefer.commit('plugins', plugins.map(p => ({ - ...p, - installId: (p as any).id, - id: undefined, - }))); - - prefer.commit('deck.profile', deckStore.s.profile); - misskeyApi('i/registry/keys', { - scope: ['client', 'deck', 'profiles'], - }).then(async keys => { - const profiles: DeckProfile[] = []; - for (const key of keys) { - const deck = await misskeyApi('i/registry/get', { - scope: ['client', 'deck', 'profiles'], - key: key, - }); - profiles.push({ - id: uuid(), - name: key, - columns: deck.columns, - layout: deck.layout, - }); - } - prefer.commit('deck.profiles', profiles); - }); - - prefer.commit('lightTheme', ColdDeviceStorage.get('lightTheme')); - prefer.commit('darkTheme', ColdDeviceStorage.get('darkTheme')); - prefer.commit('syncDeviceDarkMode', ColdDeviceStorage.get('syncDeviceDarkMode')); - prefer.commit('emojiPalettes', [{ - id: 'reactions', - name: '', - emojis: store.s.reactions, - }, { - id: 'pinnedEmojis', - name: '', - emojis: store.s.pinnedEmojis, - }]); - prefer.commit('emojiPaletteForMain', 'pinnedEmojis'); - prefer.commit('emojiPaletteForReaction', 'reactions'); - prefer.commit('overridedDeviceKind', store.s.overridedDeviceKind); - prefer.commit('widgets', store.s.widgets); - prefer.commit('keepCw', store.s.keepCw); - prefer.commit('collapseRenotes', store.s.collapseRenotes); - prefer.commit('rememberNoteVisibility', store.s.rememberNoteVisibility); - prefer.commit('uploadFolder', store.s.uploadFolder); - prefer.commit('keepOriginalUploading', store.s.keepOriginalUploading); - prefer.commit('menu', store.s.menu); - prefer.commit('statusbars', store.s.statusbars); - prefer.commit('pinnedUserLists', store.s.pinnedUserLists); - prefer.commit('serverDisconnectedBehavior', store.s.serverDisconnectedBehavior); - prefer.commit('nsfw', store.s.nsfw); - prefer.commit('highlightSensitiveMedia', store.s.highlightSensitiveMedia); - prefer.commit('animation', store.s.animation); - prefer.commit('animatedMfm', store.s.animatedMfm); - prefer.commit('advancedMfm', store.s.advancedMfm); - prefer.commit('showReactionsCount', store.s.showReactionsCount); - prefer.commit('enableQuickAddMfmFunction', store.s.enableQuickAddMfmFunction); - prefer.commit('loadRawImages', store.s.loadRawImages); - prefer.commit('imageNewTab', store.s.imageNewTab); - prefer.commit('disableShowingAnimatedImages', store.s.disableShowingAnimatedImages); - prefer.commit('emojiStyle', store.s.emojiStyle); - prefer.commit('menuStyle', store.s.menuStyle); - prefer.commit('useBlurEffectForModal', store.s.useBlurEffectForModal); - prefer.commit('useBlurEffect', store.s.useBlurEffect); - prefer.commit('showFixedPostForm', store.s.showFixedPostForm); - prefer.commit('showFixedPostFormInChannel', store.s.showFixedPostFormInChannel); - prefer.commit('enableInfiniteScroll', store.s.enableInfiniteScroll); - prefer.commit('useReactionPickerForContextMenu', store.s.useReactionPickerForContextMenu); - prefer.commit('showGapBetweenNotesInTimeline', store.s.showGapBetweenNotesInTimeline); - prefer.commit('instanceTicker', store.s.instanceTicker); - prefer.commit('emojiPickerScale', store.s.emojiPickerScale); - prefer.commit('emojiPickerWidth', store.s.emojiPickerWidth); - prefer.commit('emojiPickerHeight', store.s.emojiPickerHeight); - prefer.commit('emojiPickerStyle', store.s.emojiPickerStyle); - prefer.commit('reportError', store.s.reportError); - prefer.commit('squareAvatars', store.s.squareAvatars); - prefer.commit('showAvatarDecorations', store.s.showAvatarDecorations); - prefer.commit('numberOfPageCache', store.s.numberOfPageCache); - prefer.commit('showNoteActionsOnlyHover', store.s.showNoteActionsOnlyHover); - prefer.commit('showClipButtonInNoteFooter', store.s.showClipButtonInNoteFooter); - prefer.commit('reactionsDisplaySize', store.s.reactionsDisplaySize); - prefer.commit('limitWidthOfReaction', store.s.limitWidthOfReaction); - prefer.commit('forceShowAds', store.s.forceShowAds); - prefer.commit('aiChanMode', store.s.aiChanMode); - prefer.commit('devMode', store.s.devMode); - prefer.commit('mediaListWithOneImageAppearance', store.s.mediaListWithOneImageAppearance); - prefer.commit('notificationPosition', store.s.notificationPosition); - prefer.commit('notificationStackAxis', store.s.notificationStackAxis); - prefer.commit('enableCondensedLine', store.s.enableCondensedLine); - prefer.commit('keepScreenOn', store.s.keepScreenOn); - prefer.commit('disableStreamingTimeline', store.s.disableStreamingTimeline); - prefer.commit('useGroupedNotifications', store.s.useGroupedNotifications); - prefer.commit('dataSaver', store.s.dataSaver); - prefer.commit('enableSeasonalScreenEffect', store.s.enableSeasonalScreenEffect); - prefer.commit('enableHorizontalSwipe', store.s.enableHorizontalSwipe); - prefer.commit('useNativeUiForVideoAudioPlayer', store.s.useNativeUIForVideoAudioPlayer); - prefer.commit('keepOriginalFilename', store.s.keepOriginalFilename); - prefer.commit('alwaysConfirmFollow', store.s.alwaysConfirmFollow); - prefer.commit('confirmWhenRevealingSensitiveMedia', store.s.confirmWhenRevealingSensitiveMedia); - prefer.commit('contextMenu', store.s.contextMenu); - prefer.commit('skipNoteRender', store.s.skipNoteRender); - prefer.commit('showSoftWordMutedWord', store.s.showSoftWordMutedWord); - prefer.commit('confirmOnReact', store.s.confirmOnReact); - prefer.commit('defaultFollowWithReplies', store.s.defaultWithReplies); - prefer.commit('sound.masterVolume', store.s.sound_masterVolume); - prefer.commit('sound.notUseSound', store.s.sound_notUseSound); - prefer.commit('sound.useSoundOnlyWhenActive', store.s.sound_useSoundOnlyWhenActive); - prefer.commit('sound.on.note', store.s.sound_note as any); - prefer.commit('sound.on.noteMy', store.s.sound_noteMy as any); - prefer.commit('sound.on.notification', store.s.sound_notification as any); - prefer.commit('sound.on.reaction', store.s.sound_reaction as any); - prefer.commit('defaultNoteVisibility', store.s.defaultNoteVisibility); - prefer.commit('defaultNoteLocalOnly', store.s.defaultNoteLocalOnly); - - window.setTimeout(() => { - unisonReload(); - }, 5000); - }); + migrateOldSettings(); } } diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index d81c6fc93d..6736572e0b 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -115,6 +115,13 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.registry }} + +
+ + + {{ i18n.ts.migrateOldSettings }} + +
@@ -128,6 +135,7 @@ import MkFolder from '@/components/MkFolder.vue'; import FormInfo from '@/components/MkInfo.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkButton from '@/components/MkButton.vue'; +import FormSlot from '@/components/form/slot.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { ensureSignin } from '@/i.js'; @@ -138,6 +146,7 @@ import FormSection from '@/components/form/section.vue'; import { prefer } from '@/preferences.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; import { signout } from '@/signout.js'; +import { migrateOldSettings } from '@/pref-migrate.js'; const $i = ensureSignin(); @@ -175,6 +184,11 @@ async function deleteAccount() { await signout(); } +function migrate() { + os.waiting(); + migrateOldSettings(); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pref-migrate.ts b/packages/frontend/src/pref-migrate.ts new file mode 100644 index 0000000000..beeaac8383 --- /dev/null +++ b/packages/frontend/src/pref-migrate.ts @@ -0,0 +1,142 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { v4 as uuid } from 'uuid'; +import type { DeckProfile } from '@/deck.js'; +import { ColdDeviceStorage, store } from '@/store.js'; +import { prefer } from '@/preferences.js'; +import { misskeyApi } from '@/utility/misskey-api.js'; +import { deckStore } from '@/ui/deck/deck-store.js'; +import { unisonReload } from '@/utility/unison-reload.js'; + +// TODO: そのうち消す +export function migrateOldSettings() { + store.loaded.then(async () => { + const themes = await misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' }).catch(() => []); + if (themes.length > 0) { + prefer.commit('themes', themes); + } + + const plugins = ColdDeviceStorage.get('plugins'); + prefer.commit('plugins', plugins.map(p => ({ + ...p, + installId: (p as any).id, + id: undefined, + }))); + + prefer.commit('deck.profile', deckStore.s.profile); + misskeyApi('i/registry/keys', { + scope: ['client', 'deck', 'profiles'], + }).then(async keys => { + const profiles: DeckProfile[] = []; + for (const key of keys) { + const deck = await misskeyApi('i/registry/get', { + scope: ['client', 'deck', 'profiles'], + key: key, + }); + profiles.push({ + id: uuid(), + name: key, + columns: deck.columns, + layout: deck.layout, + }); + } + prefer.commit('deck.profiles', profiles); + }); + + prefer.commit('lightTheme', ColdDeviceStorage.get('lightTheme')); + prefer.commit('darkTheme', ColdDeviceStorage.get('darkTheme')); + prefer.commit('syncDeviceDarkMode', ColdDeviceStorage.get('syncDeviceDarkMode')); + prefer.commit('emojiPalettes', [{ + id: 'reactions', + name: '', + emojis: store.s.reactions, + }, { + id: 'pinnedEmojis', + name: '', + emojis: store.s.pinnedEmojis, + }]); + prefer.commit('emojiPaletteForMain', 'pinnedEmojis'); + prefer.commit('emojiPaletteForReaction', 'reactions'); + prefer.commit('overridedDeviceKind', store.s.overridedDeviceKind); + prefer.commit('widgets', store.s.widgets); + prefer.commit('keepCw', store.s.keepCw); + prefer.commit('collapseRenotes', store.s.collapseRenotes); + prefer.commit('rememberNoteVisibility', store.s.rememberNoteVisibility); + prefer.commit('uploadFolder', store.s.uploadFolder); + prefer.commit('keepOriginalUploading', store.s.keepOriginalUploading); + prefer.commit('menu', store.s.menu); + prefer.commit('statusbars', store.s.statusbars); + prefer.commit('pinnedUserLists', store.s.pinnedUserLists); + prefer.commit('serverDisconnectedBehavior', store.s.serverDisconnectedBehavior); + prefer.commit('nsfw', store.s.nsfw); + prefer.commit('highlightSensitiveMedia', store.s.highlightSensitiveMedia); + prefer.commit('animation', store.s.animation); + prefer.commit('animatedMfm', store.s.animatedMfm); + prefer.commit('advancedMfm', store.s.advancedMfm); + prefer.commit('showReactionsCount', store.s.showReactionsCount); + prefer.commit('enableQuickAddMfmFunction', store.s.enableQuickAddMfmFunction); + prefer.commit('loadRawImages', store.s.loadRawImages); + prefer.commit('imageNewTab', store.s.imageNewTab); + prefer.commit('disableShowingAnimatedImages', store.s.disableShowingAnimatedImages); + prefer.commit('emojiStyle', store.s.emojiStyle); + prefer.commit('menuStyle', store.s.menuStyle); + prefer.commit('useBlurEffectForModal', store.s.useBlurEffectForModal); + prefer.commit('useBlurEffect', store.s.useBlurEffect); + prefer.commit('showFixedPostForm', store.s.showFixedPostForm); + prefer.commit('showFixedPostFormInChannel', store.s.showFixedPostFormInChannel); + prefer.commit('enableInfiniteScroll', store.s.enableInfiniteScroll); + prefer.commit('useReactionPickerForContextMenu', store.s.useReactionPickerForContextMenu); + prefer.commit('showGapBetweenNotesInTimeline', store.s.showGapBetweenNotesInTimeline); + prefer.commit('instanceTicker', store.s.instanceTicker); + prefer.commit('emojiPickerScale', store.s.emojiPickerScale); + prefer.commit('emojiPickerWidth', store.s.emojiPickerWidth); + prefer.commit('emojiPickerHeight', store.s.emojiPickerHeight); + prefer.commit('emojiPickerStyle', store.s.emojiPickerStyle); + prefer.commit('reportError', store.s.reportError); + prefer.commit('squareAvatars', store.s.squareAvatars); + prefer.commit('showAvatarDecorations', store.s.showAvatarDecorations); + prefer.commit('numberOfPageCache', store.s.numberOfPageCache); + prefer.commit('showNoteActionsOnlyHover', store.s.showNoteActionsOnlyHover); + prefer.commit('showClipButtonInNoteFooter', store.s.showClipButtonInNoteFooter); + prefer.commit('reactionsDisplaySize', store.s.reactionsDisplaySize); + prefer.commit('limitWidthOfReaction', store.s.limitWidthOfReaction); + prefer.commit('forceShowAds', store.s.forceShowAds); + prefer.commit('aiChanMode', store.s.aiChanMode); + prefer.commit('devMode', store.s.devMode); + prefer.commit('mediaListWithOneImageAppearance', store.s.mediaListWithOneImageAppearance); + prefer.commit('notificationPosition', store.s.notificationPosition); + prefer.commit('notificationStackAxis', store.s.notificationStackAxis); + prefer.commit('enableCondensedLine', store.s.enableCondensedLine); + prefer.commit('keepScreenOn', store.s.keepScreenOn); + prefer.commit('disableStreamingTimeline', store.s.disableStreamingTimeline); + prefer.commit('useGroupedNotifications', store.s.useGroupedNotifications); + prefer.commit('dataSaver', store.s.dataSaver); + prefer.commit('enableSeasonalScreenEffect', store.s.enableSeasonalScreenEffect); + prefer.commit('enableHorizontalSwipe', store.s.enableHorizontalSwipe); + prefer.commit('useNativeUiForVideoAudioPlayer', store.s.useNativeUIForVideoAudioPlayer); + prefer.commit('keepOriginalFilename', store.s.keepOriginalFilename); + prefer.commit('alwaysConfirmFollow', store.s.alwaysConfirmFollow); + prefer.commit('confirmWhenRevealingSensitiveMedia', store.s.confirmWhenRevealingSensitiveMedia); + prefer.commit('contextMenu', store.s.contextMenu); + prefer.commit('skipNoteRender', store.s.skipNoteRender); + prefer.commit('showSoftWordMutedWord', store.s.showSoftWordMutedWord); + prefer.commit('confirmOnReact', store.s.confirmOnReact); + prefer.commit('defaultFollowWithReplies', store.s.defaultWithReplies); + prefer.commit('sound.masterVolume', store.s.sound_masterVolume); + prefer.commit('sound.notUseSound', store.s.sound_notUseSound); + prefer.commit('sound.useSoundOnlyWhenActive', store.s.sound_useSoundOnlyWhenActive); + prefer.commit('sound.on.note', store.s.sound_note as any); + prefer.commit('sound.on.noteMy', store.s.sound_noteMy as any); + prefer.commit('sound.on.notification', store.s.sound_notification as any); + prefer.commit('sound.on.reaction', store.s.sound_reaction as any); + prefer.commit('defaultNoteVisibility', store.s.defaultNoteVisibility); + prefer.commit('defaultNoteLocalOnly', store.s.defaultNoteLocalOnly); + + window.setTimeout(() => { + unisonReload(); + }, 5000); + }); +} From 5d3e8253c4581e8f21886083ed04e0947bfa0d01 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:24:01 +0900 Subject: [PATCH 276/405] =?UTF-8?q?fix(frontend):=20=E3=83=81=E3=83=A3?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=82=92=E5=85=A8=E5=93=A1=E3=81=AB=E8=A8=B1?= =?UTF-8?q?=E5=8F=AF=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E5=A0=B4=E5=90=88?= =?UTF-8?q?=E3=81=AB=E8=AA=B0=E3=82=82=E8=A8=B1=E5=8F=AF=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=84=E3=81=A8=E8=A1=A8=E7=A4=BA=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/chat/room.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/chat/room.vue b/packages/frontend/src/pages/chat/room.vue index 15e9f43db2..7aeb1f788a 100644 --- a/packages/frontend/src/pages/chat/room.vue +++ b/packages/frontend/src/pages/chat/room.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowers }}
{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowing }}
{{ i18n.ts._chat.thisUserAllowsChatOnlyFromMutualFollowing }}
-
{{ i18n.ts._chat.thisUserNotAllowedChatAnyone }}
+
{{ i18n.ts._chat.thisUserNotAllowedChatAnyone }}
@@ -51,6 +59,8 @@ import MkRadios from '@/components/MkRadios.vue'; import MkButton from '@/components/MkButton.vue'; import FormSlot from '@/components/form/slot.vue'; import MkContainer from '@/components/MkContainer.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue'; import * as os from '@/os.js'; import { navbarItemDef } from '@/navbar.js'; import { store } from '@/store.js'; @@ -68,6 +78,7 @@ const items = ref(prefer.s.menu.map(x => ({ }))); const menuDisplay = computed(store.makeGetterSetter('menuDisplay')); +const showNavbarSubButtons = prefer.model('showNavbarSubButtons'); async function addItem() { const menu = Object.keys(navbarItemDef).filter(k => !prefer.s.menu.includes(k)); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 127ebeef0c..310c3856eb 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -329,6 +329,9 @@ export const PREF_DEF = { makeEveryTextElementsSelectable: { default: DEFAULT_DEVICE_KIND === 'desktop', }, + showNavbarSubButtons: { + default: true, + }, plugins: { default: [] as Plugin[], }, diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 88ce3a96e2..0f00a25d7a 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> -
+
diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 64fe328478..7c2ec90dd8 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -587,6 +587,12 @@ export const searchIndexes: SearchIndexItem[] = [ path: '/settings/other', icon: 'ti ti-dots', }, + { + id: 'pOot4giZs', + label: i18n.ts._settings.showNavbarSubButtons, + keywords: ['toggle', 'button', 'sub'], + path: '/settings/navbar', + }, { id: '3icEvyv2D', children: [ From 600bb34172bc34a401324119128c2dc71a21e506 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:54:23 +0900 Subject: [PATCH 280/405] =?UTF-8?q?enhance(frontend):=20=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=AE=E3=82=A4=E3=83=B3=E3=83=87=E3=83=83=E3=82=AF=E3=82=B9?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/pages/settings/navbar.vue | 90 ++++++++++--------- .../utility/autogen/settings-search-index.ts | 14 ++- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index b6fdd009b2..f944490a66 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -4,53 +4,55 @@ SPDX-License-Identifier: AGPL-3.0-only -->
- - - - - - - - -
- {{ i18n.ts.addItem }} - {{ i18n.ts.default }} - {{ i18n.ts.save }} -
+ +
+ + + + + + + + +
+ {{ i18n.ts.addItem }} + {{ i18n.ts.default }} + {{ i18n.ts.save }} +
- - - - - + + + + + - + - - - - - - - -
+ + + + + + + +
+ diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index f461ee9bc9..3a7d2cd490 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -429,6 +429,23 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + +
+ + + + + + + +
+
+
+ @@ -609,6 +626,7 @@ const emojiStyle = prefer.model('emojiStyle'); const useBlurEffectForModal = prefer.model('useBlurEffectForModal'); const useBlurEffect = prefer.model('useBlurEffect'); const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies'); +const chatShowSenderName = prefer.model('chat.showSenderName'); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 310c3856eb..801bb29e8c 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -374,6 +374,10 @@ export const PREF_DEF = { default: 'left' as 'left' | 'right' | 'center', }, + 'chat.showSenderName': { + default: false, + }, + 'game.dropAndFusion': { default: { bgmVolume: 0.25, diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 316319bbf0..1e41dffe22 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -487,47 +487,59 @@ export const searchIndexes: SearchIndexItem[] = [ id: 'vPQPvmntL', children: [ { - id: 'zoogNoJEO', + id: 'zZxyXHk3A', + label: i18n.ts._settings._chat.showSenderName, + keywords: ['show', 'sender', 'name'], + }, + ], + label: i18n.ts.chat, + keywords: ['chat', 'messaging'], + }, + { + id: 'sCscGhMmH', + children: [ + { + id: 'dLkRNHn3k', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: '7ix3kvMyU', + id: 'BvooTWFW5', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: '6RxgjmMLN', + id: 'yzbghkAq0', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'alM3BcFzs', + id: 'aSbKFHbOy', label: i18n.ts.withRepliesByDefaultForNewlyFollowed, keywords: ['follow', 'replies'], }, { - id: 'yzbghkAq0', + id: '89bn97UgY', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: '4SPd8QhYx', + id: 'hgf3rgdA6', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: 'BoNg4LwOq', + id: '6FVdHPhhv', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: 'hgf3rgdA6', + id: '5Bx5DAST1', label: i18n.ts.hemisphere, keywords: [], }, { - id: 'nnj4DkjhP', + id: 'wv7Cwiwb1', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, From 2272eceffa42f3b3ed0602d0f30bfafc03a9a49e Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:19:54 +0900 Subject: [PATCH 288/405] =?UTF-8?q?enhance(frontend):=20Enter=E3=81=A7?= =?UTF-8?q?=E3=83=81=E3=83=A3=E3=83=83=E3=83=88=E3=81=AE=E3=83=A1=E3=83=83?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E9=80=81=E4=BF=A1=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 20 ++++++++++++++ locales/ja-JP.yml | 5 ++++ .../frontend/src/pages/chat/room.form.vue | 12 +++++++-- .../src/pages/settings/preferences.vue | 26 ++++++++++++++++++- packages/frontend/src/preferences/def.ts | 3 +++ .../utility/autogen/settings-search-index.ts | 25 +++++++++++------- 6 files changed, 78 insertions(+), 13 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 0fcfcee7e5..b0da7915dc 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5448,6 +5448,14 @@ export interface Locale extends ILocale { * ホーム */ "home": string; + /** + * 送信 + */ + "send": string; + /** + * 改行 + */ + "newline": string; /** * このルームをミュート */ @@ -5614,11 +5622,23 @@ export interface Locale extends ILocale { * ナビゲーションバーに副ボタンを表示 */ "showNavbarSubButtons": string; + /** + * オンのとき + */ + "ifOn": string; + /** + * オフのとき + */ + "ifOff": string; "_chat": { /** * 送信者の名前を表示 */ "showSenderName": string; + /** + * Enterで送信 + */ + "sendOnEnter": string; }; }; "_preferencesProfile": { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e770d04768..b95adb1871 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1359,6 +1359,8 @@ _chat: members: "メンバー" searchMessages: "メッセージを検索" home: "ホーム" + send: "送信" + newline: "改行" muteThisRoom: "このルームをミュート" deleteRoom: "ルームを削除" cannotChatWithTheUser: "このユーザーとのチャットを開始できません" @@ -1404,9 +1406,12 @@ _settings: makeEveryTextElementsSelectable: "全てのテキスト要素を選択可能にする" makeEveryTextElementsSelectable_description: "有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。" showNavbarSubButtons: "ナビゲーションバーに副ボタンを表示" + ifOn: "オンのとき" + ifOff: "オフのとき" _chat: showSenderName: "送信者の名前を表示" + sendOnEnter: "Enterで送信" _preferencesProfile: profileName: "プロファイル名" diff --git a/packages/frontend/src/pages/chat/room.form.vue b/packages/frontend/src/pages/chat/room.form.vue index aba9d6061f..27ddbeb565 100644 --- a/packages/frontend/src/pages/chat/room.form.vue +++ b/packages/frontend/src/pages/chat/room.form.vue @@ -151,8 +151,16 @@ function onDrop(ev: DragEvent): void { } function onKeydown(ev: KeyboardEvent) { - if ((ev.key === 'Enter') && (ev.ctrlKey || ev.metaKey)) { - send(); + if (ev.key === 'Enter') { + if (prefer.s['chat.sendOnEnter']) { + if (!(ev.ctrlKey || ev.metaKey || ev.shiftKey)) { + send(); + } + } else { + if ((ev.ctrlKey || ev.metaKey)) { + send(); + } + } } } diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 3a7d2cd490..816f8d7435 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -434,7 +434,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
+
@@ -442,6 +442,28 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + +
@@ -627,6 +649,7 @@ const useBlurEffectForModal = prefer.model('useBlurEffectForModal'); const useBlurEffect = prefer.model('useBlurEffect'); const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies'); const chatShowSenderName = prefer.model('chat.showSenderName'); +const chatSendOnEnter = prefer.model('chat.sendOnEnter'); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); @@ -654,6 +677,7 @@ watch([ squareAvatars, highlightSensitiveMedia, enableSeasonalScreenEffect, + chatShowSenderName, ], async () => { await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 801bb29e8c..b588cc3b5f 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -377,6 +377,9 @@ export const PREF_DEF = { 'chat.showSenderName': { default: false, }, + 'chat.sendOnEnter': { + default: false, + }, 'game.dropAndFusion': { default: { diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 1e41dffe22..c939c93425 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -491,55 +491,60 @@ export const searchIndexes: SearchIndexItem[] = [ label: i18n.ts._settings._chat.showSenderName, keywords: ['show', 'sender', 'name'], }, + { + id: 'omEy5Q3Ev', + label: i18n.ts._settings._chat.sendOnEnter, + keywords: ['send', 'enter', 'newline'], + }, ], label: i18n.ts.chat, keywords: ['chat', 'messaging'], }, { - id: 'sCscGhMmH', + id: '5fy7VEy6i', children: [ { - id: 'dLkRNHn3k', + id: 'EosiWZvak', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: 'BvooTWFW5', + id: 'qY5xTzl35', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: 'yzbghkAq0', + id: '2VSnj81vC', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'aSbKFHbOy', + id: 'hdQa7W2H1', label: i18n.ts.withRepliesByDefaultForNewlyFollowed, keywords: ['follow', 'replies'], }, { - id: '89bn97UgY', + id: 'nnj4DkjhP', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: 'hgf3rgdA6', + id: 'Eh7vTluDO', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: '6FVdHPhhv', + id: 'vTRSKf1JA', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: '5Bx5DAST1', + id: 'dwhQfcLGt', label: i18n.ts.hemisphere, keywords: [], }, { - id: 'wv7Cwiwb1', + id: 'Ar1lj7f7U', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, From 1736955694336096dd0e6e999c43fefa4c55801b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:29:03 +0900 Subject: [PATCH 289/405] =?UTF-8?q?fix(backend):=20=E8=87=AA=E5=88=86?= =?UTF-8?q?=E3=81=8C=E3=82=AA=E3=83=BC=E3=83=8A=E3=83=BC=E3=81=A7=E3=81=AF?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=8F=82=E5=8A=A0=E4=B8=AD=E3=81=AE=E3=83=AB?= =?UTF-8?q?=E3=83=BC=E3=83=A0=E3=81=AB=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=92=E9=80=81=E4=BF=A1=E3=81=99=E3=82=8B=E3=81=A8?= =?UTF-8?q?=E4=B8=8D=E5=BF=85=E8=A6=81=E3=81=AA=E9=80=9A=E7=9F=A5=E3=81=8C?= =?UTF-8?q?=E7=99=BA=E7=94=9F=E3=81=99=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/ChatService.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 57e33af107..3b73a0f6ba 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -199,6 +199,8 @@ export class ChatService { throw new Error('you are not a member of the room'); } + const membershipsOtherThanMe = memberships.filter(member => member.userId !== fromUser.id); + const message = { id: this.idService.gen(), fromUserId: fromUser.id, @@ -216,7 +218,7 @@ export class ChatService { this.globalEventService.publishChatRoomStream(toRoom.id, 'message', packedMessage); const redisPipeline = this.redisClient.pipeline(); - for (const membership of memberships) { + for (const membership of membershipsOtherThanMe) { if (membership.isMuted) continue; redisPipeline.set(`newRoomChatMessageExists:${membership.userId}:${toRoom.id}`, message.id); @@ -227,7 +229,7 @@ export class ChatService { // 3秒経っても既読にならなかったらイベント発行 setTimeout(async () => { const redisPipeline = this.redisClient.pipeline(); - for (const membership of memberships) { + for (const membership of membershipsOtherThanMe) { redisPipeline.get(`newRoomChatMessageExists:${membership.userId}:${toRoom.id}`); } const markers = await redisPipeline.exec(); @@ -237,12 +239,12 @@ export class ChatService { const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted); - for (let i = 0; i < memberships.length; i++) { + for (let i = 0; i < membershipsOtherThanMe.length; i++) { const marker = markers[i][1]; if (marker == null) continue; - this.globalEventService.publishMainStream(memberships[i].userId, 'newChatMessage', packedMessageForTo); - //this.pushNotificationService.pushNotification(memberships[i].userId, 'newChatMessage', packedMessageForTo); + this.globalEventService.publishMainStream(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); + //this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo); } }, 3000); From c51e862b7de460cd7c1b57a63a002156bbfe1b49 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:41:12 +0900 Subject: [PATCH 290/405] =?UTF-8?q?enhance:=20=E9=80=81=E4=BF=A1=E3=81=97?= =?UTF-8?q?=E3=81=9F=E3=83=81=E3=83=A3=E3=83=83=E3=83=88=E3=83=AB=E3=83=BC?= =?UTF-8?q?=E3=83=A0=E3=81=B8=E3=81=AE=E6=8B=9B=E5=BE=85=E3=82=92=E7=A2=BA?= =?UTF-8?q?=E8=AA=8D=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 4 ++ locales/ja-JP.yml | 1 + packages/backend/src/core/ChatService.ts | 10 +++ .../backend/src/server/api/endpoint-list.ts | 1 + .../chat/rooms/invitations/outbox.ts | 67 ++++++++++++++++++ .../frontend/src/pages/chat/room.members.vue | 32 ++++++++- packages/misskey-js/etc/misskey-js.api.md | 8 +++ .../misskey-js/src/autogen/apiClientJSDoc.ts | 11 +++ packages/misskey-js/src/autogen/endpoint.ts | 3 + packages/misskey-js/src/autogen/entities.ts | 2 + packages/misskey-js/src/autogen/types.ts | 69 +++++++++++++++++++ 11 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index b0da7915dc..eee9de8f0d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5424,6 +5424,10 @@ export interface Locale extends ILocale { * ユーザーを招待 */ "inviteUser": string; + /** + * 送信した招待 + */ + "sentInvitations": string; /** * 参加 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b95adb1871..e4c17276b1 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1353,6 +1353,7 @@ _chat: noHistory: "履歴はありません" noRooms: "ルームはありません" inviteUser: "ユーザーを招待" + sentInvitations: "送信した招待" join: "参加" ignore: "無視" leave: "ルームから退出" diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 3b73a0f6ba..062ca96f17 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -547,6 +547,16 @@ export class ChatService { return created; } + @bindThis + public async getSentRoomInvitationsWithPagination(roomId: MiChatRoom['id'], limit: number, sinceId?: MiChatRoomInvitation['id'] | null, untilId?: MiChatRoomInvitation['id'] | null) { + const query = this.queryService.makePaginationQuery(this.chatRoomInvitationsRepository.createQueryBuilder('invitation'), sinceId, untilId) + .where('invitation.roomId = :roomId', { roomId }); + + const invitations = await query.take(limit).getMany(); + + return invitations; + } + @bindThis public async getOwnedRoomsWithPagination(ownerId: MiUser['id'], limit: number, sinceId?: MiChatRoom['id'] | null, untilId?: MiChatRoom['id'] | null) { const query = this.queryService.makePaginationQuery(this.chatRoomsRepository.createQueryBuilder('room'), sinceId, untilId) diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts index 9bb29e138b..c8cb87e0ab 100644 --- a/packages/backend/src/server/api/endpoint-list.ts +++ b/packages/backend/src/server/api/endpoint-list.ts @@ -417,5 +417,6 @@ export * as 'chat/rooms/members' from './endpoints/chat/rooms/members.js'; export * as 'chat/rooms/invitations/create' from './endpoints/chat/rooms/invitations/create.js'; export * as 'chat/rooms/invitations/ignore' from './endpoints/chat/rooms/invitations/ignore.js'; export * as 'chat/rooms/invitations/inbox' from './endpoints/chat/rooms/invitations/inbox.js'; +export * as 'chat/rooms/invitations/outbox' from './endpoints/chat/rooms/invitations/outbox.js'; export * as 'chat/history' from './endpoints/chat/history.js'; export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js'; diff --git a/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts new file mode 100644 index 0000000000..12d496e94b --- /dev/null +++ b/packages/backend/src/server/api/endpoints/chat/rooms/invitations/outbox.ts @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { ChatService } from '@/core/ChatService.js'; +import { ChatEntityService } from '@/core/entities/ChatEntityService.js'; + +export const meta = { + tags: ['chat'], + + requireCredential: true, + + kind: 'read:chat', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'ChatRoomInvitation', + }, + }, + + errors: { + noSuchRoom: { + message: 'No such room.', + code: 'NO_SUCH_ROOM', + id: 'a3c6b309-9717-4316-ae94-a69b53437237', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roomId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: ['roomId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private chatService: ChatService, + private chatEntityService: ChatEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const room = await this.chatService.findMyRoomById(me.id, ps.roomId); + if (room == null) { + throw new ApiError(meta.errors.noSuchRoom); + } + + const invitations = await this.chatService.getSentRoomInvitationsWithPagination(ps.roomId, ps.limit, ps.sinceId, ps.untilId); + return this.chatEntityService.packRoomInvitations(invitations, me); + }); + } +} diff --git a/packages/frontend/src/pages/chat/room.members.vue b/packages/frontend/src/pages/chat/room.members.vue index d20216a81c..2b31efab38 100644 --- a/packages/frontend/src/pages/chat/room.members.vue +++ b/packages/frontend/src/pages/chat/room.members.vue @@ -18,6 +18,18 @@ SPDX-License-Identifier: AGPL-3.0-only
+ +
@@ -47,12 +59,20 @@ const isOwner = computed(() => { }); const memberships = ref([]); +const invitations = ref([]); onMounted(async () => { memberships.value = await misskeyApi('chat/rooms/members', { roomId: props.room.id, limit: 50, }); + + if (isOwner.value) { + invitations.value = await misskeyApi('chat/rooms/invitations/outbox', { + roomId: props.room.id, + limit: 50, + }); + } }); @@ -65,9 +85,15 @@ onMounted(async () => { flex: 1; min-width: 0; margin-right: 8px; +} - &:hover { - text-decoration: none; - } +.invitation { + display: flex; +} + +.invitationBody { + flex: 1; + min-width: 0; + margin-right: 8px; } diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index e79cd794a6..8610a21304 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1046,6 +1046,12 @@ type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations__ // @public (undocumented) type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; +// @public (undocumented) +type ChatRoomsInvitationsOutboxRequest = operations['chat___rooms___invitations___outbox']['requestBody']['content']['application/json']; + +// @public (undocumented) +type ChatRoomsInvitationsOutboxResponse = operations['chat___rooms___invitations___outbox']['responses']['200']['content']['application/json']; + // @public (undocumented) type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; @@ -1619,6 +1625,8 @@ declare namespace entities { ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, + ChatRoomsInvitationsOutboxRequest, + ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, ChatRoomsJoinResponse, ChatRoomsJoiningRequest, diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index e12ba0d6b2..e5d0b94a10 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -1688,6 +1688,17 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 558f4ab514..1dfdb53320 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -235,6 +235,8 @@ import type { ChatRoomsInvitationsIgnoreResponse, ChatRoomsInvitationsInboxRequest, ChatRoomsInvitationsInboxResponse, + ChatRoomsInvitationsOutboxRequest, + ChatRoomsInvitationsOutboxResponse, ChatRoomsJoinRequest, ChatRoomsJoinResponse, ChatRoomsJoiningRequest, @@ -784,6 +786,7 @@ export type Endpoints = { 'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse }; 'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: ChatRoomsInvitationsIgnoreResponse }; 'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse }; + 'chat/rooms/invitations/outbox': { req: ChatRoomsInvitationsOutboxRequest; res: ChatRoomsInvitationsOutboxResponse }; 'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse }; 'chat/rooms/joining': { req: ChatRoomsJoiningRequest; res: ChatRoomsJoiningResponse }; 'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse }; diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts index bb21272e01..0e3ca7202e 100644 --- a/packages/misskey-js/src/autogen/entities.ts +++ b/packages/misskey-js/src/autogen/entities.ts @@ -238,6 +238,8 @@ export type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invit export type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json']; export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json']; export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json']; +export type ChatRoomsInvitationsOutboxRequest = operations['chat___rooms___invitations___outbox']['requestBody']['content']['application/json']; +export type ChatRoomsInvitationsOutboxResponse = operations['chat___rooms___invitations___outbox']['responses']['200']['content']['application/json']; export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json']; export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json']; export type ChatRoomsJoiningRequest = operations['chat___rooms___joining']['requestBody']['content']['application/json']; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 31bc34e473..3acef22101 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -1484,6 +1484,15 @@ export type paths = { */ post: operations['chat___rooms___invitations___inbox']; }; + '/chat/rooms/invitations/outbox': { + /** + * chat/rooms/invitations/outbox + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + post: operations['chat___rooms___invitations___outbox']; + }; '/chat/rooms/join': { /** * chat/rooms/join @@ -14737,6 +14746,66 @@ export type operations = { }; }; }; + /** + * chat/rooms/invitations/outbox + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *read:chat* + */ + chat___rooms___invitations___outbox: { + requestBody: { + content: { + 'application/json': { + /** Format: misskey:id */ + roomId: string; + /** @default 30 */ + limit?: number; + /** Format: misskey:id */ + sinceId?: string; + /** Format: misskey:id */ + untilId?: string; + }; + }; + }; + responses: { + /** @description OK (with results) */ + 200: { + content: { + 'application/json': components['schemas']['ChatRoomInvitation'][]; + }; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * chat/rooms/join * @description No description provided. From 2fc3baa9882dd257a1ed91277ced85b862eb2836 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 25 Mar 2025 04:45:07 +0000 Subject: [PATCH 291/405] Bump version to 2025.3.2-beta.12 --- package.json | 2 +- packages/misskey-js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8acf676bff..fc2de6e887 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2025.3.2-beta.11", + "version": "2025.3.2-beta.12", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json index ec98e1542b..dc4d49183b 100644 --- a/packages/misskey-js/package.json +++ b/packages/misskey-js/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "misskey-js", - "version": "2025.3.2-beta.11", + "version": "2025.3.2-beta.12", "description": "Misskey SDK for JavaScript", "license": "MIT", "main": "./built/index.js", From 304d0eb83b6e10336ec319600f060f0a719d07b9 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:25:43 +0900 Subject: [PATCH 292/405] =?UTF-8?q?enhance:=20=E3=83=81=E3=83=A3=E3=83=83?= =?UTF-8?q?=E3=83=88=E3=83=AB=E3=83=BC=E3=83=A0=E3=81=AB=E6=8B=9B=E5=BE=85?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=AE=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 8 ++++ locales/ja-JP.yml | 2 + packages/backend/src/core/ChatService.ts | 6 +++ .../entities/NotificationEntityService.ts | 23 +++++++--- packages/backend/src/models/Notification.ts | 6 +++ .../src/models/json-schema/notification.ts | 15 +++++++ .../backend/src/models/json-schema/user.ts | 1 + .../server/api/endpoints/admin/show-user.ts | 1 + .../src/server/api/endpoints/i/update.ts | 1 + packages/backend/src/types.ts | 2 + packages/frontend-shared/js/const.ts | 1 + .../src/components/MkNotification.vue | 5 +++ packages/misskey-js/etc/misskey-js.api.md | 2 +- packages/misskey-js/src/autogen/types.ts | 43 +++++++++++++++++-- packages/misskey-js/src/consts.ts | 2 +- 15 files changed, 105 insertions(+), 13 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index eee9de8f0d..f7d875d2c8 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9883,6 +9883,10 @@ export interface Locale extends ILocale { * ロールが付与されました */ "roleAssigned": string; + /** + * チャットルームへ招待されました + */ + "chatRoomInvitationReceived": string; /** * プッシュ通知の更新をしました */ @@ -9992,6 +9996,10 @@ export interface Locale extends ILocale { * ロールが付与された */ "roleAssigned": string; + /** + * チャットルームへ招待された + */ + "chatRoomInvitationReceived": string; /** * 実績の獲得 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e4c17276b1..efaa279454 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2612,6 +2612,7 @@ _notification: newNote: "新しい投稿" unreadAntennaNote: "アンテナ {name}" roleAssigned: "ロールが付与されました" + chatRoomInvitationReceived: "チャットルームへ招待されました" emptyPushNotificationMessage: "プッシュ通知の更新をしました" achievementEarned: "実績を獲得" testNotification: "通知テスト" @@ -2641,6 +2642,7 @@ _notification: receiveFollowRequest: "フォロー申請を受け取った" followRequestAccepted: "フォローが受理された" roleAssigned: "ロールが付与された" + chatRoomInvitationReceived: "チャットルームへ招待された" achievementEarned: "実績の獲得" exportCompleted: "エクスポートが完了した" login: "ログイン" diff --git a/packages/backend/src/core/ChatService.ts b/packages/backend/src/core/ChatService.ts index 062ca96f17..4c010b2ef7 100644 --- a/packages/backend/src/core/ChatService.ts +++ b/packages/backend/src/core/ChatService.ts @@ -26,6 +26,7 @@ import { Packed } from '@/misc/json-schema.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { emojiRegex } from '@/misc/emoji-regex.js'; +import { NotificationService } from '@/core/NotificationService.js'; const MAX_ROOM_MEMBERS = 30; const MAX_REACTIONS_PER_MESSAGE = 100; @@ -68,6 +69,7 @@ export class ChatService { private apRendererService: ApRendererService, private queueService: QueueService, private pushNotificationService: PushNotificationService, + private notificationService: NotificationService, private userBlockingService: UserBlockingService, private queryService: QueryService, private roleService: RoleService, @@ -544,6 +546,10 @@ export class ChatService { const created = await this.chatRoomInvitationsRepository.insertOne(invitation); + this.notificationService.createNotification(inviteeId, 'chatRoomInvitationReceived', { + invitationId: invitation.id, + }, inviterId); + return created; } diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index dff6968f9c..d8b947839f 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -16,6 +16,7 @@ import { bindThis } from '@/decorators.js'; import { FilterUnionByProperty, groupedNotificationTypes } from '@/types.js'; import { CacheService } from '@/core/CacheService.js'; import { RoleEntityService } from './RoleEntityService.js'; +import { ChatEntityService } from './ChatEntityService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; @@ -27,6 +28,7 @@ export class NotificationEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; private roleEntityService: RoleEntityService; + private chatEntityService: ChatEntityService; constructor( private moduleRef: ModuleRef, @@ -41,9 +43,6 @@ export class NotificationEntityService implements OnModuleInit { private followRequestsRepository: FollowRequestsRepository, private cacheService: CacheService, - - //private userEntityService: UserEntityService, - //private noteEntityService: NoteEntityService, ) { } @@ -51,6 +50,7 @@ export class NotificationEntityService implements OnModuleInit { this.userEntityService = this.moduleRef.get('UserEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); this.roleEntityService = this.moduleRef.get('RoleEntityService'); + this.chatEntityService = this.moduleRef.get('ChatEntityService'); } /** @@ -59,7 +59,6 @@ export class NotificationEntityService implements OnModuleInit { async #packInternal ( src: T, meId: MiUser['id'], - options: { checkValidNotifier?: boolean; }, @@ -92,7 +91,7 @@ export class NotificationEntityService implements OnModuleInit { // if the user has been deleted, don't show this notification if (needsUser && !userIfNeed) return null; - // #region Grouped notifications + //#region Grouped notifications if (notification.type === 'reaction:grouped') { const reactions = (await Promise.all(notification.reactions.map(async reaction => { const user = hint?.packedUsers != null @@ -137,7 +136,7 @@ export class NotificationEntityService implements OnModuleInit { users, }); } - // #endregion + //#endregion const needsRole = notification.type === 'roleAssigned'; const role = needsRole ? await this.roleEntityService.pack(notification.roleId) : undefined; @@ -146,6 +145,13 @@ export class NotificationEntityService implements OnModuleInit { return null; } + const needsChatRoomInvitation = notification.type === 'chatRoomInvitationReceived'; + const chatRoomInvitation = needsChatRoomInvitation ? await this.chatEntityService.packRoomInvitation(notification.invitationId, { id: meId }) : undefined; + // if the invitation has been deleted, don't show this notification + if (needsChatRoomInvitation && !chatRoomInvitation) { + return null; + } + return await awaitAll({ id: notification.id, createdAt: new Date(notification.createdAt).toISOString(), @@ -159,6 +165,9 @@ export class NotificationEntityService implements OnModuleInit { ...(notification.type === 'roleAssigned' ? { role: role, } : {}), + ...(notification.type === 'chatRoomInvitationReceived' ? { + invitation: chatRoomInvitation, + } : {}), ...(notification.type === 'followRequestAccepted' ? { message: notification.message, } : {}), @@ -236,7 +245,7 @@ export class NotificationEntityService implements OnModuleInit { public async pack( src: MiNotification | MiGroupedNotification, meId: MiUser['id'], - + options: { checkValidNotifier?: boolean; }, diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts index 5772ace338..5764a307b0 100644 --- a/packages/backend/src/models/Notification.ts +++ b/packages/backend/src/models/Notification.ts @@ -75,6 +75,12 @@ export type MiNotification = { id: string; createdAt: string; roleId: MiRole['id']; +} | { + type: 'chatRoomInvitationReceived'; + id: string; + createdAt: string; + notifierId: MiUser['id']; + invitationId: string; } | { type: 'achievementEarned'; id: string; diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index 1638b2b3c7..7f23d2d6a1 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -287,6 +287,21 @@ export const packedNotificationSchema = { optional: false, nullable: false, }, }, + }, { + type: 'object', + properties: { + ...baseSchema.properties, + type: { + type: 'string', + optional: false, nullable: false, + enum: ['chatRoomInvitationReceived'], + }, + invitation: { + type: 'object', + ref: 'ChatRoomInvitation', + optional: false, nullable: false, + }, + }, }, { type: 'object', properties: { diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index b10430782b..0094f25e34 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -608,6 +608,7 @@ export const packedMeDetailedOnlySchema = { receiveFollowRequest: { optional: true, ...notificationRecieveConfig }, followRequestAccepted: { optional: true, ...notificationRecieveConfig }, roleAssigned: { optional: true, ...notificationRecieveConfig }, + chatRoomInvitationReceived: { optional: true, ...notificationRecieveConfig }, achievementEarned: { optional: true, ...notificationRecieveConfig }, app: { optional: true, ...notificationRecieveConfig }, test: { optional: true, ...notificationRecieveConfig }, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 655bd32bce..1ba6853dbe 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -106,6 +106,7 @@ export const meta = { receiveFollowRequest: { optional: true, ...notificationRecieveConfig }, followRequestAccepted: { optional: true, ...notificationRecieveConfig }, roleAssigned: { optional: true, ...notificationRecieveConfig }, + chatRoomInvitationReceived: { optional: true, ...notificationRecieveConfig }, achievementEarned: { optional: true, ...notificationRecieveConfig }, app: { optional: true, ...notificationRecieveConfig }, test: { optional: true, ...notificationRecieveConfig }, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index baf397bf06..082d97f5d4 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -212,6 +212,7 @@ export const paramDef = { receiveFollowRequest: notificationRecieveConfig, followRequestAccepted: notificationRecieveConfig, roleAssigned: notificationRecieveConfig, + chatRoomInvitationReceived: notificationRecieveConfig, achievementEarned: notificationRecieveConfig, app: notificationRecieveConfig, test: notificationRecieveConfig, diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index c6b1035554..4e215c93c6 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -15,6 +15,7 @@ * receiveFollowRequest - フォローリクエストされた * followRequestAccepted - 自分の送ったフォローリクエストが承認された * roleAssigned - ロールが付与された + * chatRoomInvitationReceived - チャットルームに招待された * achievementEarned - 実績を獲得 * exportCompleted - エクスポートが完了 * login - ログイン @@ -34,6 +35,7 @@ export const notificationTypes = [ 'receiveFollowRequest', 'followRequestAccepted', 'roleAssigned', + 'chatRoomInvitationReceived', 'achievementEarned', 'exportCompleted', 'login', diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 0241446784..fa60476b69 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -66,6 +66,7 @@ export const notificationTypes = [ 'receiveFollowRequest', 'followRequestAccepted', 'roleAssigned', + 'chatRoomInvitationReceived', 'achievementEarned', 'exportCompleted', 'login', diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 2824701579..c2e8b8e2fe 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -43,6 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only + From ed86b1706de715925be2d3a22ae8f68cdc12195b Mon Sep 17 00:00:00 2001 From: Acid Chicken Date: Wed, 26 Mar 2025 12:17:56 +0900 Subject: [PATCH 309/405] ci(storybook): prevent running for bots --- .github/workflows/storybook.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml index 6608761c13..07f196b7b8 100644 --- a/.github/workflows/storybook.yml +++ b/.github/workflows/storybook.yml @@ -5,13 +5,15 @@ on: branches: - master - develop - - dev/storybook8 # for testing pull_request_target: branches-ignore: # Since pull requests targets master mostly is the "develop" branch. # Storybook CI is checked on the "push" event of "develop" branch so it would cause a duplicate build. # This is a waste of chromatic build quota, so we don't run storybook CI on pull requests targets master. - master + # Neither Dependabot nor Renovate will change the actual behavior for components. + - dependabot/** + - renovate/** jobs: build: From c29a5764d3836bc05906036be20fab2eeea8b85b Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 16:51:08 +0900 Subject: [PATCH 310/405] refactor(backend): better method name --- packages/backend/src/core/QueryService.ts | 4 ++-- packages/backend/src/core/SearchService.ts | 4 ++-- packages/backend/src/server/api/endpoints/antennas/notes.ts | 4 ++-- .../backend/src/server/api/endpoints/channels/timeline.ts | 4 ++-- packages/backend/src/server/api/endpoints/clips/notes.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/children.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/global-timeline.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/local-timeline.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/mentions.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/renotes.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/replies.ts | 4 ++-- .../backend/src/server/api/endpoints/notes/search-by-tag.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/timeline.ts | 4 ++-- .../src/server/api/endpoints/notes/user-list-timeline.ts | 4 ++-- packages/backend/src/server/api/endpoints/roles/notes.ts | 4 ++-- packages/backend/src/server/api/endpoints/users/notes.ts | 4 ++-- .../backend/src/server/api/endpoints/users/recommendation.ts | 2 +- 18 files changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index c4feeaf971..412ab33b3f 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -69,7 +69,7 @@ export class QueryService { // ここでいうBlockedは被Blockedの意 @bindThis - public generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }): void { + public generateBlockedUserQueryForNotes(q: SelectQueryBuilder, me: { id: MiUser['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); @@ -127,7 +127,7 @@ export class QueryService { } @bindThis - public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void { + public generateMutedUserQueryForNotes(q: SelectQueryBuilder, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts index bc62559e46..aa787c93de 100644 --- a/packages/backend/src/core/SearchService.ts +++ b/packages/backend/src/core/SearchService.ts @@ -234,8 +234,8 @@ export class SearchService { } this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); return query.limit(pagination.limit).getMany(); } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 727697ea14..4b8543c2d1 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -109,8 +109,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); const notes = await query.getMany(); if (sinceId != null && untilId == null) { diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index d4fd75e049..cec5f8fd9c 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -122,8 +122,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('note.channel', 'channel'); if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); } //#endregion diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 943c31c894..7638aae442 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -87,8 +87,8 @@ export default class extends Endpoint { // eslint- if (me) { this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); } const notes = await query diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 0c6533d336..e73c98282c 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -71,8 +71,8 @@ export default class extends Endpoint { // eslint- this.queryService.generateVisibilityQuery(query, me); if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); } const notes = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 258a0bfb8f..8d38bb1c65 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -79,8 +79,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); if (me) { - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index aed9065bf9..99d1c9f19c 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -243,8 +243,8 @@ export default class extends Endpoint { // eslint- } this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.includeMyRenotes === false) { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 0b48f2c78b..97acf2ad39 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -156,8 +156,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.withFiles) { diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 18a3915ab5..bbb63646e9 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -72,9 +72,9 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateMutedNoteThreadQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); if (ps.visibility) { query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index ffe1ee6eb8..b34d9261a1 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -72,8 +72,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); const renotes = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 5f32332a6a..f36af1a328 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -56,8 +56,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 626ff080c7..c45851548a 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -81,8 +81,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQueryForNotes(query, me); + if (me) this.queryService.generateBlockedUserQueryForNotes(query, me); try { if (ps.tag) { diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 7cb11cc1eb..a88b28892e 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -199,8 +199,8 @@ export default class extends Endpoint { // eslint- })); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.includeMyRenotes === false) { diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 87f9b322a6..80f1c69b25 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -184,8 +184,8 @@ export default class extends Endpoint { // eslint- })); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.includeMyRenotes === false) { diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 71f2782a5d..6cd9f80929 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -102,8 +102,8 @@ export default class extends Endpoint { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); const notes = await query.getMany(); notes.sort((a, b) => a.id > b.id ? -1 : 1); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index e9c334057e..f5b7a07b01 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -185,8 +185,8 @@ export default class extends Endpoint { // eslint- this.queryService.generateVisibilityQuery(query, me); if (me) { - this.queryService.generateMutedUserQuery(query, me, { id: ps.userId }); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId }); + this.queryService.generateBlockedUserQueryForNotes(query, me); } if (ps.withFiles) { diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 5b3b4527f7..5b1c6b514b 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -63,7 +63,7 @@ export default class extends Endpoint { // eslint- this.queryService.generateMutedUserQueryForUsers(query, me); this.queryService.generateBlockQueryForUsers(query, me); - this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateBlockedUserQueryForNotes(query, me); const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') From b95da9c9a46d55c673787ed78526afbc9107785c Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:12:23 +0900 Subject: [PATCH 311/405] =?UTF-8?q?enhance(backend):=20=E3=83=9F=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E6=A4=9C=E7=B4=A2=E3=81=AE=E7=B5=90=E6=9E=9C=E3=81=8B?= =?UTF-8?q?=E3=82=89=E9=99=A4=E5=A4=96=E3=81=99=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 | 1 + .../backend/src/core/UserSearchService.ts | 100 +++++++++++++++++- .../users/search-by-username-and-host.ts | 2 +- .../src/server/api/endpoints/users/search.ts | 81 ++------------ 4 files changed, 107 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff95b6b5dc..6d94dbe213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - メッセージにはリアクションも可能です - Enhance: セキュリティを強化するため、ジョブキューのダッシュボード(bull-board)統合が削除されました。 - Misskeyネイティブでダッシュボードを実装予定です +- Enhance: ミュートしているユーザーをユーザー検索の結果から除外するように ### Client - Feat: 設定の管理が強化されました diff --git a/packages/backend/src/core/UserSearchService.ts b/packages/backend/src/core/UserSearchService.ts index 0d03cf6ee0..4be7bd9bdb 100644 --- a/packages/backend/src/core/UserSearchService.ts +++ b/packages/backend/src/core/UserSearchService.ts @@ -6,7 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets, SelectQueryBuilder } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { type FollowingsRepository, MiUser, type UsersRepository } from '@/models/_.js'; +import { type FollowingsRepository, MiUser, type MutingsRepository, type UserProfilesRepository, type UsersRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import type { Config } from '@/config.js'; @@ -22,10 +22,19 @@ export class UserSearchService { constructor( @Inject(DI.config) private config: Config, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, + + @Inject(DI.mutingsRepository) + private mutingsRepository: MutingsRepository, + private userEntityService: UserEntityService, ) { } @@ -58,7 +67,7 @@ export class UserSearchService { * @see {@link UserSearchService#buildSearchUserNoLoginQueries} */ @bindThis - public async search( + public async searchByUsernameAndHost( params: { username?: string | null, host?: string | null, @@ -202,4 +211,91 @@ export class UserSearchService { return userQuery; } + + @bindThis + public async search(query: string, meId: MiUser['id'] | null, options: Partial<{ + limit: number; + offset: number; + origin: 'local' | 'remote' | 'combined'; + }> = {}) { + const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 + + const isUsername = query.startsWith('@') && !query.includes(' ') && query.indexOf('@', 1) === -1; + + let users: MiUser[] = []; + + const mutingQuery = meId == null ? null : this.mutingsRepository.createQueryBuilder('muting') + .select('muting.muteeId') + .where('muting.muterId = :muterId', { muterId: meId }); + + const nameQuery = this.usersRepository.createQueryBuilder('user') + .where(new Brackets(qb => { + qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' }); + + if (isUsername) { + qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(query.replace('@', '').toLowerCase()) + '%' }); + } else if (this.userEntityService.validateLocalUsername(query)) { // Also search username if it qualifies as username + qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(query.toLowerCase()) + '%' }); + } + })) + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })) + .andWhere('user.isSuspended = FALSE'); + + if (mutingQuery) { + nameQuery.andWhere(`user.id NOT IN (${mutingQuery.getQuery()})`); + nameQuery.setParameters(mutingQuery.getParameters()); + } + + if (options.origin === 'local') { + nameQuery.andWhere('user.host IS NULL'); + } else if (options.origin === 'remote') { + nameQuery.andWhere('user.host IS NOT NULL'); + } + + users = await nameQuery + .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') + .limit(options.limit) + .offset(options.offset) + .getMany(); + + if (users.length < (options.limit ?? 30)) { + const profQuery = this.userProfilesRepository.createQueryBuilder('prof') + .select('prof.userId') + .where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' }); + + if (mutingQuery) { + profQuery.andWhere(`prof.userId NOT IN (${mutingQuery.getQuery()})`); + profQuery.setParameters(mutingQuery.getParameters()); + } + + if (options.origin === 'local') { + profQuery.andWhere('prof.userHost IS NULL'); + } else if (options.origin === 'remote') { + profQuery.andWhere('prof.userHost IS NOT NULL'); + } + + const userQuery = this.usersRepository.createQueryBuilder('user') + .where(`user.id IN (${ profQuery.getQuery() })`) + .andWhere(new Brackets(qb => { + qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })) + .andWhere('user.isSuspended = FALSE') + .setParameters(profQuery.getParameters()); + + users = users.concat(await userQuery + .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') + .limit(options.limit) + .offset(options.offset) + .getMany(), + ); + } + + return users; + } } diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 8ff952dcb5..134f1a8e87 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { // eslint- private userSearchService: UserSearchService, ) { super(meta, paramDef, (ps, me) => { - return this.userSearchService.search({ + return this.userSearchService.searchByUsernameAndHost({ username: ps.username, host: ps.host, }, { diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 0b0136066d..5d36847e03 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -3,14 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, UserProfilesRepository } from '@/models/_.js'; -import type { MiUser } from '@/models/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; -import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; +import { UserSearchService } from '@/core/UserSearchService.js'; export const meta = { tags: ['users'], @@ -45,79 +42,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - private userEntityService: UserEntityService, + private userSearchService: UserSearchService, ) { super(meta, paramDef, async (ps, me) => { - const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 - - ps.query = ps.query.trim(); - const isUsername = ps.query.startsWith('@') && !ps.query.includes(' ') && ps.query.indexOf('@', 1) === -1; - - let users: MiUser[] = []; - - const nameQuery = this.usersRepository.createQueryBuilder('user') - .where(new Brackets(qb => { - qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }); - - if (isUsername) { - qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(ps.query.replace('@', '').toLowerCase()) + '%' }); - } else if (this.userEntityService.validateLocalUsername(ps.query)) { // Also search username if it qualifies as username - qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(ps.query.toLowerCase()) + '%' }); - } - })) - .andWhere(new Brackets(qb => { - qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })) - .andWhere('user.isSuspended = FALSE'); - - if (ps.origin === 'local') { - nameQuery.andWhere('user.host IS NULL'); - } else if (ps.origin === 'remote') { - nameQuery.andWhere('user.host IS NOT NULL'); - } - - users = await nameQuery - .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .limit(ps.limit) - .offset(ps.offset) - .getMany(); - - if (users.length < ps.limit) { - const profQuery = this.userProfilesRepository.createQueryBuilder('prof') - .select('prof.userId') - .where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(ps.query) + '%' }); - - if (ps.origin === 'local') { - profQuery.andWhere('prof.userHost IS NULL'); - } else if (ps.origin === 'remote') { - profQuery.andWhere('prof.userHost IS NOT NULL'); - } - - const query = this.usersRepository.createQueryBuilder('user') - .where(`user.id IN (${ profQuery.getQuery() })`) - .andWhere(new Brackets(qb => { - qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })) - .andWhere('user.isSuspended = FALSE') - .setParameters(profQuery.getParameters()); - - users = users.concat(await query - .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .limit(ps.limit) - .offset(ps.offset) - .getMany(), - ); - } + const users = await this.userSearchService.search(ps.query.trim(), me?.id ?? null, { + offset: ps.offset, + limit: ps.limit, + origin: ps.origin, + }); return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' }); }); From f7e901deb2a3d7457231c9a240d13d332e24461a Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:30:04 +0900 Subject: [PATCH 312/405] test fixes --- .../backend/test/unit/UserSearchService.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/backend/test/unit/UserSearchService.ts b/packages/backend/test/unit/UserSearchService.ts index 66a7f39ff1..697425beb8 100644 --- a/packages/backend/test/unit/UserSearchService.ts +++ b/packages/backend/test/unit/UserSearchService.ts @@ -134,13 +134,13 @@ describe('UserSearchService', () => { await app.close(); }); - describe('search', () => { + describe('searchByUsernameAndHost', () => { test('フォロー中のアクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => { await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setActive([alice, alyce, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alycia, alysha, alyson]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -154,7 +154,7 @@ describe('UserSearchService', () => { await createFollowings(root, [alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -168,7 +168,7 @@ describe('UserSearchService', () => { await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alice, alyce, alycia]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -181,7 +181,7 @@ describe('UserSearchService', () => { test('フォローしていない非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => { await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, @@ -195,7 +195,7 @@ describe('UserSearchService', () => { await setActive([root, alyssa, bob, bobbi, alyce, alycia]); await setInactive([alyson, alice, alysha, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { }, { limit: 100 }, root, @@ -216,7 +216,7 @@ describe('UserSearchService', () => { await setActive([alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setInactive([alice, alyce, alycia]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, ); @@ -228,7 +228,7 @@ describe('UserSearchService', () => { test('[非ログイン] 非アクティブユーザのうち、"al"から始まる人が全員ヒットする', async () => { await setInactive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, ); @@ -240,7 +240,7 @@ describe('UserSearchService', () => { await createFollowings(root, [alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al', host: 'exam' }, { limit: 100 }, root, @@ -253,7 +253,7 @@ describe('UserSearchService', () => { await setActive([alice, alyce, alycia, alysha, alyson, alyssa, bob, bobbi, bobbie, bobby]); await setSuspended([alice, alyce, alycia]); - const result = await service.search( + const result = await service.searchByUsernameAndHost( { username: 'al' }, { limit: 100 }, root, From a78db27a3ca4e2125b217bdef4a0920d33029423 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:30:06 +0900 Subject: [PATCH 313/405] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d94dbe213..ff77553206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - 投稿フォームをリセットできるように - 文字数カウントを復活 - Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように +- Enhance: 全体的なブラッシュアップ - Fix: テーマ切り替え時に一部の色が変わらない問題を修正 ### Server From e40846c46bdcbb8be0c15ac7e874e8ec2291f519 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 27 Mar 2025 19:50:03 +0900 Subject: [PATCH 314/405] fix e2e test --- packages/backend/test/e2e/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index f82d4c6a30..e04d258c0d 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -732,7 +732,7 @@ describe('ユーザー', () => { }); test.each([ { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: () => userMutedByAlice }, + { label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true }, { label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice }, { label: 'ブロックしてきているユーザーが含まれる', user: () => userBlockingAlice }, { label: '承認制ユーザーが含まれる', user: () => userLocking }, From 61e09d483e35b6d464a04a2d04633fd6b878b475 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Fri, 28 Mar 2025 09:22:37 +0900 Subject: [PATCH 315/405] refactor --- .../src/components/global/MkPageHeader.vue | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index d5680b8413..6a926f7718 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index a1e1460da1..4ed4cdc773 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -122,11 +122,6 @@ const menuDef = computed(() => [{ text: i18n.ts.sounds, to: '/settings/sounds', active: currentPage.value?.route.name === 'sounds', - }, { - icon: 'ti ti-accessible', - text: i18n.ts.accessibility, - to: '/settings/accessibility', - active: currentPage.value?.route.name === 'accessibility', }, { icon: 'ti ti-plug', text: i18n.ts.plugins, diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index 7e7ed21dd5..a4e9616325 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -418,6 +418,116 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + +
+ + {{ i18n.ts._settings.accessibilityBanner }} + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ @@ -671,6 +781,18 @@ const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies'); const chatShowSenderName = prefer.model('chat.showSenderName'); const chatSendOnEnter = prefer.model('chat.sendOnEnter'); const useStickyIcons = prefer.model('useStickyIcons'); +const reduceAnimation = prefer.model('animation', v => !v, v => !v); +const animatedMfm = prefer.model('animatedMfm'); +const disableShowingAnimatedImages = prefer.model('disableShowingAnimatedImages'); +const keepScreenOn = prefer.model('keepScreenOn'); +const enableHorizontalSwipe = prefer.model('enableHorizontalSwipe'); +const useNativeUiForVideoAudioPlayer = prefer.model('useNativeUiForVideoAudioPlayer'); +const contextMenu = prefer.model('contextMenu'); +const menuStyle = prefer.model('menuStyle'); +const makeEveryTextElementsSelectable = prefer.model('makeEveryTextElementsSelectable'); + +const fontSize = ref(miLocalStorage.getItem('fontSize')); +const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); @@ -678,6 +800,22 @@ watch(lang, () => { miLocalStorage.removeItem('localeVersion'); }); +watch(fontSize, () => { + if (fontSize.value == null) { + miLocalStorage.removeItem('fontSize'); + } else { + miLocalStorage.setItem('fontSize', fontSize.value); + } +}); + +watch(useSystemFont, () => { + if (useSystemFont.value) { + miLocalStorage.setItem('useSystemFont', 't'); + } else { + miLocalStorage.removeItem('useSystemFont'); + } +}); + watch([ hemisphere, lang, @@ -700,6 +838,11 @@ watch([ enableSeasonalScreenEffect, chatShowSenderName, useStickyIcons, + keepScreenOn, + contextMenu, + fontSize, + useSystemFont, + makeEveryTextElementsSelectable, ], async () => { await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true }); }); diff --git a/packages/frontend/src/router.definition.ts b/packages/frontend/src/router.definition.ts index 0585a31fd1..23118160e2 100644 --- a/packages/frontend/src/router.definition.ts +++ b/packages/frontend/src/router.definition.ts @@ -128,10 +128,6 @@ export const ROUTE_DEF = [{ path: '/sounds', name: 'sounds', component: page(() => import('@/pages/settings/sounds.vue')), - }, { - path: '/accessibility', - name: 'accessibility', - component: page(() => import('@/pages/settings/accessibility.vue')), }, { path: '/plugin/install', name: 'plugin', diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index 9e1f41e3c7..b77957fe84 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -489,17 +489,79 @@ export const searchIndexes: SearchIndexItem[] = [ id: '96LnS1sxB', children: [ { - id: '5h8vhCX1S', + id: 'vPQPvmntL', + label: i18n.ts.reduceUiAnimation, + keywords: ['animation', 'motion', 'reduce'], + }, + { + id: 'wfJ91vwzq', + label: i18n.ts.disableShowingAnimatedImages, + keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'], + }, + { + id: '42b1L4xdq', + label: i18n.ts.enableAnimatedMfm, + keywords: ['mfm', 'enable', 'show', 'animated'], + }, + { + id: 'dLkRNHn3k', + label: i18n.ts.enableHorizontalSwipe, + keywords: ['swipe', 'horizontal', 'tab'], + }, + { + id: 'BvooTWFW5', + label: i18n.ts.keepScreenOn, + keywords: ['keep', 'screen', 'display', 'on'], + }, + { + id: 'yzbghkAq0', + label: i18n.ts.useNativeUIForVideoAudioPlayer, + keywords: ['native', 'system', 'video', 'audio', 'player', 'media'], + }, + { + id: 'aSbKFHbOy', + label: i18n.ts._settings.makeEveryTextElementsSelectable, + keywords: ['text', 'selectable'], + }, + { + id: 'bTcAsPvNz', + label: i18n.ts.menuStyle, + keywords: ['menu', 'style', 'popup', 'drawer'], + }, + { + id: 'lSVBaLnyW', + label: i18n.ts._contextMenu.title, + keywords: ['contextmenu', 'system', 'native'], + }, + { + id: 'pec0uMPq5', + label: i18n.ts.fontSize, + keywords: ['font', 'size'], + }, + { + id: 'Eh7vTluDO', + label: i18n.ts.useSystemFont, + keywords: ['font', 'system', 'native'], + }, + ], + label: i18n.ts.accessibility, + keywords: ['accessibility', i18n.ts._settings.accessibilityBanner], + }, + { + id: 'vTRSKf1JA', + children: [ + { + id: '2VjlA02wB', label: i18n.ts.turnOffToImprovePerformance, keywords: ['blur'], }, { - id: 'Cbjosj3TG', + id: 'f6J0lmg1g', label: i18n.ts.turnOffToImprovePerformance, keywords: ['blur', 'modal'], }, { - id: 'BKndoHcCj', + id: 'hQqXhfNg8', label: i18n.ts.turnOffToImprovePerformance, keywords: ['sticky'], }, @@ -508,55 +570,55 @@ export const searchIndexes: SearchIndexItem[] = [ keywords: ['performance'], }, { - id: '4yCgcFElF', + id: 'utM8dEobb', label: i18n.ts.dataSaver, keywords: ['datasaver'], }, { - id: 'DILm2LlCn', + id: 'gOUvwkE9t', children: [ { - id: 'Fd0rFTSry', + id: 'iUMUvFURf', label: i18n.ts.squareAvatars, keywords: ['avatar', 'icon', 'square'], }, { - id: 'xNsLokqeA', + id: 'ceyPO9Ywi', label: i18n.ts.seasonalScreenEffect, keywords: ['effect', 'show'], }, { - id: 'sZcalFBE8', + id: 'ztwIlsXhP', label: i18n.ts.openImageInNewTab, keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'], }, { - id: 'Eh7vTluDO', + id: 'vLSsQbZEo', label: i18n.ts.withRepliesByDefaultForNewlyFollowed, keywords: ['follow', 'replies'], }, { - id: 'vTRSKf1JA', + id: 'hQt85bBIX', label: i18n.ts.whenServerDisconnected, keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'], }, { - id: 'zlO5cBZFH', + id: 'C9SyK2m0', label: i18n.ts.numberOfPageCache, keywords: ['cache', 'page'], }, { - id: 'huQ8nc4iD', + id: '2U0iVUtfW', label: i18n.ts.forceShowAds, keywords: ['ad', 'show'], }, { - id: 'nJWfqwQ4R', + id: '1rA7ADEXY', label: i18n.ts.hemisphere, keywords: [], }, { - id: 'kwEEgTlwR', + id: 'vRayx89Rt', label: i18n.ts.additionalEmojiDictionary, keywords: ['emoji', 'dictionary', 'additional', 'extra'], }, @@ -906,70 +968,6 @@ export const searchIndexes: SearchIndexItem[] = [ path: '/settings/account-data', icon: 'ti ti-package', }, - { - id: 'f08Mi1Uwn', - children: [ - { - id: 'C5dRH2Ypy', - label: i18n.ts.reduceUiAnimation, - keywords: ['animation', 'motion', 'reduce'], - }, - { - id: '5mZxz2cru', - label: i18n.ts.disableShowingAnimatedImages, - keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'], - }, - { - id: 'c0Iy5hL5o', - label: i18n.ts.enableAnimatedMfm, - keywords: ['mfm', 'enable', 'show', 'animated'], - }, - { - id: '4HYFjs2Nv', - label: i18n.ts.enableHorizontalSwipe, - keywords: ['swipe', 'horizontal', 'tab'], - }, - { - id: 'kYVJ3SVNq', - label: i18n.ts.keepScreenOn, - keywords: ['keep', 'screen', 'display', 'on'], - }, - { - id: 'w4Bv0meAt', - label: i18n.ts.useNativeUIForVideoAudioPlayer, - keywords: ['native', 'system', 'video', 'audio', 'player', 'media'], - }, - { - id: 'b1GYEEJeh', - label: i18n.ts._settings.makeEveryTextElementsSelectable, - keywords: ['text', 'selectable'], - }, - { - id: 'vVLxwINTJ', - label: i18n.ts.menuStyle, - keywords: ['menu', 'style', 'popup', 'drawer'], - }, - { - id: '14cMhMLHL', - label: i18n.ts._contextMenu.title, - keywords: ['contextmenu', 'system', 'native'], - }, - { - id: 'oSo4LXMX9', - label: i18n.ts.fontSize, - keywords: ['font', 'size'], - }, - { - id: '7LQSAThST', - label: i18n.ts.useSystemFont, - keywords: ['font', 'system', 'native'], - }, - ], - label: i18n.ts.accessibility, - keywords: ['accessibility', i18n.ts._settings.accessibilityBanner], - path: '/settings/accessibility', - icon: 'ti ti-accessible', - }, ] as const; export type SearchIndex = typeof searchIndexes; From c06d0b9b42f426078b549354476606c3f4d33b93 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 11:27:35 +0900 Subject: [PATCH 368/405] enhance(frontend): organize settings page --- .../src/pages/settings/preferences.vue | 18 +++++++-------- .../utility/autogen/settings-search-index.ts | 22 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue index a4e9616325..86ae8af213 100644 --- a/packages/frontend/src/pages/settings/preferences.vue +++ b/packages/frontend/src/pages/settings/preferences.vue @@ -104,15 +104,6 @@ SPDX-License-Identifier: AGPL-3.0-only
- - - - - - {{ i18n.ts.add }} - {{ i18n.ts.remove }} - -
@@ -164,6 +155,15 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + {{ i18n.ts.add }} + {{ i18n.ts.remove }} + +

diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts index b77957fe84..df621beb7d 100644 --- a/packages/frontend/src/utility/autogen/settings-search-index.ts +++ b/packages/frontend/src/utility/autogen/settings-search-index.ts @@ -323,43 +323,43 @@ export const searchIndexes: SearchIndexItem[] = [ label: i18n.ts.emojiStyle, keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'], }, - { - id: 'wo0s0CaI1', - label: i18n.ts.pinnedList, - keywords: ['pinned', 'list'], - }, ], label: i18n.ts.general, keywords: ['general'], }, { - id: 'l78F2W9Ok', + id: '5G6O6qdis', children: [ { - id: 'iOJ3Crlky', + id: 'khT3n6byY', label: i18n.ts.showFixedPostForm, keywords: ['post', 'form', 'timeline'], }, { - id: 'CQldliCSi', + id: 'q5ElfNSou', label: i18n.ts.showFixedPostFormInChannel, keywords: ['post', 'form', 'timeline', 'channel'], }, { - id: 'd2H4E5ys6', + id: '3GcWIaZf8', label: i18n.ts.collapseRenotes, keywords: ['renote', i18n.ts.collapseRenotesDescription], }, { - id: 'yb11lSY1G', + id: 'd2H4E5ys6', label: i18n.ts.showGapBetweenNotesInTimeline, keywords: ['note', 'timeline', 'gap'], }, { - id: 'fL49Zxe9i', + id: '1LHOhDKGW', label: i18n.ts.disableStreamingTimeline, keywords: ['disable', 'streaming', 'timeline'], }, + { + id: 'DSzwvTp7i', + label: i18n.ts.pinnedList, + keywords: ['pinned', 'list'], + }, { id: 'ykifk3NHS', label: i18n.ts.showNoteActionsOnlyHover, From 88c743aa3386049f2b2de1afe7fb5c125e0581f1 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 11:45:41 +0900 Subject: [PATCH 369/405] chore(frontend): remove unused style --- packages/frontend/src/components/MkNote.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 8840a6a8bf..66a70c9be3 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -658,7 +658,6 @@ function emitUpdReaction(emoji: string, delta: number) { diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue deleted file mode 100644 index 43d24da28b..0000000000 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue deleted file mode 100644 index c984cde342..0000000000 --- a/packages/frontend/src/ui/classic.vue +++ /dev/null @@ -1,326 +0,0 @@ - - - - - - - From 17f3113b92a4ff79b4f7588663c7f08ae955325d Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 14:30:04 +0900 Subject: [PATCH 373/405] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkPostForm.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 78c69306b0..e43ff65e1d 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1314,7 +1314,7 @@ html[data-color-scheme=light] .preview { padding: 0 24px; margin: 0; width: 100%; - font-size: 16px; + font-size: 110%; border: none; border-radius: 0; background: transparent; From 0655c8a29bb6122cdda7235720a1ef220bb0a293 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 14:33:52 +0900 Subject: [PATCH 374/405] clean up --- packages/frontend/src/pages/settings/navbar.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue index f944490a66..91968c5300 100644 --- a/packages/frontend/src/pages/settings/navbar.vue +++ b/packages/frontend/src/pages/settings/navbar.vue @@ -40,8 +40,6 @@ SPDX-License-Identifier: AGPL-3.0-only - - From 303b62aff3c01cd0945ccef2e0fea17dbd27da40 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 14:34:32 +0900 Subject: [PATCH 375/405] New Crowdin updates (#15721) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Catalan) * New translations ja-jp.yml (Chinese Traditional) * New translations ja-jp.yml (German) * New translations ja-jp.yml (German) * New translations ja-jp.yml (German) * New translations ja-jp.yml (German) * New translations ja-jp.yml (Chinese Simplified) * New translations ja-jp.yml (English) --- locales/ca-ES.yml | 1 + locales/de-DE.yml | 183 +++++++++++++++++++++++++++++++++++++++++++++- locales/en-US.yml | 1 + locales/zh-CN.yml | 1 + locales/zh-TW.yml | 1 + 5 files changed, 186 insertions(+), 1 deletion(-) diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index b371a6b025..fde5b2aad9 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "Línia de temps i nota" makeEveryTextElementsSelectable: "Fes que tots els elements del text siguin seleccionables" makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions." + useStickyIcons: "Utilitza icones fixes" showNavbarSubButtons: "Mostrar sub botons a la barra de navegació " ifOn: "Quan s'encén " ifOff: "Quan s'apaga " diff --git a/locales/de-DE.yml b/locales/de-DE.yml index b32f4512be..ee0a97098c 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -301,6 +301,7 @@ uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis das Hochladen abgeschl explore: "Erkunden" messageRead: "Gelesen" noMoreHistory: "Kein weiterer Verlauf vorhanden" +startChat: "Chat starten" nUsersRead: "Von {n} Benutzern gelesen" agreeTo: "Ich stimme {0} zu" agree: "Zustimmen" @@ -1256,7 +1257,7 @@ replaying: "Aufzeichnung" endReplay: "Aufzeichnung verlassen" copyReplayData: "Aufzeichnung kopieren" ranking: "Rangliste" -lastNDays: "Letzten {n} Tage" +lastNDays: "Letzte {n} Tage" backToTitle: "Zurück zum Startbildschirm" hemisphere: "Hemisphäre" withSensitive: "Zeige \"sensitive Inhalte\" an" @@ -1318,25 +1319,93 @@ noName: "Kein Name" skip: "Überspringen" restore: "Wiederherstellen" syncBetweenDevices: "Zwischen Geräten synchronisieren" +preferenceSyncConflictTitle: "Der konfigurierte Wert ist auf dem Server bereits vorhanden." +preferenceSyncConflictText: "Die Einstellungen mit aktivierter Synchronisierung werden ihre Werte auf dem Server speichern. Es gibt jedoch bereits Werte auf dem Server. Welche Einstellungswerte sollen überschrieben werden?" +preferenceSyncConflictChoiceServer: "Konfigurierte Werte auf dem Server" +preferenceSyncConflictChoiceDevice: "Konfigurierte Werte auf dem Gerät" +preferenceSyncConflictChoiceCancel: "Einrichten der Synchronisierung abbrechen" paste: "Einfügen" +emojiPalette: "Emoji-Palette" postForm: "Notizfenster" textCount: "Zeichenanzahl" information: "Über" +chat: "Chat" +migrateOldSettings: "Alte Client-Einstellungen migrieren" +migrateOldSettings_description: "Dies sollte normalerweise automatisch geschehen, aber wenn die Migration aus irgendeinem Grund nicht erfolgreich war, kannst du den Migrationsprozess selbst manuell auslösen. Die aktuellen Konfigurationsinformationen werden dabei überschrieben." +compress: "Komprimieren" _chat: + noMessagesYet: "Noch keine Nachrichten" + newMessage: "Neue Nachricht" + individualChat: "Privater Chat" + individualChat_description: "Führe einen privaten Chat mit einer anderen Person." + roomChat_description: "Ein Chat-Raum, an dem mehrere Personen teilnehmen können.\nDu kannst auch Personen einladen, die keine privaten Chats zulassen, wenn sie die Einladung annehmen." + createRoom: "Raum erstellen" + inviteUserToChat: "Lade Benutzer ein, um mit dem Chatten zu beginnen" + yourRooms: "Erstellte Räume" invitations: "Einladen" + noInvitations: "Keine Einladungen" + history: "Verlauf" noHistory: "Kein Verlauf gefunden" + noRooms: "Keine Räume gefunden" + inviteUser: "Benutzer einladen" + sentInvitations: "Verschickte Einladungen" + join: "Beitreten" + ignore: "Ignorieren" + leave: "Raum verlassen" members: "Mitglieder" + searchMessages: "Nachrichten suchen" home: "Startseite" send: "Senden" + newline: "Neue Zeile" + muteThisRoom: "Raum stummschalten" + deleteRoom: "Raum löschen" + chatNotAvailableForThisAccountOrServer: "Der Chat ist auf diesem Server oder für dieses Konto nicht aktiviert." + chatNotAvailableInOtherAccount: "Die Chatfunktion wurde vom anderen Benutzer deaktiviert." + cannotChatWithTheUser: "Starten eines Chats mit diesem Benutzer nicht möglich" + cannotChatWithTheUser_description: "Der Chat ist entweder nicht verfügbar oder die andere Seite hat den Chat nicht aktiviert." + chatWithThisUser: "Mit dem Benutzer chatten" + thisUserAllowsChatOnlyFromFollowers: "Dieser Benutzer nimmt nur Chats von Followern an." + thisUserAllowsChatOnlyFromFollowing: "Dieser Benutzer nimmt nur Chats von Benutzern an, denen er folgt." + thisUserAllowsChatOnlyFromMutualFollowing: "Dieser Benutzer akzeptiert nur Chats von Benutzern, die sich gegenseitig folgen." + thisUserNotAllowedChatAnyone: "Dieser Benutzer nimmt keine Chats von anderen Benutzern an." + chatAllowedUsers: "Wem das Chatten erlaubt werden soll" + chatAllowedUsers_note: "Du kannst unabhängig von dieser Einstellung mit allen Personen chatten, denen du eine Chat-Nachricht gesendet hast." + _chatAllowedUsers: + everyone: "Jeder" + followers: "Nur deine Follower" + following: "Nur Benutzer, denen du folgst" + mutual: "Nur Benutzer, die sich gegenseitig folgen" + none: "Niemand" _emojiPalette: palettes: "Palette" enableSyncBetweenDevicesForPalettes: "Synchronisierung der Paletten zwischen Geräten aktivieren" paletteForMain: "Hauptpalette" + paletteForReaction: "Reaktions-Palette" _settings: + driveBanner: "Du kannst den Drive verwalten und konfigurieren, die Auslastung überprüfen und Einstellungen für das Hochladen von Dateien vornehmen." + pluginBanner: "Du kannst die Funktionen des Clients mit Plugins erweitern. Plugins können installiert, individuell konfiguriert und verwaltet werden." api: "API" webhook: "Webhook" + serviceConnectionBanner: "Du kannst Zugriffstoken und Webhooks für die Integration mit externen Anwendungen und Diensten verwalten und konfigurieren." accountData: "Kontodaten" + accountDataBanner: "Export/Import und Verwaltung von Kontodatenarchiven." + muteAndBlockBanner: "Du kannst Einstellungen konfigurieren und verwalten, um Inhalte auszublenden und Aktionen für bestimmte Benutzer zu beschränken." + accessibilityBanner: "Die Clients können personalisiert und für eine optimale Nutzung im Hinblick auf ihre Darstellung und ihr Verhalten eingerichtet werden." + privacyBanner: "Du kannst Einstellungen für die Privatsphäre deines Kontos vornehmen, z. B. inwieweit Inhalte veröffentlicht werden, wie leicht sie zu finden sind und ob Follower genehmigt werden müssen." + securityBanner: "Du kannst Einstellungen für die Kontosicherheit konfigurieren, z. B. Passwörter, Anmeldemethoden, Authentifizierungs-Apps und Passkeys." + appearanceBanner: "Du kannst das Erscheinungsbild und die Anzeigeeinstellungen für den Client nach deinen Wünschen konfigurieren." + soundsBanner: "Du kannst die Einstellungen für die Wiedergabe von Klängen im Client konfigurieren." + timelineAndNote: "Chroniken und Notizen" + makeEveryTextElementsSelectable: "Alle Textelemente auswählbar machen" + makeEveryTextElementsSelectable_description: "Die Aktivierung kann in manchen Situationen die Benutzerfreundlichkeit beeinträchtigen." + ifOn: "Wenn eingeschaltet" + ifOff: "Wenn ausgeschaltet" + _chat: + showSenderName: "Name des Absenders anzeigen" + sendOnEnter: "Eingabetaste sendet Nachricht" _preferencesProfile: + profileName: "Profilname" + profileNameDescription: "Lege einen Namen fest, der dieses Gerät identifiziert." profileNameDescription2: "Beispiel: \"Haupt-PC\", \"Smartphone\"" _preferencesBackup: autoBackup: "Automatische Sicherung" @@ -1353,9 +1422,11 @@ _accountSettings: requireSigninToViewContentsDescription2: "Der Inhalt wird nicht in URL-Vorschauen (OGP), eingebettet in Webseiten oder auf Servern, die keine Zitate unterstützen, angezeigt." requireSigninToViewContentsDescription3: "Diese Einschränkungen gelten möglicherweise nicht für föderierte Inhalte von anderen Servern." makeNotesFollowersOnlyBefore: "Macht frühere Notizen nur für Follower sichtbar" + makeNotesFollowersOnlyBeforeDescription: "Solange diese Funktion aktiviert ist, sind Notizen, die nach dem eingestellten Datum und der eingestellten Zeit liegen oder die eingestellte Zeit abgelaufen ist, nur für Follower sichtbar. Bei Deaktivierung wird auch der öffentliche Status der Notiz wiederhergestellt." makeNotesHiddenBefore: "Frühere Notizen privat machen" makeNotesHiddenBeforeDescription: "" mayNotEffectForFederatedNotes: "Dies hat möglicherweise keine Auswirkungen auf Notizen, die an andere Server föderiert werden." + mayNotEffectSomeSituations: "Diese Einschränkungen sind vereinfacht. Sie gelten möglicherweise nicht in allen Situationen, z. B. bei der Anzeige auf einem fremden Server oder während der Moderation." notesOlderThanSpecifiedDateAndTime: "Notizen vor einem bestimmtem Datum und Uhrzeit" _abuseUserReport: forward: "Weiterleiten" @@ -1363,11 +1434,15 @@ _abuseUserReport: resolve: "lösen" accept: "Akzeptieren" reject: "Ablehnen" + resolveTutorial: "Wenn der Inhalt der Meldung rechtmäßig ist, wähle „Akzeptieren“, um sie als gelöst zu markieren.\nWenn der Inhalt der Meldung unzulässig ist, wähle „Ablehnen“, um sie zu ignorieren." _delivery: + status: "Auslieferungsstatus" stop: "Gesperrt" _type: none: "Wird veröffentlicht" manuallySuspended: "Manuell gesperrt" + goneSuspended: "Gesperrt wegen Löschung des Servers" + autoSuspendedForNotResponding: "Gesperrt, weil der Server nicht antwortet" _bubbleGame: howToPlay: "Wie man spielt" hold: "Halten" @@ -1377,6 +1452,8 @@ _bubbleGame: highScore: "Höchstpunktzahl" maxChain: "Maximale Anzahl an Verkettungen" yen: "{yen} Yen" + estimatedQty: "{qty} Stück" + scoreSweets: "{onigiriQtyWithUnit} Onigiri" _howToPlay: section1: "Passe die Position an und lasse das Objekt in das Spielfeld fallen." section2: "Wenn sich zwei Objekte der gleichen Art berühren, verwandeln sie sich in ein anderes Objekt und du bekommst Punkte." @@ -1434,15 +1511,20 @@ _initialTutorial: reactDone: "Du kannst eine Reaktion zurücknehmen, indem du auf den '-' Button drückst." _timeline: title: "So funktionieren die Chroniken" + description1: "Misskey stellt mehrere Chroniken bereit (einige können je nach den Richtlinien des Servers nicht verfügbar sein)." home: "Du kannst Beiträge von den Konten sehen, denen du folgst." local: "Du kannst Beiträge aller Benutzer auf diesem Server sehen." social: "Notizen von der Startseite und der lokalen Chronik werden angezeigt." global: "Du kannst Notizen von allen föderierten Servern sehen." description2: "Du kannst jederzeit am oberen Rand des Bildschirms zwischen den jeweiligen Chroniken wechseln." + description3: "Darüber hinaus gibt es Listen-Chroniken und Kanal-Chroniken. Weitere Einzelheiten findest du unter {link}." _postNote: + description1: "Wenn du eine Notiz auf Misskey veröffentlichst, stehen dir verschiedene Optionen zur Verfügung. Die Oberfläche sieht folgendermaßen aus." _visibility: description: "Du kannst einschränken, wer deine Notiz sehen kann." public: "Deine Notiz wird für alle Nutzer sichtbar sein." + home: "Nur auf der Startseite sichtbar. Kann von Followern, Profilbesuchern und durch Renotes gesehen werden." + followers: "Nur für Follower sichtbar. Nur Follower können es sehen und niemand sonst, und es kann nicht von anderen gerenoted werden." direct: "Die Notiz wird nur für den angegebenen Benutzer veröffentlicht und der Empfänger wird benachrichtigt. Kann anstelle von Direktnachrichten verwendet werden." doNotSendConfidencialOnDirect1: "Sei vorsichtig, wenn du sensible Informationen verschickst!" doNotSendConfidencialOnDirect2: "Die Administratoren des Servers können den Inhalt der Notiz sehen. Sei vorsichtig mit sensiblen Informationen, wenn du Direktnachrichten an Benutzer auf nicht vertrauenswürdigen Servern sendest." @@ -1453,8 +1535,10 @@ _initialTutorial: _exampleNote: cw: "Das wird dich bestimmt hungrig machen!" note: "Ich hatte gerade einen Donut mit Schokoladenüberzug 🍩😋" + useCases: "Dient zur Kennzeichnung von Notizen, wie sie in den Serverrichtlinien vorgeschrieben sind, oder zur eigenen Festlegung von Spoiler-Beiträgen oder sensiblem Text." _howToMakeAttachmentsSensitive: title: "Wie markiert man Anhänge als sensibel?" + description: "Markiere Anhänge als sensibel, die aufgrund von den Serverregeln nicht sichtbar sein sollen." tryThisFile: "Versuche, das angehängte Bild als sensibel zu markieren!" _exampleNote: note: "Ups, ich habe es vergeigt, den Natto-Deckel zu öffnen..." @@ -1465,7 +1549,9 @@ _initialTutorial: title: "Du hast das Tutorial abgeschlossen! 🎉" description: "Die hier beschriebenen Funktionen sind nur ein kleiner Teil dessen, was Misskey zu bieten hat; um mehr darüber zu erfahren, wie du Misskey benutzen kannst, besuche bitte {link}." _timelineDescription: + home: "In der Startseiten-Chronik kannst du Notizen von Konten sehen, denen du folgst." local: "In der lokalen Chronik siehst du Notizen von allen Benutzern auf diesem Server." + social: "Die soziale Chronik zeigt Notizen von der Startseite und der lokalen Chronik." global: "In der globalen Chronik siehst du Notizen von allen föderierten Servern." _serverRules: description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen." @@ -1483,6 +1569,8 @@ _serverSettings: fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. " reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis." inquiryUrl: "Kontakt-URL" + inquiryUrlDescription: "Gib eine URL für das Kontaktformular der Serverbetreiber oder eine Webseite an, die Kontaktinformationen enthält." + openRegistration: "Registrierung von Konten aktivieren" openRegistrationWarning: "Das Aktivieren von Registrierungen ist riskant. Es wird empfohlen, sie nur dann zu aktivieren, wenn der Server ständig überwacht wird und im Falle eines Problems sofort reagiert werden kann." thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Wenn über einen bestimmten Zeitraum keine Moderatorenaktivität festgestellt wird, wird diese Einstellung automatisch deaktiviert, um Spam zu verhindern." _accountMigration: @@ -1800,6 +1888,7 @@ _role: canManageAvatarDecorations: "Profilbilddekorationen verwalten" driveCapacity: "Drive-Kapazität" alwaysMarkNsfw: "Dateien immer als NSFW markieren" + canUpdateBioMedia: "Kann ein Profil- oder ein Bannerbild bearbeiten" pinMax: "Maximale Anzahl an angehefteten Notizen" antennaMax: "Maximale Anzahl an Antennen" wordMuteMax: "Maximale Zeichenlänge für Wortstummschaltungen" @@ -1815,12 +1904,20 @@ _role: canUseTranslator: "Verwendung des Übersetzers" avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können" canImportAntennas: "Importieren von Antennen erlauben" + canImportBlocking: "Importieren von Blockierungen zulassen" + canImportFollowing: "Importieren von Gefolgten zulassen" + canImportMuting: "Importieren von Stummgeschalteten zulassen" canImportUserLists: "Importieren von Listen erlauben" + canChat: "Chatten erlauben" _condition: + roleAssignedTo: "Manuellen Rollen zugewiesen" isLocal: "Lokaler Benutzer" isRemote: "Benutzer fremder Instanz" isCat: "Katzen-Benutzer" isBot: "Bot-Benutzer" + isSuspended: "Gesperrter Benutzer" + isLocked: "Private Konten" + isExplorable: "Benutzer, die ihr Konto im \"Erkunden\"-Bereich sichtbar machen" createdLessThan: "Kontoerstellung liegt weniger als X zurück" createdMoreThan: "Kontoerstellung liegt mehr als X zurück" followersLessThanOrEq: "Hat X oder weniger Follower" @@ -1975,6 +2072,7 @@ _theme: installed: "{name} wurde installiert" installedThemes: "Installierte Farbschemata" builtinThemes: "Eingebaute Farbschemata" + instanceTheme: "Server-Thema" alreadyInstalled: "Dieses Farbschema ist bereits installiert" invalid: "Der Code dieses Farbschemas ist ungültig" make: "Farbschema erstellen" @@ -2041,6 +2139,7 @@ _sfx: noteMy: "Meine Notizen" notification: "Benachrichtigungen" reaction: "Auswählen einer Reaktion" + chatMessage: "Chat-Nachrichten" _soundSettings: driveFile: "Audiodatei aus dem Drive verwenden" driveFileWarn: "Wähle eine Audiodatei aus dem Drive" @@ -2064,6 +2163,10 @@ _timeIn: seconds: "In {n}s" minutes: "In {n} Min." hours: "In {n} Std." + days: "In {n} Tagen" + weeks: "In {n} Wochen" + months: "In {n} Monaten" + years: "In {n} Jahren" _time: second: "Sekunde(n)" minute: "Minute(n)" @@ -2097,6 +2200,7 @@ _2fa: backupCodesDescription: "Verwende diese Codes, falls du nicht mehr auf deine App zur Zweifaktorauthentifizierung zugreifen kannst. Jeder Code kann nur einmal verwendet werden. Bewahre sie an einem sicheren Ort auf." backupCodeUsedWarning: "Ein Backup-Code wurde verwendet. Falls du den Zugriff zu deiner Zweifaktorauthentifizierungsapp verloren hast, konfiguriere diese bitte möglichst bald erneut." backupCodesExhaustedWarning: "Alle Backup-Codes wurden verwendet. Falls du den Zugang zu deiner Zweifaktorauthentifizierungsapp verlierst, wirst du dich nicht mehr in dieses Konto einloggen können. Bitte konfiguriere diese App erneut." + moreDetailedGuideHere: "Hier ist eine ausführliche Anleitung" _permissions: "read:account": "Deine Benutzerkontoinformationen lesen" "write:account": "Deine Benutzerkontoinformationen bearbeiten" @@ -2147,8 +2251,12 @@ _permissions: "read:admin:server-info": "Serverinformationen anzeigen" "read:admin:show-moderation-log": "Moderationsprotokoll einsehen" "read:admin:show-user": "Private Benutzerinformationen einsehen" + "write:admin:suspend-user": "Benutzer sperren" "write:admin:unset-user-avatar": "Benutzer-Profilbild entfernen" "write:admin:unset-user-banner": "Benutzer-Banner entfernen" + "write:admin:unsuspend-user": "Benutzer entsperren" + "write:admin:meta": "Metadaten der Instanz verwalten" + "write:admin:user-note": "Moderationsvermerke verwalten" "write:admin:roles": "Rollen verwalten" "read:admin:roles": "Rollen anzeigen" "write:admin:relays": "Relays verwalten" @@ -2172,7 +2280,10 @@ _permissions: "read:admin:ad": "Werbung ansehen" "write:invite-codes": "Einladungscodes erstellen" "read:invite-codes": "Einladungscodes anzeigen" + "read:federation": "Informationen zur Föderation einsehen" + "write:report-abuse": "Verstöße melden" "write:chat": "Chats bedienen" + "read:chat": "Chats durchsuchen" _auth: shareAccessTitle: "Verteilung von App-Berechtigungen" shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?" @@ -2295,6 +2406,7 @@ _profile: avatarDecorationMax: "Du kannst bis zu {max} Dekorationen hinzufügen." followedMessage: "Nachricht, wenn dir jemand folgt" followedMessageDescription: "Du kannst eine kurze Nachricht festlegen, die dem Empfänger angezeigt wird, wenn er dir folgt." + followedMessageDescriptionForLockedAccount: "Wenn Folgeanfragen deine Genehmigung brauchen, wird dies beim Genehmigen einer Anfrage angezeigt." _exportOrImport: allNotes: "Alle Notizen" favoritedNotes: "Als Favorit markierte Notizen" @@ -2352,6 +2464,7 @@ _play: title: "Titel" script: "Skript" summary: "Beschreibung" + visibilityDescription: "Wenn du die Sichtbarkeit auf Privat stellst, wird der Play nicht auf deinem Profil sichtbar sein, aber jeder, der die URL hat, kann ihn trotzdem aufrufen." _pages: newPage: "Seite erstellen" editPage: "Seite bearbeiten" @@ -2383,6 +2496,7 @@ _pages: eyeCatchingImageSet: "Vorschaubild festlegen" eyeCatchingImageRemove: "Vorschaubild entfernen" chooseBlock: "Block hinzufügen" + enterSectionTitle: "Titel des Abschnitts eingeben" selectType: "Typ auswählen" contentBlocks: "Inhalt" inputBlocks: "Eingabe" @@ -2393,6 +2507,8 @@ _pages: section: "Abschnitt" image: "Bild" button: "Knopf" + dynamic: "Dynamische Bausteine" + dynamicDescription: "Dieser Baustein wurde abgeschafft. Bitte verwende von nun an {play}." note: "Eingebettete Notiz" _note: id: "Notiz-ID" @@ -2415,6 +2531,7 @@ _notification: newNote: "Neue Notiz" unreadAntennaNote: "Antenne {name}" roleAssigned: "Rolle zugewiesen" + chatRoomInvitationReceived: "Du wurdest in einen Chatraum eingeladen" emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert" achievementEarned: "Errungenschaft freigeschaltet" testNotification: "Testbenachrichtigung" @@ -2442,9 +2559,11 @@ _notification: receiveFollowRequest: "Erhaltene Follow-Anfragen" followRequestAccepted: "Akzeptierte Follow-Anfragen" roleAssigned: "Rolle zugewiesen" + chatRoomInvitationReceived: "Einladungen zum Chatraum" achievementEarned: "Errungenschaft freigeschaltet" exportCompleted: "Der Export ist abgeschlossen" login: "Anmeldung" + createToken: "Erstellung von Zugriffstokens" test: "Test-Benachrichtigungen" app: "Benachrichtigungen von Apps" _actions: @@ -2560,6 +2679,7 @@ _moderationLogTypes: unmarkSensitiveDriveFile: "Datei als nicht sensitiv markiert" resolveAbuseReport: "Meldung bearbeitet" forwardAbuseReport: "Meldung weitergeleitet" + updateAbuseReportNote: "Moderationsnotiz einer Meldung aktualisiert" createInvitation: "Einladung erstellt" createAd: "Werbung erstellt" deleteAd: "Werbung gelöscht" @@ -2579,6 +2699,8 @@ _moderationLogTypes: deletePage: "Seite gelöscht" deleteFlash: "Play gelöscht" deleteGalleryPost: "Galeriebeitrag gelöscht" + deleteChatRoom: "Chatraum gelöscht" + updateProxyAccountDescription: "Beschreibung des Proxy-Benutzerkontos aktualisiert" _fileViewer: title: "Dateiinformationen" type: "Dateityp" @@ -2644,6 +2766,7 @@ _hemisphere: S: "Südliche Erdhalbkugel" caption: "Wird in einigen Client-Einstellungen zur Bestimmung der Jahreszeit verwendet." _reversi: + reversi: "Reversi" gameSettings: "Spieleinstellungen" chooseBoard: "Spielbrett auswählen" blackOrWhite: "Schwarz/Weiß" @@ -2661,6 +2784,7 @@ _reversi: pastTurnOf: "Zug von {name}" surrender: "Aufgeben" surrendered: "Aufgegeben" + timeout: "Zeit abgelaufen" drawn: "Unentschieden" won: "{name} hat gewonnen" black: "Schwarz" @@ -2675,6 +2799,7 @@ _reversi: freeMatch: "Freies Spiel" lookingForPlayer: "Gegner werden gesucht..." gameCanceled: "Das Spiel wurde abgesagt." + shareToTlTheGameWhenStart: "Spiel in der Chronik teilen, wenn es gestartet wurde" iStartedAGame: "Das Spiel hat begonnen! #MisskeyReversi" opponentHasSettingsChanged: "Der Gegner hat seine Einstellungen geändert." allowIrregularRules: "Irreguläre Regeln (völlig frei)" @@ -2695,7 +2820,9 @@ _urlPreviewSetting: requireContentLengthDescription: "Wenn der Server keine Content-Length zurückgibt, wird keine Vorschau erzeugt." userAgent: "User-Agent" userAgentDescription: "Legt den User-Agent fest, der beim Abrufen der Vorschau verwendet werden soll. Bleibt er leer, wird der Standard-User-Agent verwendet." + summaryProxy: "Proxy-Endpunkte, die Vorschaubilder erzeugen" summaryProxyDescription: "Generierung von Vorschaubildern mit Summaly Proxy anstelle von Misskey selbst." + summaryProxyDescription2: "Die folgenden Parameter werden als Abfrage-Strings mit dem Proxy verknüpft. Wenn der Proxy sie nicht unterstützt, werden die Werte ignoriert." _mediaControls: pip: "Bild-in-Bild" playbackRate: "Wiedergabegeschwindigkeit" @@ -2703,15 +2830,57 @@ _mediaControls: _contextMenu: title: "Kontextmenü" app: "Anwendung" + appWithShift: "Anwendung per Umschalttaste" + native: "Natives Browsermenü" _gridComponent: _error: requiredValue: "Dieser Wert ist ein Pflichtfeld" + columnTypeNotSupport: "Die Validierung regulärer Ausdrücke wird nur für Spalten vom Typ \"Text\" unterstützt." + patternNotMatch: "Dieser Wert stimmt nicht mit dem Schema in {pattern} überein" notUnique: "Dieser Wert muss eindeutig sein" +_roleSelectDialog: + notSelected: "Nicht ausgewählt" _customEmojisManager: + _gridCommon: + copySelectionRows: "Ausgewählte Zeilen kopieren" + copySelectionRanges: "Auswahl kopieren" + deleteSelectionRows: "Ausgewählte Zeilen löschen" + searchSettings: "Sucheinstellungen" + searchSettingCaption: "Detaillierte Suchkriterien festlegen." + searchLimit: "Anzahl der Ergebnisse" + sortOrder: "Sortierung" + registrationLogs: "Registrierungsprotokoll" + registrationLogsCaption: "Protokolle werden beim Aktualisieren oder Löschen von Emojis angezeigt. Sie verschwinden nach dem Aktualisieren oder Löschen, dem Wechsel zu einer neuen Seite oder dem Neuladen." + alertEmojisRegisterFailedDescription: "Emoji konnte nicht aktualisiert oder gelöscht werden. Bitte prüfe das Registrierungsprotokoll für Details." _logs: + failureLogNothing: "Es gibt kein Fehlerprotokoll." logNothing: "Keine Protokoll-Einträge." _remote: + selectionRowDetail: "Details der ausgewählten Zeile" + importSelectionRangesRows: "Zeilen in der Auswahl importieren" + importEmojisButton: "Ausgewählte Emojis importieren" confirmImportEmojisTitle: "Emojis importieren" + confirmImportEmojisDescription: "Importiere {count} Emoji(s), die von entfernten Server empfangen wurden. Bitte achte genau auf die Lizenz der Emojis. Bist du sicher, dass du fortfahren möchtest?" + _local: + tabTitleList: "Hinzugefügte Emojis" + tabTitleRegister: "Emojis hinzufügen" + _list: + emojisNothing: "Es wurden keine Emojis hinzugefügt." + alertUpdateEmojisNothingDescription: "Es wurden keine Emojis geändert." + alertDeleteEmojisNothingDescription: "Es gibt keine zu löschenden Emojis." + confirmUpdateEmojisDescription: "Aktualisiere {count} Emoji(s). Willst du fortfahren?" + confirmDeleteEmojisDescription: "Lösche {count} ausgewählte Emoji(s). Willst du fortfahren?" + confirmMovePageDesciption: "An den Emojis auf dieser Seite wurden Änderungen vorgenommen.\nWenn du die Seite verlässt, ohne zu speichern, werden alle auf dieser Seite vorgenommenen Änderungen verworfen." + _register: + uploadSettingDescription: "Hier kannst du das Verhalten beim Hochladen von Emojis konfigurieren." + directoryToCategoryLabel: "Gib den Namen des Verzeichnisses in das Feld „Kategorie“ ein" + directoryToCategoryCaption: "Wenn du ein Verzeichnis ziehst und ablegst, gib den Verzeichnisnamen in das Feld „Kategorie“ ein." + emojiInputAreaList1: "Ziehe Bilddateien oder Verzeichnisse per Drag-and-drop in diesen Rahmen" + emojiInputAreaList2: "Klicke auf diesen Link, um von deinem PC aus zu wählen" + emojiInputAreaList3: "Klicke auf diesen Link, um vom Drive aus zu wählen" + confirmRegisterEmojisDescription: "Füge die in der Liste aufgeführten Emojis als neue benutzerdefinierte Emojis hinzu. Bist du sicher? (Um eine Überlastung zu vermeiden, können nur {count} Emoji(s) in einem Vorgang hinzugefügt werden)" + confirmClearEmojisDescription: "Verwerfe die Bearbeitungen und lösche die Emojis aus der Liste. Bist du sicher, dass du fortfahren möchtest?" + confirmUploadEmojisDescription: "Lade die {count} abgelegte(n) Datei(en) in das Drive hoch. Bist du sicher, dass du fortfahren möchtest?" _embedCodeGen: title: "Einbettungscode anpassen" header: "Kopfzeile anzeigen" @@ -2719,6 +2888,9 @@ _embedCodeGen: maxHeight: "Maximale Höhe" maxHeightDescription: "Der Wert 0 deaktiviert die Einstellung der maximalen Höhe. Gib einen Wert an, um zu verhindern, dass das Widget weiterhin vertikal vergrößert wird." maxHeightWarn: "Die Begrenzung der maximalen Höhe ist deaktiviert (0). Wenn dies nicht beabsichtigt war, setze die maximale Höhe auf einen Wert fest." + previewIsNotActual: "Die Anzeige weicht von der tatsächlichen Einbettung ab, da sie den auf dem Vorschaufenster angezeigten Bereich überschreitet." + rounded: "Ecken abrunden" + border: "Dem äußeren Rand einen Rahmen hinzufügen" applyToPreview: "Auf die Vorschau anwenden" generateCode: "Einbettungscode generieren" codeGenerated: "Der Code wurde generiert" @@ -2749,8 +2921,17 @@ _remoteLookupErrors: title: "Nicht gefunden" description: "Die angeforderte Ressource konnte nicht gefunden werden, bitte überprüfe die URI erneut." _captcha: + verify: "Bitte beantworte das CAPTCHA" + testSiteKeyMessage: "Du kannst die Vorschau prüfen, indem du die Testwerte für den Site- und Secret-Key eingibst. Weitere Informationen findest du auf der folgenden Seite." _error: + _requestFailed: + title: "CAPTCHA-Anfrage fehlgeschlagen." + text: "Bitte probiere es später noch einmal oder überprüfe die Einstellungen erneut." + _verificationFailed: + title: "CAPTCHA-Prüfung fehlgeschlagen" + text: "Bitte überprüfe nochmals, ob die Einstellungen korrekt sind." _unknown: + title: "CAPTCHA-Fehler" text: "Es ist ein unerwarteter Fehler aufgetreten." _bootErrors: title: "Laden fehlgeschlagen" diff --git a/locales/en-US.yml b/locales/en-US.yml index 68d52c721d..089ed2383d 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "Timeline and note" makeEveryTextElementsSelectable: "Make all text elements selectable" makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations." + useStickyIcons: "Make icons follow while scrolling" showNavbarSubButtons: "Show sub-buttons on the navigation bar" ifOn: "When turned on" ifOff: "When turned off" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 3f2fa089da..a39ca4f8db 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "时间线和帖子" makeEveryTextElementsSelectable: "使所有的文字均可选择" makeEveryTextElementsSelectable_description: "若开启,在某些情况下可能降低用户体验。" + useStickyIcons: "使图标跟随滚动" showNavbarSubButtons: "在导航栏中显示副按钮" ifOn: "启用时" ifOff: "关闭时" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 5ff252eb49..71f3c45d6a 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1406,6 +1406,7 @@ _settings: timelineAndNote: "時間軸及貼文" makeEveryTextElementsSelectable: "允許選取所有文字" makeEveryTextElementsSelectable_description: "啟用此功能後,可能會在某些情境下降低可用性。" + useStickyIcons: "使大頭貼跟隨捲動" showNavbarSubButtons: "在導覽列顯示輔助按鈕" ifOn: "開啟時" ifOff: "關閉時" From 6e929ece6f1a1b5b369ebf1f6b534a69789a27b2 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:13:08 +0900 Subject: [PATCH 376/405] fix(frontend): suppress inject warn --- packages/frontend/src/components/global/MkPageHeader.vue | 2 +- packages/frontend/src/router.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 6b524785e4..2914012a0c 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -68,7 +68,7 @@ const emit = defineEmits<{ (ev: 'update:tab', key: string); }>(); -const viewId = inject(DI.viewId); +//const viewId = inject(DI.viewId); const injectedPageMetadata = inject(DI.pageMetadata); const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value); diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index d702da80fa..97ca63f50d 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -42,5 +42,5 @@ mainRouter.addListener('change', ctx => { mainRouter.init(); export function useRouter(): Router { - return inject(DI.router) ?? mainRouter; + return inject(DI.router, null) ?? mainRouter; } From e0d87028391500898adc2668d509e2d155d8b961 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 18:13:39 +0900 Subject: [PATCH 377/405] perf(frontend): tweak MkRange --- packages/frontend/src/components/MkRange.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index 734b624541..c0e3e5db4b 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -159,12 +159,13 @@ function onMousedown(ev: MouseEvent | TouchEvent) { const onDrag = (ev: MouseEvent | TouchEvent) => { ev.preventDefault(); + let beforeValue = finalValue.value; const containerRect = containerEl.value!.getBoundingClientRect(); const pointerX = 'touches' in ev && ev.touches.length > 0 ? ev.touches[0].clientX : 'clientX' in ev ? ev.clientX : 0; const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth / 2)); rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth))); - if (props.continuousUpdate) { + if (props.continuousUpdate && beforeValue !== finalValue.value) { emit('update:modelValue', finalValue.value); } }; From 87a723897611fff9b5ddda8a8bec3cdb427a21dc Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 30 Mar 2025 20:44:00 +0900 Subject: [PATCH 378/405] =?UTF-8?q?enhance(frontend):=20=E3=83=87=E3=83=83?= =?UTF-8?q?=E3=82=AD=E3=81=AE=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 +- locales/index.d.ts | 24 ++ locales/ja-JP.yml | 6 + packages/frontend/src/pages/settings/deck.vue | 33 +++ packages/frontend/src/preferences/def.ts | 11 +- .../frontend/src/ui/_common_/navbar-h.vue | 214 ++++++++++++++++++ packages/frontend/src/ui/deck.vue | 166 +++++++++----- .../utility/autogen/settings-search-index.ts | 17 +- 8 files changed, 417 insertions(+), 60 deletions(-) create mode 100644 packages/frontend/src/ui/_common_/navbar-h.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 0268127cbe..0ecec21953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ - Enhance: プラグインの管理が強化されました - インストール/アンインストール/設定の変更時にリロード不要になりました - Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように +- Enhance: デッキUIでカラム間のマージンを設定できるように +- Enhance: デッキUIでデッキメニューの位置を設定できるように +- Enhance: デッキUIでナビゲーションバーの位置を設定できるように - Enhance: アイコンのスクロール追従を無効化してパフォーマンス向上できるように - Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに - Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように @@ -52,8 +55,7 @@ - Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正 - Fix: テーマ切り替え時に一部の色が変わらない問題を修正 - NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました - - デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置することである程度クラシックUIを再現できます - - また、デッキでナビゲーションバーを上部に表示するオプションを実装予定です + - デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置し、ナビゲーションバーを上部に表示することである程度クラシックUIを再現できます ### Server - Enhance 全体的なパフォーマンス向上 diff --git a/locales/index.d.ts b/locales/index.d.ts index 3dfd0e6029..3645639305 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5362,6 +5362,18 @@ export interface Locale extends ILocale { * 圧縮 */ "compress": string; + /** + * 右 + */ + "right": string; + /** + * 下 + */ + "bottom": string; + /** + * 上 + */ + "top": string; "_chat": { /** * まだメッセージはありません @@ -10065,6 +10077,18 @@ export interface Locale extends ILocale { * カラムの寄せ */ "columnAlign": string; + /** + * カラム間のマージン + */ + "columnGap": string; + /** + * デッキメニューの位置 + */ + "deckMenuPosition": string; + /** + * ナビゲーションバーの位置 + */ + "navbarPosition": string; /** * カラムを追加 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index eb1505270c..0e20001d6b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1336,6 +1336,9 @@ chat: "チャット" migrateOldSettings: "旧設定情報を移行" migrateOldSettings_description: "通常これは自動で行われていますが、何らかの理由により上手く移行されなかった場合は手動で移行処理をトリガーできます。現在の設定情報は上書きされます。" compress: "圧縮" +right: "右" +bottom: "下" +top: "上" _chat: noMessagesYet: "まだメッセージはありません" @@ -2662,6 +2665,9 @@ _notification: _deck: alwaysShowMainColumn: "常にメインカラムを表示" columnAlign: "カラムの寄せ" + columnGap: "カラム間のマージン" + deckMenuPosition: "デッキメニューの位置" + navbarPosition: "ナビゲーションバーの位置" addColumn: "カラムを追加" newNoteNotificationSettings: "新着ノート通知の設定" configureColumn: "カラムの設定" diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue index 9b2b40374e..902e058e0d 100644 --- a/packages/frontend/src/pages/settings/deck.vue +++ b/packages/frontend/src/pages/settings/deck.vue @@ -45,6 +45,35 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -53,6 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ref } from 'vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkRadios from '@/components/MkRadios.vue'; +import MkRange from '@/components/MkRange.vue'; import { i18n } from '@/i18n.js'; import { definePage } from '@/page.js'; import { prefer } from '@/preferences.js'; @@ -62,6 +92,9 @@ const navWindow = prefer.model('deck.navWindow'); const useSimpleUiForNonRootPages = prefer.model('deck.useSimpleUiForNonRootPages'); const alwaysShowMainColumn = prefer.model('deck.alwaysShowMainColumn'); const columnAlign = prefer.model('deck.columnAlign'); +const columnGap = prefer.model('deck.columnGap'); +const menuPosition = prefer.model('deck.menuPosition'); +const navbarPosition = prefer.model('deck.navbarPosition'); const profilesSyncEnabled = ref(prefer.isSyncEnabled('deck.profiles')); diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts index 37fa9471ee..5aa463f045 100644 --- a/packages/frontend/src/preferences/def.ts +++ b/packages/frontend/src/preferences/def.ts @@ -371,7 +371,16 @@ export const PREF_DEF = { default: true, }, 'deck.columnAlign': { - default: 'left' as 'left' | 'right' | 'center', + default: 'center' as 'left' | 'right' | 'center', + }, + 'deck.columnGap': { + default: 6, + }, + 'deck.menuPosition': { + default: 'bottom' as 'right' | 'bottom', + }, + 'deck.navbarPosition': { + default: 'left' as 'left' | 'top' | 'bottom', }, 'chat.showSenderName': { diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue new file mode 100644 index 0000000000..c93935dd26 --- /dev/null +++ b/packages/frontend/src/ui/_common_/navbar-h.vue @@ -0,0 +1,214 @@ + + + + + + + diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 3f3bc32fad..3de8137404 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -4,37 +4,43 @@ SPDX-License-Identifier: AGPL-3.0-only -->