From b10b92a876eb185a88e751d028e69063c9117298 Mon Sep 17 00:00:00 2001
From: Shpuld Shpuldson <shpuld@shpposter.club>
Date: Tue, 14 Jan 2020 10:06:14 +0200
Subject: [PATCH] clean up code, fix prediction bug

---
 .../emoji_reactions/emoji_reactions.js        | 30 +++++++++++
 .../emoji_reactions/emoji_reactions.vue       | 51 +++++++++++++++++++
 src/components/react_button/react_button.js   |  5 +-
 src/components/react_button/react_button.vue  | 46 +++++++++--------
 src/components/status/status.js               | 23 ++-------
 src/components/status/status.vue              | 47 ++---------------
 src/modules/statuses.js                       |  9 ++--
 7 files changed, 122 insertions(+), 89 deletions(-)
 create mode 100644 src/components/emoji_reactions/emoji_reactions.js
 create mode 100644 src/components/emoji_reactions/emoji_reactions.vue

diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js
new file mode 100644
index 00000000..e81e6e25
--- /dev/null
+++ b/src/components/emoji_reactions/emoji_reactions.js
@@ -0,0 +1,30 @@
+
+const EmojiReactions = {
+  name: 'EmojiReactions',
+  props: ['status'],
+  computed: {
+    emojiReactions () {
+      return this.status.emojiReactions
+    }
+  },
+  methods: {
+    reactedWith (emoji) {
+      return this.status.reactedWithEmoji.includes(emoji)
+    },
+    reactWith (emoji) {
+      this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
+    },
+    unreact (emoji) {
+      this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
+    },
+    emojiOnClick (emoji, event) {
+      if (this.reactedWith(emoji)) {
+        this.unreact(emoji)
+      } else {
+        this.reactWith(emoji)
+      }
+    }
+  }
+}
+
+export default EmojiReactions
diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue
new file mode 100644
index 00000000..d83f60b6
--- /dev/null
+++ b/src/components/emoji_reactions/emoji_reactions.vue
@@ -0,0 +1,51 @@
+<template>
+  <div class="emoji-reactions">
+    <button
+      v-for="(users, emoji) in emojiReactions"
+      :key="emoji"
+      class="emoji-reaction btn btn-default"
+      :class="{ 'picked-reaction': reactedWith(emoji) }"
+      @click="emojiOnClick(emoji, $event)"
+    >
+      <span v-if="users">{{ users.length }}</span>
+      <span>{{ emoji }}</span>
+    </button>
+  </div>
+</template>
+
+<script src="./emoji_reactions.js" ></script>
+<style lang="scss">
+@import '../../_variables.scss';
+
+.emoji-reactions {
+  display: flex;
+  margin-top: 0.25em;
+  flex-wrap: wrap;
+}
+
+.emoji-reaction {
+  padding: 0 0.5em;
+  margin-right: 0.5em;
+  margin-top: 0.5em;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-sizing: border-box;
+  :first-child {
+    margin-right: 0.25em;
+  }
+  :last-child {
+    width: 1.5em;
+  }
+  &:focus {
+    outline: none;
+  }
+}
+
+.picked-reaction {
+  border: 1px solid var(--link, $fallback--link);
+  margin-left: -1px; // offset the border, can't use inset shadows either
+  margin-right: calc(0.5em - 1px);
+}
+
+</style>
diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js
index 76a49305..d1a179bc 100644
--- a/src/components/react_button/react_button.js
+++ b/src/components/react_button/react_button.js
@@ -15,8 +15,9 @@ const ReactButton = {
     }
   },
   methods: {
-    toggleReactionSelect () {
-      this.showTooltip = !this.showTooltip
+    openReactionSelect () {
+      this.showTooltip = true
+      this.filterWord = ''
     },
     closeReactionSelect () {
       this.showTooltip = false
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index f7015316..ae975dee 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -9,13 +9,16 @@
   >
     <div slot="popover">
       <div class="reaction-picker-filter">
-        <input v-model="filterWord" placeholder="Search...">
+        <input
+          v-model="filterWord"
+          :placeholder="$t('emoji.search_emoji')"
+        >
       </div>
       <div class="reaction-picker">
         <span
           v-for="emoji in commonEmojis"
           :key="emoji"
-          class="emoji-reaction-button"
+          class="emoji-button"
           @click="addReaction($event, emoji)"
         >
           {{ emoji }}
@@ -24,7 +27,7 @@
         <span
           v-for="(emoji, key) in emojis"
           :key="key"
-          class="emoji-reaction-button"
+          class="emoji-button"
           @click="addReaction($event, emoji.replacement)"
         >
           {{ emoji.replacement }}
@@ -34,11 +37,11 @@
     </div>
     <div
       v-if="loggedIn"
-      @click.prevent="toggleReactionSelect"
+      @click.prevent="openReactionSelect"
     >
       <i
         :class="classes"
-        class="button-icon favorite-button fav-active"
+        class="button-icon add-reaction-button"
         :title="$t('tool_tip.add_reaction')"
       />
       <span v-if="!mergedConfig.hidePostStats && status.fave_num > 0">{{ status.fave_num }}</span>
@@ -58,7 +61,7 @@
 .reaction-picker-divider {
   height: 1px;
   width: 100%;
-  margin: 0.4em;
+  margin: 0.5em;
   background-color: var(--border, $fallback--border);
 }
 
@@ -82,26 +85,27 @@
   // Autoprefixed seem to ignore this one, and also syntax is different
   -webkit-mask-composite: xor;
   mask-composite: exclude;
-}
 
-.emoji-reaction-button {
-  flex-basis: 20%;
-  line-height: 1.5em;
-  align-content: center;
-}
+  .emoji-button {
+    cursor: pointer;
 
-.fav-active {
-  cursor: pointer;
-  animation-duration: 0.6s;
+    flex-basis: 20%;
+    line-height: 1.5em;
+    align-content: center;
 
-  &:hover {
-    color: $fallback--cOrange;
-    color: var(--cOrange, $fallback--cOrange);
+    &:hover {
+      transform: scale(1.25);
+    }
   }
 }
 
-.favorite-button.icon-star {
-  color: $fallback--cOrange;
-  color: var(--cOrange, $fallback--cOrange);
+.add-reaction-button {
+  cursor: pointer;
+
+  &:hover {
+    color: $fallback--text;
+    color: var(--text, $fallback--text);
+  }
 }
+
 </style>
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 18617938..81b57667 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -12,6 +12,7 @@ import LinkPreview from '../link-preview/link-preview.vue'
 import AvatarList from '../avatar_list/avatar_list.vue'
 import Timeago from '../timeago/timeago.vue'
 import StatusPopover from '../status_popover/status_popover.vue'
+import EmojiReactions from '../emoji_reactions/emoji_reactions.vue'
 import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
 import fileType from 'src/services/file_type/file_type.service'
 import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js'
@@ -311,9 +312,6 @@ const Status = {
     hidePostStats () {
       return this.mergedConfig.hidePostStats
     },
-    emojiReactions () {
-      return this.status.emojiReactions
-    },
     ...mapGetters(['mergedConfig']),
     ...mapState({
       betterShadow: state => state.interface.browserSupport.cssFilter,
@@ -334,7 +332,8 @@ const Status = {
     LinkPreview,
     AvatarList,
     Timeago,
-    StatusPopover
+    StatusPopover,
+    EmojiReactions
   },
   methods: {
     visibilityIcon (visibility) {
@@ -418,22 +417,6 @@ const Status = {
     setMedia () {
       const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments
       return () => this.$store.dispatch('setMedia', attachments)
-    },
-    reactedWith (emoji) {
-      return this.status.reactedWithEmoji.includes(emoji)
-    },
-    reactWith (emoji) {
-      this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
-    },
-    unreact (emoji) {
-      this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
-    },
-    emojiOnClick (emoji, event) {
-      if (this.reactedWith(emoji)) {
-        this.unreact(emoji)
-      } else {
-        this.reactWith(emoji)
-      }
     }
   },
   watch: {
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 4ea1b74b..87e8b5da 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -354,18 +354,10 @@
             </div>
           </transition>
 
-          <div v-if="isFocused" class="emoji-reactions">
-            <button
-              v-for="(users, emoji) in emojiReactions"
-              :key="emoji"
-              class="emoji-reaction btn btn-default"
-              :class="{ 'picked-reaction': reactedWith(emoji) }"
-              @click="emojiOnClick(emoji, $event)"
-            >
-              <span v-if="users">{{ users.length }}</span>
-              <span>{{ emoji }}</span>
-            </button>
-          </div>
+          <EmojiReactions
+            v-if="isFocused"
+            :status="status"
+          />
 
           <div
             v-if="!noHeading && !isPreview"
@@ -789,37 +781,6 @@ $status-margin: 0.75em;
   }
 }
 
-.emoji-reactions {
-  display: flex;
-  margin-top: 0.25em;
-  flex-wrap: wrap;
-}
-
-.emoji-reaction {
-  padding: 0 0.5em;
-  margin-right: 0.5em;
-  margin-top: 0.5em;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  box-sizing: border-box;
-  :first-child {
-    margin-right: 0.25em;
-  }
-  :last-child {
-    width: 1.5em;
-  }
-  &:focus {
-    outline: none;
-  }
-}
-
-.picked-reaction {
-  border: 1px solid var(--link, $fallback--link);
-  margin-left: -1px; // offset the border, can't use inset shadows either
-  margin-right: calc(0.5em - 1px);
-}
-
 .button-icon.icon-reply {
   &:not(.button-icon-disabled):hover,
   &.button-icon-active {
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index ae6f6853..dbae9d38 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -537,7 +537,10 @@ export const mutations = {
   addEmojiReactions (state, { id, emojiReactions, currentUser }) {
     const status = state.allStatusesObject[id]
     set(status, 'emojiReactions', emojiReactions)
-    const reactedWithEmoji = flow(keys, filter(reaction => find(reaction, { id: currentUser.id })))(emojiReactions)
+    const reactedWithEmoji = flow(
+      keys,
+      filter(reaction => find(reaction, { id: currentUser.id }))
+    )(emojiReactions)
     set(status, 'reactedWithEmoji', reactedWithEmoji)
   },
   addOwnReaction (state, { id, emoji, currentUser }) {
@@ -547,7 +550,7 @@ export const mutations = {
     const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id })
     if (!hasSelfAlready) {
       set(status.emojiReactions, emoji, listOfUsers.concat([{ id: currentUser.id }]))
-      set(status, 'reactedWithEmoji', emoji)
+      set(status, 'reactedWithEmoji', [...status.reactedWithEmoji, emoji])
     }
   },
   removeOwnReaction (state, { id, emoji, currentUser }) {
@@ -557,7 +560,7 @@ export const mutations = {
     if (hasSelfAlready) {
       const newUsers = filter(listOfUsers, user => user.id !== currentUser.id)
       set(status.emojiReactions, emoji, newUsers)
-      set(status, 'reactedWith', emoji)
+      set(status, 'reactedWithEmoji', status.reactedWithEmoji.filter(e => e !== emoji))
     }
   },
   updateStatusWithPoll (state, { id, poll }) {