Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ノートの下書き(draft of note) #15298

Open
wants to merge 54 commits into
base: develop
Choose a base branch
from

Conversation

tai-cha
Copy link
Contributor

@tai-cha tai-cha commented Jan 16, 2025

What

ノートの下書きを実装する
・バックエンドを持ち全端末で同期可能に

draft

いまは離脱時に聞いてフッターで呼び戻す形になってる
image
image

Why

close #7098

Additional info (optional)

Checklist

  • Read the contribution guide
  • Test working in a local environment
  • (If needed) Add story of storybook
  • (If needed) Update CHANGELOG.md
  • (If possible) Add tests

@github-actions github-actions bot added packages/backend Server side specific issue/PR packages/misskey-js labels Jan 16, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 16, 2025

まだバックエンドだけ

Copy link

codecov bot commented Jan 16, 2025

Codecov Report

Attention: Patch coverage is 57.47180% with 905 lines in your changes missing coverage. Please review.

Project coverage is 39.64%. Comparing base (8d7b1f2) to head (833a2ad).
Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
...rontend/src/components/MkNoteDraftSelectDialog.vue 0.00% 237 Missing and 1 partial ⚠️
...nd/src/server/api/endpoints/notes/drafts/update.ts 52.78% 178 Missing ⚠️
...nd/src/server/api/endpoints/notes/drafts/create.ts 52.47% 173 Missing ⚠️
...ackend/src/core/entities/NoteDraftEntityService.ts 31.07% 122 Missing ⚠️
packages/backend/src/core/NoteDraftService.ts 34.24% 96 Missing ⚠️
packages/frontend/src/scripts/get-note-summary.ts 3.12% 31 Missing ⚠️
...kend/src/server/api/endpoints/notes/drafts/list.ts 71.21% 19 Missing ⚠️
packages/frontend/src/pages/admin/roles.editor.vue 0.00% 19 Missing ⚠️
...nd/src/server/api/endpoints/notes/drafts/delete.ts 72.13% 17 Missing ⚠️
packages/frontend/src/pages/admin/roles.vue 0.00% 7 Missing ⚠️
... and 1 more
Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #15298      +/-   ##
===========================================
- Coverage    40.45%   39.64%   -0.82%     
===========================================
  Files         1605     1614       +9     
  Lines       209680   211749    +2069     
  Branches      4082     3799     -283     
===========================================
- Hits         84836    83953     -883     
- Misses      124240   127191    +2951     
- Partials       604      605       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

github-actions bot commented Jan 16, 2025

このPRによるapi.jsonの差分

差分はこちら
--- base
+++ head
@@ -60013,6 +60013,1236 @@
         }
       }
     },
