feat: チャンネルミュートの実装
※でかいので本線投入のタイミングは計りたい
fix: #10649
What
チャンネルミュートの機能を実装しました。
- エンドポイントの追加
- channels/mute/create
- channels/mute/delete
- channels/mute/list
- 各TLのフィルタ時にチャンネルのミュート有無を考慮するように
- HTL
- ミュート中チャンネルのノートが流れてこなくなる
- リノート含め/フォロー中のチャンネルも同様
- LTL
- ミュート中チャンネルのノートがチャンネル外にリノートされた場合でも流れてこなくなる
- STL
- LTL + HTL
- リストTL
- ミュート中チャンネルのノートがチャンネル外にリノートされた場合でも流れてこなくなる
- ロールTL
- ミュート中チャンネルのノートが流れてこなくなる
- ユーザTL
- そのユーザの投稿からミュート中チャンネルの投稿が省かれる
- チャンネルTL
- ミュート中でもそのチャンネル自体のTLは通常通り閲覧可能
- ただし、ミュート中チャンネルの投稿を別チャンネルにリノートした場合はミュートされて見えなくなる
- HTL
Why
fix: #10649
Additional info (optional)
- unittestに新設したServiceのテストを追加
- e2etestに各TL向けのテストを追加
- pnpm migrate / revertによるエラーが無いことを確認
Checklist
- [x] Read the contribution guide
- [x] Test working in a local environment
- [ ] (If needed) Add story of storybook
- [x] (If needed) Update CHANGELOG.md
- [x] (If possible) Add tests
このPRによるapi.jsonの差分
差分はこちら
--- base
+++ head
@@ -24802,6 +24802,493 @@
}
}
},
+ "/channels/mute/create": {
+ "post": {
+ "operationId": "post___channels___mute___create",
+ "summary": "channels/mute/create",
+ "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:channels*",
+ "externalDocs": {
+ "description": "Source code",
+ "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/channels/mute/create.ts"
+ },
+ "tags": [
+ "channels"
+ ],
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "channelId": {
+ "type": "string",
+ "format": "misskey:id"
+ },
+ "expiresAt": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "description": "A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute."
+ }
+ },
+ "required": [
+ "channelId"
+ ]
+ }
+ }
+ }
+ },
+ "responses": {
+ "204": {
+ "description": "OK (without any results)"
+ },
+ "400": {
+ "description": "Client error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "NO_SUCH_CHANNEL": {
+ "value": {
+ "error": {
+ "message": "No such Channel.",
+ "code": "NO_SUCH_CHANNEL",
+ "id": "7174361e-d58f-31d6-2e7c-6fb830786a3f"
+ }
+ }
+ },
+ "ALREADY_MUTING_CHANNEL": {
+ "value": {
+ "error": {
+ "message": "You are already muting that user.",
+ "code": "ALREADY_MUTING_CHANNEL",
+ "id": "5a251978-769a-da44-3e89-3931e43bb592"
+ }
+ }
+ },
+ "EXPIRES_AT_IS_PAST": {
+ "value": {
+ "error": {
+ "message": "Cannot set past date to \"expiresAt\".",
+ "code": "EXPIRES_AT_IS_PAST",
+ "id": "42b32236-df2c-a45f-fdbf-def67268f749"
+ }
+ }
+ },
+ "INVALID_PARAM": {
+ "value": {
+ "error": {
+ "message": "Invalid param.",
+ "code": "INVALID_PARAM",
+ "id": "3d81ceae-475f-4600-b2a8-2bc116157532"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authentication error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "CREDENTIAL_REQUIRED": {
+ "value": {
+ "error": {
+ "message": "Credential required.",
+ "code": "CREDENTIAL_REQUIRED",
+ "id": "1384574d-a912-4b81-8601-c7b1c4085df1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "AUTHENTICATION_FAILED": {
+ "value": {
+ "error": {
+ "message": "Authentication failed. Please ensure your token is correct.",
+ "code": "AUTHENTICATION_FAILED",
+ "id": "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "418": {
+ "description": "I'm Ai",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "I_AM_AI": {
+ "value": {
+ "error": {
+ "message": "You sent a request to Ai-chan, Misskey's showgirl, instead of the server.",
+ "code": "I_AM_AI",
+ "id": "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "INTERNAL_ERROR": {
+ "value": {
+ "error": {
+ "message": "Internal error occurred. Please contact us if the error persists.",
+ "code": "INTERNAL_ERROR",
+ "id": "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/channels/mute/delete": {
+ "post": {
+ "operationId": "post___channels___mute___delete",
+ "summary": "channels/mute/delete",
+ "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:channels*",
+ "externalDocs": {
+ "description": "Source code",
+ "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/channels/mute/delete.ts"
+ },
+ "tags": [
+ "channels"
+ ],
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "channelId": {
+ "type": "string",
+ "format": "misskey:id"
+ }
+ },
+ "required": [
+ "channelId"
+ ]
+ }
+ }
+ }
+ },
+ "responses": {
+ "204": {
+ "description": "OK (without any results)"
+ },
+ "400": {
+ "description": "Client error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "NO_SUCH_CHANNEL": {
+ "value": {
+ "error": {
+ "message": "No such Channel.",
+ "code": "NO_SUCH_CHANNEL",
+ "id": "e7998769-6e94-d9c2-6b8f-94a527314aba"
+ }
+ }
+ },
+ "NOT_MUTING_CHANNEL": {
+ "value": {
+ "error": {
+ "message": "You are not muting that channel.",
+ "code": "NOT_MUTING_CHANNEL",
+ "id": "14d55962-6ea8-d990-1333-d6bef78dc2ab"
+ }
+ }
+ },
+ "INVALID_PARAM": {
+ "value": {
+ "error": {
+ "message": "Invalid param.",
+ "code": "INVALID_PARAM",
+ "id": "3d81ceae-475f-4600-b2a8-2bc116157532"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authentication error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "CREDENTIAL_REQUIRED": {
+ "value": {
+ "error": {
+ "message": "Credential required.",
+ "code": "CREDENTIAL_REQUIRED",
+ "id": "1384574d-a912-4b81-8601-c7b1c4085df1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "AUTHENTICATION_FAILED": {
+ "value": {
+ "error": {
+ "message": "Authentication failed. Please ensure your token is correct.",
+ "code": "AUTHENTICATION_FAILED",
+ "id": "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "418": {
+ "description": "I'm Ai",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "I_AM_AI": {
+ "value": {
+ "error": {
+ "message": "You sent a request to Ai-chan, Misskey's showgirl, instead of the server.",
+ "code": "I_AM_AI",
+ "id": "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "INTERNAL_ERROR": {
+ "value": {
+ "error": {
+ "message": "Internal error occurred. Please contact us if the error persists.",
+ "code": "INTERNAL_ERROR",
+ "id": "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/channels/mute/list": {
+ "post": {
+ "operationId": "post___channels___mute___list",
+ "summary": "channels/mute/list",
+ "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *read:channels*",
+ "externalDocs": {
+ "description": "Source code",
+ "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/channels/mute/list.ts"
+ },
+ "tags": [
+ "channels"
+ ],
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK (with results)",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Channel"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Client error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "INVALID_PARAM": {
+ "value": {
+ "error": {
+ "message": "Invalid param.",
+ "code": "INVALID_PARAM",
+ "id": "3d81ceae-475f-4600-b2a8-2bc116157532"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Authentication error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "CREDENTIAL_REQUIRED": {
+ "value": {
+ "error": {
+ "message": "Credential required.",
+ "code": "CREDENTIAL_REQUIRED",
+ "id": "1384574d-a912-4b81-8601-c7b1c4085df1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Forbidden error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "AUTHENTICATION_FAILED": {
+ "value": {
+ "error": {
+ "message": "Authentication failed. Please ensure your token is correct.",
+ "code": "AUTHENTICATION_FAILED",
+ "id": "b0a7f5f8-dc2f-4171-b91f-de88ad238e14"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "418": {
+ "description": "I'm Ai",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "I_AM_AI": {
+ "value": {
+ "error": {
+ "message": "You sent a request to Ai-chan, Misskey's showgirl, instead of the server.",
+ "code": "I_AM_AI",
+ "id": "60c46cd1-f23a-46b1-bebe-5d2b73951a84"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "500": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "examples": {
+ "INTERNAL_ERROR": {
+ "value": {
+ "error": {
+ "message": "Internal error occurred. Please contact us if the error persists.",
+ "code": "INTERNAL_ERROR",
+ "id": "5d37dbcb-891e-41ca-a3d6-e690c97775ac"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/channels/my-favorites": {
"post": {
"operationId": "post___channels___my-favorites",
@@ -90777,6 +91264,9 @@
"isFavorited": {
"type": "boolean"
},
+ "isMuting": {
+ "type": "boolean"
+ },
"pinnedNotes": {
"type": "array",
"items": {
Codecov Report
:x: Patch coverage is 61.84084% with 398 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 45.57%. Comparing base (a888f28) to head (59f04c9).
:warning: Report is 20 commits behind head on develop.
Additional details and impacted files
@@ Coverage Diff @@
## develop #14105 +/- ##
===========================================
- Coverage 45.86% 45.57% -0.30%
===========================================
Files 1781 1802 +21
Lines 184646 187146 +2500
Branches 5514 5653 +139
===========================================
+ Hits 84695 85292 +597
- Misses 99922 101824 +1902
- Partials 29 30 +1
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
チャンネルのミュート・アンミュートはこちらから。
分かりにくい位置になってしまいましたが、今の画面構成だとここに置くのが限界だと思い…
ユーザのフォロー画面のように出来ればいいのかもしれませんが、あまり派手に変えるとチャンネルミュートの本筋から逸れてしまうのでひとまずこの形式で実装しました。
ready for review
マージするか
コンフリ解消済み
マージするかしら
次にしたいかも・・(2025.5.1はもうだいぶ大きいので)
https://github.com/misskey-dev/misskey/actions/runs/18817767528/job/53688599755?pr=14105
(これどうしたらいいんだ…ぱっと見通ってそうだけど)
CONSTRAINT/INDEX名の不整合です。アノテーション側を変えるか、migrationの名前を自動生成に揃える化してください
通りました、ありがとうございます。
入れるか
後でメンテします
全pass確認
👍👍