diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md
index c47e18ad1e..9850508286 100644
--- a/CHANGELOG_CHERRYPICK.md
+++ b/CHANGELOG_CHERRYPICK.md
@@ -28,6 +28,7 @@
### General
- 스크롤 시 요소 표시(헤더, 플로팅 버튼, 탐색 모음)를 사용자화 할 수 있는 옵션 추가
- 노트 작성 폼의 "노트" 버튼을 "냥!"으로 변경할 수 있는 옵션 추가
+- 노트의 리액션을 삭제하지 않고도 리액션 버튼을 눌러 리액션을 변경할 수 있도록 (misskey-dev/misskey#11157)
### Client
- 이모티콘 피커의 검색 건수를 100개로 증가 (misskey-dev/misskey#11371)
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 4618936b9b..6501b78d2e 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -133,12 +133,12 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+
+
+
-
-
-
+
+
@@ -401,19 +401,43 @@ function react(viaKeyboard = false): void {
} else {
blur();
reactionPicker.show(reactButton.value, reaction => {
- os.api('notes/reactions/create', {
- noteId: appearNote.id,
- reaction: reaction,
- });
- if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
- claimAchievement('reactWithoutRead');
- }
+ toggleReaction(reaction);
}, () => {
focus();
});
}
}
+async function toggleReaction(reaction) {
+ const oldReaction = note.myReaction;
+ if (oldReaction) {
+ const confirm = await os.confirm({
+ type: 'warning',
+ text: oldReaction !== reaction ? i18n.ts.changeReactionConfirm : i18n.ts.cancelReactionConfirm,
+ });
+ if (confirm.canceled) return;
+
+ os.api('notes/reactions/delete', {
+ noteId: note.id,
+ }).then(() => {
+ if (oldReaction !== reaction) {
+ os.api('notes/reactions/create', {
+ noteId: note.id,
+ reaction: reaction,
+ });
+ }
+ });
+ } else {
+ os.api('notes/reactions/create', {
+ noteId: appearNote.id,
+ reaction: reaction,
+ });
+ }
+ if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
+ claimAchievement('reactWithoutRead');
+ }
+}
+
function heartReact(): void {
pleaseLogin();
showMovedDialog();
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 787010e9ae..8423467843 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -145,12 +145,12 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
-
-
-
+
+
+
+
+
+
@@ -379,19 +379,43 @@ function react(viaKeyboard = false): void {
} else {
blur();
reactionPicker.show(reactButton.value, reaction => {
- os.api('notes/reactions/create', {
- noteId: appearNote.id,
- reaction: reaction,
- });
- if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
- claimAchievement('reactWithoutRead');
- }
+ toggleReaction(reaction);
}, () => {
focus();
});
}
}
+async function toggleReaction(reaction) {
+ const oldReaction = note.myReaction;
+ if (oldReaction) {
+ const confirm = await os.confirm({
+ type: 'warning',
+ text: oldReaction !== reaction ? i18n.ts.changeReactionConfirm : i18n.ts.cancelReactionConfirm,
+ });
+ if (confirm.canceled) return;
+
+ os.api('notes/reactions/delete', {
+ noteId: note.id,
+ }).then(() => {
+ if (oldReaction !== reaction) {
+ os.api('notes/reactions/create', {
+ noteId: note.id,
+ reaction: reaction,
+ });
+ }
+ });
+ } else {
+ os.api('notes/reactions/create', {
+ noteId: appearNote.id,
+ reaction: reaction,
+ });
+ }
+ if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
+ claimAchievement('reactWithoutRead');
+ }
+}
+
function heartReact(): void {
pleaseLogin();
showMovedDialog();