+    "/notes/drafts": {
+      "post": {
+        "operationId": "notes___drafts",
+        "summary": "notes/drafts",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *read:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "limit": {
+                    "type": "integer",
+                    "minimum": 1,
+                    "maximum": 100,
+                    "default": 30
+                  },
+                  "sinceId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  },
+                  "untilId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  }
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "OK (with results)",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "array",
+                  "items": {
+                    "type": "object",
+                    "$ref": "#/components/schemas/NoteDraft"
+                  }
+                }
+              }
+            }
+          },
+          "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"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/notes/drafts/create": {
+      "post": {
+        "operationId": "notes___drafts___create",
+        "summary": "notes/drafts/create",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts/create.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "visibility": {
+                    "type": "string",
+                    "enum": [
+                      "public",
+                      "home",
+                      "followers",
+                      "specified"
+                    ],
+                    "default": "public"
+                  },
+                  "visibleUserIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "cw": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 1,
+                    "maxLength": 100
+                  },
+                  "hashtag": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "maxLength": 200
+                  },
+                  "localOnly": {
+                    "type": "boolean",
+                    "default": false
+                  },
+                  "reactionAcceptance": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "enum": [
+                      null,
+                      "likeOnly",
+                      "likeOnlyForRemote",
+                      "nonSensitiveOnly",
+                      "nonSensitiveOnlyForLocalLikeOnlyForRemote"
+                    ],
+                    "default": null
+                  },
+                  "replyId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "renoteId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "channelId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "text": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 0,
+                    "maxLength": 3000
+                  },
+                  "fileIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "minItems": 1,
+                    "maxItems": 16,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "poll": {
+                    "type": [
+                      "object",
+                      "null"
+                    ],
+                    "properties": {
+                      "choices": {
+                        "type": "array",
+                        "uniqueItems": true,
+                        "minItems": 0,
+                        "maxItems": 10,
+                        "items": {
+                          "type": "string",
+                          "minLength": 1,
+                          "maxLength": 50
+                        }
+                      },
+                      "multiple": {
+                        "type": "boolean"
+                      },
+                      "expiresAt": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ]
+                      },
+                      "expiredAfter": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ],
+                        "minimum": 1
+                      }
+                    },
+                    "required": [
+                      "choices"
+                    ]
+                  }
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "OK (with results)",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "object",
+                  "properties": {
+                    "createdDraft": {
+                      "type": "object",
+                      "$ref": "#/components/schemas/NoteDraft"
+                    }
+                  },
+                  "required": [
+                    "createdDraft"
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "description": "Client error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "NO_SUCH_RENOTE_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such renote target.",
+                        "code": "NO_SUCH_RENOTE_TARGET",
+                        "id": "b5c90186-4ab0-49c8-9bba-a1f76c282ba4"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote a pure Renote.",
+                        "code": "CANNOT_RENOTE_TO_A_PURE_RENOTE",
+                        "id": "fd4cc33e-2a37-48dd-99cc-9b806eb2031a"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_DUE_TO_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote due to target visibility.",
+                        "code": "CANNOT_RENOTE_DUE_TO_VISIBILITY",
+                        "id": "be9529e9-fe72-4de0-ae43-0b363c4938af"
+                      }
+                    }
+                  },
+                  "NO_SUCH_REPLY_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such reply target.",
+                        "code": "NO_SUCH_REPLY_TARGET",
+                        "id": "749ee0f6-d3da-459a-bf02-282e2da4292c"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to an invisible Note.",
+                        "code": "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE",
+                        "id": "b98980fa-3780-406c-a935-b6d0eeee10d1"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not reply to a pure Renote.",
+                        "code": "CANNOT_REPLY_TO_A_PURE_RENOTE",
+                        "id": "3ac74a84-8fd5-4bb0-870f-01804f82ce15"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to a specified visibility note with extended visibility.",
+                        "code": "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY",
+                        "id": "ed940410-535c-4d5e-bfa3-af798671e93c"
+                      }
+                    }
+                  },
+                  "CANNOT_CREATE_ALREADY_EXPIRED_POLL": {
+                    "value": {
+                      "error": {
+                        "message": "Poll is already expired.",
+                        "code": "CANNOT_CREATE_ALREADY_EXPIRED_POLL",
+                        "id": "04da457d-b083-4055-9082-955525eda5a5"
+                      }
+                    }
+                  },
+                  "NO_SUCH_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "No such channel.",
+                        "code": "NO_SUCH_CHANNEL",
+                        "id": "b1653923-5453-4edc-b786-7c4f39bb0bbb"
+                      }
+                    }
+                  },
+                  "YOU_HAVE_BEEN_BLOCKED": {
+                    "value": {
+                      "error": {
+                        "message": "You have been blocked by this user.",
+                        "code": "YOU_HAVE_BEEN_BLOCKED",
+                        "id": "b390d7e1-8a5e-46ed-b625-06271cafd3d3"
+                      }
+                    }
+                  },
+                  "NO_SUCH_FILE": {
+                    "value": {
+                      "error": {
+                        "message": "Some files are not found.",
+                        "code": "NO_SUCH_FILE",
+                        "id": "b6992544-63e7-67f0-fa7f-32444b1b5306"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot renote outside of channel.",
+                        "code": "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL",
+                        "id": "33510210-8452-094c-6227-4a6c05d99f00"
+                      }
+                    }
+                  },
+                  "CONTAINS_PROHIBITED_WORDS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it contains prohibited words.",
+                        "code": "CONTAINS_PROHIBITED_WORDS",
+                        "id": "aa6e01d3-a85c-669d-758a-76aab43af334"
+                      }
+                    }
+                  },
+                  "CONTAINS_TOO_MANY_MENTIONS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it exceeds the allowed number of mentions.",
+                        "code": "CONTAINS_TOO_MANY_MENTIONS",
+                        "id": "4de0363a-3046-481b-9b0f-feff3e211025"
+                      }
+                    }
+                  },
+                  "TOO_MANY_DRAFTS": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot create drafts any more.",
+                        "code": "TOO_MANY_DRAFTS",
+                        "id": "9ee33bbe-fde3-4c71-9b51-e50492c6b9c8"
+                      }
+                    }
+                  },
+                  "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"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "429": {
+            "description": "Too many requests",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "RATE_LIMIT_EXCEEDED": {
+                    "value": {
+                      "error": {
+                        "message": "Rate limit exceeded. Please try again later.",
+                        "code": "RATE_LIMIT_EXCEEDED",
+                        "id": "d5826d14-3982-4d2e-8011-b9e9f02499ef"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "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"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/notes/drafts/delete": {
+      "post": {
+        "operationId": "notes___drafts___delete",
+        "summary": "notes/drafts/delete",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts/delete.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "draftId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  }
+                },
+                "required": [
+                  "draftId"
+                ]
+              }
+            }
+          }
+        },
+        "responses": {
+          "204": {
+            "description": "OK (without any results)"
+          },
+          "400": {
+            "description": "Client error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "NO_SUCH_NOTE_DRAFT": {
+                    "value": {
+                      "error": {
+                        "message": "No such note draft.",
+                        "code": "NO_SUCH_NOTE_DRAFT",
+                        "id": "49cd6b9d-848e-41ee-b0b9-adaca711a6b1"
+                      }
+                    }
+                  },
+                  "ACCESS_DENIED": {
+                    "value": {
+                      "error": {
+                        "message": "Access denied.",
+                        "code": "ACCESS_DENIED",
+                        "id": "56f35758-7dd5-468b-8439-5d6fb8ec9b8e"
+                      }
+                    }
+                  },
+                  "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"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    "/notes/drafts/update": {
+      "post": {
+        "operationId": "notes___drafts___update",
+        "summary": "notes/drafts/update",
+        "description": "No description provided.\n\n**Credential required**: *Yes* / **Permission**: *write:account*",
+        "externalDocs": {
+          "description": "Source code",
+          "url": "https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/notes/drafts/update.ts"
+        },
+        "tags": [
+          "notes"
+        ],
+        "security": [
+          {
+            "bearerAuth": []
+          }
+        ],
+        "requestBody": {
+          "required": true,
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "properties": {
+                  "draftId": {
+                    "type": "string",
+                    "format": "misskey:id"
+                  },
+                  "visibility": {
+                    "type": "string",
+                    "enum": [
+                      "public",
+                      "home",
+                      "followers",
+                      "specified"
+                    ],
+                    "default": "public"
+                  },
+                  "visibleUserIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "cw": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 1,
+                    "maxLength": 100
+                  },
+                  "hashtag": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "maxLength": 200
+                  },
+                  "localOnly": {
+                    "type": "boolean",
+                    "default": false
+                  },
+                  "reactionAcceptance": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "enum": [
+                      null,
+                      "likeOnly",
+                      "likeOnlyForRemote",
+                      "nonSensitiveOnly",
+                      "nonSensitiveOnlyForLocalLikeOnlyForRemote"
+                    ],
+                    "default": null
+                  },
+                  "replyId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "renoteId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "channelId": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "format": "misskey:id"
+                  },
+                  "text": {
+                    "type": [
+                      "string",
+                      "null"
+                    ],
+                    "minLength": 0,
+                    "maxLength": 3000
+                  },
+                  "fileIds": {
+                    "type": "array",
+                    "uniqueItems": true,
+                    "minItems": 1,
+                    "maxItems": 16,
+                    "items": {
+                      "type": "string",
+                      "format": "misskey:id"
+                    }
+                  },
+                  "poll": {
+                    "type": [
+                      "object",
+                      "null"
+                    ],
+                    "properties": {
+                      "choices": {
+                        "type": "array",
+                        "uniqueItems": true,
+                        "minItems": 0,
+                        "maxItems": 10,
+                        "items": {
+                          "type": "string",
+                          "minLength": 1,
+                          "maxLength": 50
+                        }
+                      },
+                      "multiple": {
+                        "type": "boolean"
+                      },
+                      "expiresAt": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ]
+                      },
+                      "expiredAfter": {
+                        "type": [
+                          "integer",
+                          "null"
+                        ],
+                        "minimum": 1
+                      }
+                    },
+                    "required": [
+                      "choices"
+                    ]
+                  }
+                },
+                "required": [
+                  "draftId"
+                ]
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "OK (with results)",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "object",
+                  "properties": {
+                    "updatedDraft": {
+                      "type": "object",
+                      "$ref": "#/components/schemas/NoteDraft"
+                    }
+                  },
+                  "required": [
+                    "updatedDraft"
+                  ]
+                }
+              }
+            }
+          },
+          "400": {
+            "description": "Client error",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "NO_SUCH_RENOTE_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such renote target.",
+                        "code": "NO_SUCH_RENOTE_TARGET",
+                        "id": "b5c90186-4ab0-49c8-9bba-a1f76c282ba4"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote a pure Renote.",
+                        "code": "CANNOT_RENOTE_TO_A_PURE_RENOTE",
+                        "id": "fd4cc33e-2a37-48dd-99cc-9b806eb2031a"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_DUE_TO_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You can not Renote due to target visibility.",
+                        "code": "CANNOT_RENOTE_DUE_TO_VISIBILITY",
+                        "id": "be9529e9-fe72-4de0-ae43-0b363c4938af"
+                      }
+                    }
+                  },
+                  "NO_SUCH_REPLY_TARGET": {
+                    "value": {
+                      "error": {
+                        "message": "No such reply target.",
+                        "code": "NO_SUCH_REPLY_TARGET",
+                        "id": "749ee0f6-d3da-459a-bf02-282e2da4292c"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to an invisible Note.",
+                        "code": "CANNOT_REPLY_TO_AN_INVISIBLE_NOTE",
+                        "id": "b98980fa-3780-406c-a935-b6d0eeee10d1"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_A_PURE_RENOTE": {
+                    "value": {
+                      "error": {
+                        "message": "You can not reply to a pure Renote.",
+                        "code": "CANNOT_REPLY_TO_A_PURE_RENOTE",
+                        "id": "3ac74a84-8fd5-4bb0-870f-01804f82ce15"
+                      }
+                    }
+                  },
+                  "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY": {
+                    "value": {
+                      "error": {
+                        "message": "You cannot reply to a specified visibility note with extended visibility.",
+                        "code": "CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY",
+                        "id": "ed940410-535c-4d5e-bfa3-af798671e93c"
+                      }
+                    }
+                  },
+                  "CANNOT_CREATE_ALREADY_EXPIRED_POLL": {
+                    "value": {
+                      "error": {
+                        "message": "Poll is already expired.",
+                        "code": "CANNOT_CREATE_ALREADY_EXPIRED_POLL",
+                        "id": "04da457d-b083-4055-9082-955525eda5a5"
+                      }
+                    }
+                  },
+                  "NO_SUCH_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "No such channel.",
+                        "code": "NO_SUCH_CHANNEL",
+                        "id": "b1653923-5453-4edc-b786-7c4f39bb0bbb"
+                      }
+                    }
+                  },
+                  "YOU_HAVE_BEEN_BLOCKED": {
+                    "value": {
+                      "error": {
+                        "message": "You have been blocked by this user.",
+                        "code": "YOU_HAVE_BEEN_BLOCKED",
+                        "id": "b390d7e1-8a5e-46ed-b625-06271cafd3d3"
+                      }
+                    }
+                  },
+                  "NO_SUCH_FILE": {
+                    "value": {
+                      "error": {
+                        "message": "Some files are not found.",
+                        "code": "NO_SUCH_FILE",
+                        "id": "b6992544-63e7-67f0-fa7f-32444b1b5306"
+                      }
+                    }
+                  },
+                  "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot renote outside of channel.",
+                        "code": "CANNOT_RENOTE_OUTSIDE_OF_CHANNEL",
+                        "id": "33510210-8452-094c-6227-4a6c05d99f00"
+                      }
+                    }
+                  },
+                  "CONTAINS_PROHIBITED_WORDS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it contains prohibited words.",
+                        "code": "CONTAINS_PROHIBITED_WORDS",
+                        "id": "aa6e01d3-a85c-669d-758a-76aab43af334"
+                      }
+                    }
+                  },
+                  "CONTAINS_TOO_MANY_MENTIONS": {
+                    "value": {
+                      "error": {
+                        "message": "Cannot post because it exceeds the allowed number of mentions.",
+                        "code": "CONTAINS_TOO_MANY_MENTIONS",
+                        "id": "4de0363a-3046-481b-9b0f-feff3e211025"
+                      }
+                    }
+                  },
+                  "NO_SUCH_NOTE_DRAFT": {
+                    "value": {
+                      "error": {
+                        "message": "No such note draft.",
+                        "code": "NO_SUCH_NOTE_DRAFT",
+                        "id": "49cd6b9d-848e-41ee-b0b9-adaca711a6b1"
+                      }
+                    }
+                  },
+                  "ACCESS_DENIED": {
+                    "value": {
+                      "error": {
+                        "message": "Access denied.",
+                        "code": "ACCESS_DENIED",
+                        "id": "56f35758-7dd5-468b-8439-5d6fb8ec9b8e"
+                      }
+                    }
+                  },
+                  "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"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "429": {
+            "description": "Too many requests",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Error"
+                },
+                "examples": {
+                  "RATE_LIMIT_EXCEEDED": {
+                    "value": {
+                      "error": {
+                        "message": "Rate limit exceeded. Please try again later.",
+                        "code": "RATE_LIMIT_EXCEEDED",
+                        "id": "d5826d14-3982-4d2e-8011-b9e9f02499ef"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "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"
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
     "/notes/favorites/create": {
       "post": {
         "operationId": "notes___favorites___create",
@@ -80044,6 +81274,215 @@
           "repliesCount"
         ]
       },
+      "NoteDraft": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "string",
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "createdAt": {
+            "type": "string",
+            "format": "date-time"
+          },
+          "text": {
+            "type": [
+              "string",
+              "null"
+            ]
+          },
+          "cw": {
+            "type": [
+              "string",
+              "null"
+            ]
+          },
+          "userId": {
+            "type": "string",
+            "format": "id"
+          },
+          "user": {
+            "type": "object",
+            "$ref": "#/components/schemas/UserLite"
+          },
+          "replyId": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "renoteId": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "reply": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/Note"
+              }
+            ]
+          },
+          "renote": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/Note"
+              }
+            ]
+          },
+          "visibility": {
+            "type": "string",
+            "enum": [
+              "public",
+              "home",
+              "followers",
+              "specified"
+            ]
+          },
+          "visibleUserIds": {
+            "type": "array",
+            "items": {
+              "type": "string",
+              "format": "id"
+            }
+          },
+          "fileIds": {
+            "type": "array",
+            "items": {
+              "type": "string",
+              "format": "id"
+            }
+          },
+          "files": {
+            "type": "array",
+            "items": {
+              "type": "object",
+              "$ref": "#/components/schemas/DriveFile"
+            }
+          },
+          "hashtag": {
+            "type": "string"
+          },
+          "poll": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "properties": {
+              "expiresAt": {
+                "type": [
+                  "string",
+                  "null"
+                ],
+                "format": "date-time"
+              },
+              "expiredAfter": {
+                "type": [
+                  "number",
+                  "null"
+                ]
+              },
+              "multiple": {
+                "type": "boolean"
+              },
+              "choices": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              }
+            },
+            "required": [
+              "multiple",
+              "choices"
+            ]
+          },
+          "channelId": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "format": "id",
+            "example": "xxxxxxxxxx"
+          },
+          "channel": {
+            "type": [
+              "object",
+              "null"
+            ],
+            "properties": {
+              "id": {
+                "type": "string"
+              },
+              "name": {
+                "type": "string"
+              },
+              "color": {
+                "type": "string"
+              },
+              "isSensitive": {
+                "type": "boolean"
+              },
+              "allowRenoteToExternal": {
+                "type": "boolean"
+              },
+              "userId": {
+                "type": [
+                  "string",
+                  "null"
+                ]
+              }
+            },
+            "required": [
+              "id",
+              "name",
+              "color",
+              "isSensitive",
+              "allowRenoteToExternal",
+              "userId"
+            ]
+          },
+          "localOnly": {
+            "type": "boolean"
+          },
+          "reactionAcceptance": {
+            "type": [
+              "string",
+              "null"
+            ],
+            "enum": [
+              "likeOnly",
+              "likeOnlyForRemote",
+              "nonSensitiveOnly",
+              "nonSensitiveOnlyForLocalLikeOnlyForRemote",
+              null
+            ]
+          }
+        },
+        "required": [
+          "id",
+          "createdAt",
+          "text",
+          "userId",
+          "user",
+          "visibility",
+          "reactionAcceptance"
+        ]
+      },
       "NoteReaction": {
         "type": "object",
         "properties": {
@@ -82753,6 +84192,9 @@
           },
           "canImportUserLists": {
             "type": "boolean"
+          },
+          "noteDraftLimit": {
+            "type": "integer"
           }
         },
         "required": [
@@ -82786,7 +84228,8 @@
           "canImportBlocking",
           "canImportFollowing",
           "canImportMuting",
-          "canImportUserLists"
+          "canImportUserLists",
+          "noteDraftLimit"
         ]
       },
       "ReversiGameLite": {

Get diff files from Workflow Page

@kakkokari-gtyih
Copy link
Contributor

kakkokari-gtyih commented Jan 16, 2025

今日は忙しいので明日以降に見ます

@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 16, 2025

まだDraftのためレビュー頂いても変わる可能性はままある

@github-actions github-actions bot added the packages/frontend Client side specific issue/PR label Jan 16, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 16, 2025

とりあえず下書きの取得、保存、削除はかけたのでどのタイミングで保存するかとかUIの調整とかはやっていきたい

@Sayamame-beans
Copy link
Member

Sayamame-beans commented Jan 17, 2025

ちなみに、内容がある状態で下書き画面開いて選択した場合って問答無用で上書きされちゃいます…? (過去の分を消そうとして誤選択とか起きそうな気がしたので)

@kakkokari-gtyih
Copy link
Contributor

ちなみに、内容がある状態で下書き画面開いて選択した場合って問答無用で上書きされちゃいます…? (過去の分を消そうとして誤選択とか起きそうな気がしたので)

下書きの復旧にワンクッションある(復旧したい下書きを選択してから決定ボタンを押す)ので誤爆は稀な気がする

@kakkokari-gtyih
Copy link
Contributor

@tai-cha 一通り見たので確認お願いします

@kakkokari-gtyih kakkokari-gtyih removed their assignment Jan 17, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 17, 2025

下書き自体セレクタはまあいい感じとして下書きの挙動がこれでいいかはしゅいろさんの判断になりそうだからいったんこの仕様で細かいFixいれるか(リクエストパラメータ周りなど)

@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 17, 2025

良さそうに見えるけどコード書いたの我々だしレビューに進めるか

@tai-cha tai-cha marked this pull request as ready for review January 17, 2025 05:33
@tai-cha tai-cha changed the title feat: draft of note feat: ノートの下書き(draft of note) Jan 17, 2025
@kakkokari-gtyih kakkokari-gtyih added this to the v2025.2.0~ milestone Jan 17, 2025
@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 19, 2025

CHANGELOG忘れてるけど次バージョン以降になる予定だから変な位置にCHANGELOG行きそうだから保留

@tai-cha
Copy link
Contributor Author

tai-cha commented Jan 20, 2025

エンドポイント定義の方法が変わったため、コンフリクト解消

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
packages/backend Server side specific issue/PR packages/frontend Client side specific issue/PR packages/misskey-js
Projects
Development

Successfully merging this pull request may close these issues.

Draft of note
3 participants