diff --git a/src/client/app/common/scripts/note-subscriber.ts b/src/client/app/common/scripts/note-subscriber.ts index bc434c436..9545b5406 100644 --- a/src/client/app/common/scripts/note-subscriber.ts +++ b/src/client/app/common/scripts/note-subscriber.ts @@ -95,6 +95,7 @@ export default prop => ({ Vue.set(this.$_ns_target.reactionCounts, reaction, 0); } + // Increment the count this.$_ns_target.reactionCounts[reaction]++; if (body.userId == this.$store.state.i.id) { @@ -103,6 +104,26 @@ export default prop => ({ break; } + case 'unreacted': { + const reaction = body.reaction; + + if (this.$_ns_target.reactionCounts == null) { + return; + } + + if (this.$_ns_target.reactionCounts[reaction] == null) { + return; + } + + // Decrement the count + if (this.$_ns_target.reactionCounts[reaction] > 0) this.$_ns_target.reactionCounts[reaction]--; + + if (body.userId == this.$store.state.i.id) { + Vue.set(this.$_ns_target, 'myReaction', null); + } + break; + } + case 'pollVoted': { if (body.userId == this.$store.state.i.id) return; const choice = body.choice; diff --git a/src/client/app/common/views/components/reactions-viewer.vue b/src/client/app/common/views/components/reactions-viewer.vue index 73267347b..a3c421bd3 100644 --- a/src/client/app/common/views/components/reactions-viewer.vue +++ b/src/client/app/common/views/components/reactions-viewer.vue @@ -1,16 +1,16 @@ <template> <div class="mk-reactions-viewer"> <template v-if="reactions"> - <span :class="{ reacted: note.myReaction == 'like' }" @click="react('like')" v-if="reactions.like" v-particle><mk-reaction-icon reaction="like" ref="like"/><span>{{ reactions.like }}</span></span> - <span :class="{ reacted: note.myReaction == 'love' }" @click="react('love')" v-if="reactions.love" v-particle><mk-reaction-icon reaction="love" ref="love"/><span>{{ reactions.love }}</span></span> - <span :class="{ reacted: note.myReaction == 'laugh' }" @click="react('laugh')" v-if="reactions.laugh" v-particle><mk-reaction-icon reaction="laugh" ref="laugh"/><span>{{ reactions.laugh }}</span></span> - <span :class="{ reacted: note.myReaction == 'hmm' }" @click="react('hmm')" v-if="reactions.hmm" v-particle><mk-reaction-icon reaction="hmm" ref="hmm"/><span>{{ reactions.hmm }}</span></span> - <span :class="{ reacted: note.myReaction == 'surprise' }" @click="react('surprise')" v-if="reactions.surprise" v-particle><mk-reaction-icon reaction="surprise" ref="surprise"/><span>{{ reactions.surprise }}</span></span> - <span :class="{ reacted: note.myReaction == 'congrats' }" @click="react('congrats')" v-if="reactions.congrats" v-particle><mk-reaction-icon reaction="congrats" ref="congrats"/><span>{{ reactions.congrats }}</span></span> - <span :class="{ reacted: note.myReaction == 'angry' }" @click="react('angry')" v-if="reactions.angry" v-particle><mk-reaction-icon reaction="angry" ref="angry"/><span>{{ reactions.angry }}</span></span> - <span :class="{ reacted: note.myReaction == 'confused' }" @click="react('confused')" v-if="reactions.confused" v-particle><mk-reaction-icon reaction="confused" ref="confused"/><span>{{ reactions.confused }}</span></span> - <span :class="{ reacted: note.myReaction == 'rip' }" @click="react('rip')" v-if="reactions.rip" v-particle><mk-reaction-icon reaction="rip" ref="rip"/><span>{{ reactions.rip }}</span></span> - <span :class="{ reacted: note.myReaction == 'pudding' }" @click="react('pudding')" v-if="reactions.pudding" v-particle><mk-reaction-icon reaction="pudding" ref="pudding"/><span>{{ reactions.pudding }}</span></span> + <span :class="{ reacted: note.myReaction == 'like' }" @click="toggleReaction('like')" v-if="reactions.like" v-particle><mk-reaction-icon reaction="like" ref="like"/><span>{{ reactions.like }}</span></span> + <span :class="{ reacted: note.myReaction == 'love' }" @click="toggleReaction('love')" v-if="reactions.love" v-particle><mk-reaction-icon reaction="love" ref="love"/><span>{{ reactions.love }}</span></span> + <span :class="{ reacted: note.myReaction == 'laugh' }" @click="toggleReaction('laugh')" v-if="reactions.laugh" v-particle><mk-reaction-icon reaction="laugh" ref="laugh"/><span>{{ reactions.laugh }}</span></span> + <span :class="{ reacted: note.myReaction == 'hmm' }" @click="toggleReaction('hmm')" v-if="reactions.hmm" v-particle><mk-reaction-icon reaction="hmm" ref="hmm"/><span>{{ reactions.hmm }}</span></span> + <span :class="{ reacted: note.myReaction == 'surprise' }" @click="toggleReaction('surprise')" v-if="reactions.surprise" v-particle><mk-reaction-icon reaction="surprise" ref="surprise"/><span>{{ reactions.surprise }}</span></span> + <span :class="{ reacted: note.myReaction == 'congrats' }" @click="toggleReaction('congrats')" v-if="reactions.congrats" v-particle><mk-reaction-icon reaction="congrats" ref="congrats"/><span>{{ reactions.congrats }}</span></span> + <span :class="{ reacted: note.myReaction == 'angry' }" @click="toggleReaction('angry')" v-if="reactions.angry" v-particle><mk-reaction-icon reaction="angry" ref="angry"/><span>{{ reactions.angry }}</span></span> + <span :class="{ reacted: note.myReaction == 'confused' }" @click="toggleReaction('confused')" v-if="reactions.confused" v-particle><mk-reaction-icon reaction="confused" ref="confused"/><span>{{ reactions.confused }}</span></span> + <span :class="{ reacted: note.myReaction == 'rip' }" @click="toggleReaction('rip')" v-if="reactions.rip" v-particle><mk-reaction-icon reaction="rip" ref="rip"/><span>{{ reactions.rip }}</span></span> + <span :class="{ reacted: note.myReaction == 'pudding' }" @click="toggleReaction('pudding')" v-if="reactions.pudding" v-particle><mk-reaction-icon reaction="pudding" ref="pudding"/><span>{{ reactions.pudding }}</span></span> </template> </div> </template> @@ -60,11 +60,25 @@ export default Vue.extend({ } }, methods: { - react(reaction: string) { - this.$root.api('notes/reactions/create', { - noteId: this.note.id, - reaction: reaction - }); + toggleReaction(reaction: string) { + const oldReaction = this.note.myReaction; + if (oldReaction) { + this.$root.api('notes/reactions/delete', { + noteId: this.note.id + }).then(() => { + if (oldReaction !== reaction) { + this.$root.api('notes/reactions/create', { + noteId: this.note.id, + reaction: reaction + }); + } + }); + } else { + this.$root.api('notes/reactions/create', { + noteId: this.note.id, + reaction: reaction + }); + } }, anime(reaction: string) { if (this.$store.state.device.reduceMotion) return; diff --git a/src/server/api/endpoints/notes/reactions/delete.ts b/src/server/api/endpoints/notes/reactions/delete.ts index 367538bed..40139afee 100644 --- a/src/server/api/endpoints/notes/reactions/delete.ts +++ b/src/server/api/endpoints/notes/reactions/delete.ts @@ -2,6 +2,8 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id import Reaction from '../../../../../models/note-reaction'; import Note from '../../../../../models/note'; import define from '../../../define'; +import { publishNoteStream } from '../../../../../stream'; +const ms = require('ms'); export const meta = { desc: { @@ -13,6 +15,12 @@ export const meta = { kind: 'reaction-write', + limit: { + duration: ms('1hour'), + max: 5, + minInterval: ms('3sec') + }, + params: { noteId: { validator: $.type(ID), @@ -60,4 +68,9 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { Note.update({ _id: note._id }, { $inc: dec }); + + publishNoteStream(note._id, 'unreacted', { + reaction: exist.reaction, + userId: user._id + }); }));