misskey icon indicating copy to clipboard operation
misskey copied to clipboard

feat: チャンネルミュートの実装

Open samunohito opened this issue 1 year ago • 8 comments

※でかいので本線投入のタイミングは計りたい

fix: #10649

What

チャンネルミュートの機能を実装しました。

  • エンドポイントの追加
    • channels/mute/create
    • channels/mute/delete
    • channels/mute/list
  • 各TLのフィルタ時にチャンネルのミュート有無を考慮するように
    • HTL
      • ミュート中チャンネルのノートが流れてこなくなる
      • リノート含め/フォロー中のチャンネルも同様
    • LTL
      • ミュート中チャンネルのノートがチャンネル外にリノートされた場合でも流れてこなくなる
    • STL
      • LTL + HTL
    • リストTL
      • ミュート中チャンネルのノートがチャンネル外にリノートされた場合でも流れてこなくなる
    • ロールTL
      • ミュート中チャンネルのノートが流れてこなくなる
    • ユーザTL
      • そのユーザの投稿からミュート中チャンネルの投稿が省かれる
    • チャンネルTL
      • ミュート中でもそのチャンネル自体のTLは通常通り閲覧可能
      • ただし、ミュート中チャンネルの投稿を別チャンネルにリノートした場合はミュートされて見えなくなる

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

samunohito avatar Jun 30 '24 13:06 samunohito

この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": {

Get diff files from Workflow Page

github-actions[bot] avatar Jun 30 '24 13:06 github-actions[bot]

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.

Files with missing lines Patch % Lines
.../backend/src/core/entities/ChannelEntityService.ts 10.60% 118 Missing :warning:
packages/frontend/src/pages/channel.vue 0.00% 64 Missing :warning:
...backend/src/server/api/endpoints/notes/timeline.ts 15.62% 27 Missing :warning:
...d/src/server/api/endpoints/channels/mute/create.ts 71.11% 26 Missing :warning:
...es/backend/src/server/api/endpoints/users/notes.ts 7.69% 24 Missing :warning:
...d/src/server/api/endpoints/channels/mute/delete.ts 72.60% 20 Missing :warning:
...backend/src/server/api/endpoints/antennas/notes.ts 16.66% 15 Missing :warning:
.../src/server/api/endpoints/notes/hybrid-timeline.ts 25.00% 15 Missing :warning:
...es/backend/src/server/api/endpoints/roles/notes.ts 16.66% 15 Missing :warning:
packages/backend/src/misc/is-channel-related.ts 54.83% 14 Missing :warning:
... and 8 more
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.

codecov[bot] avatar Jun 30 '24 13:06 codecov[bot]

image チャンネルのミュート・アンミュートはこちらから。 分かりにくい位置になってしまいましたが、今の画面構成だとここに置くのが限界だと思い…

ユーザのフォロー画面のように出来ればいいのかもしれませんが、あまり派手に変えるとチャンネルミュートの本筋から逸れてしまうのでひとまずこの形式で実装しました。

samunohito avatar Jul 07 '24 02:07 samunohito

ready for review

samunohito avatar Jul 07 '24 02:07 samunohito

マージするか

syuilo avatar Oct 19 '24 07:10 syuilo

コンフリ解消済み

samunohito avatar Apr 13 '25 03:04 samunohito

マージするかしら

syuilo avatar May 09 '25 10:05 syuilo

次にしたいかも・・(2025.5.1はもうだいぶ大きいので)

samunohito avatar May 17 '25 02:05 samunohito

https://github.com/misskey-dev/misskey/actions/runs/18817767528/job/53688599755?pr=14105

(これどうしたらいいんだ…ぱっと見通ってそうだけど)

samunohito avatar Oct 26 '25 12:10 samunohito

CONSTRAINT/INDEX名の不整合です。アノテーション側を変えるか、migrationの名前を自動生成に揃える化してください

anatawa12 avatar Oct 26 '25 13:10 anatawa12

通りました、ありがとうございます。

samunohito avatar Oct 27 '25 13:10 samunohito

入れるか

syuilo avatar Oct 28 '25 01:10 syuilo

後でメンテします

samunohito avatar Oct 28 '25 05:10 samunohito

全pass確認

samunohito avatar Oct 30 '25 13:10 samunohito

👍👍

syuilo avatar Nov 06 '25 23:11 syuilo