From 29e10b6acdedb4d82bdc5b14fe1ddf18ef929049 Mon Sep 17 00:00:00 2001
From: Adam Procter <adamprocter@researchnot.es>
Date: Sun, 1 Mar 2020 18:18:15 +0000
Subject: [PATCH] Emoji reaction version 1

added the ability to add emoji reactions to other nodes. Still a little messy
---
 app/package.json                       |  13 ++-
 app/src/components/NodesLayer.vue      |   7 +-
 app/src/components/OtherNodeslayer.vue | 148 ++++++++++++++++++++++++-
 app/src/store/index.js                 |  51 +++++++++
 app/yarn.lock                          |  74 +++++++------
 5 files changed, 249 insertions(+), 44 deletions(-)

diff --git a/app/package.json b/app/package.json
index f03c165..d02c201 100644
--- a/app/package.json
+++ b/app/package.json
@@ -15,15 +15,16 @@
     "vue": "^2.6.11",
     "vue-context": "^5.0.0",
     "vue-draggable-resizable": "^2.1.0",
-    "vue-router": "^3.1.5",
+    "vue-emoji-picker": "^1.0.1",
+    "vue-router": "^3.1.6",
     "vuex": "^3.1.2"
   },
   "devDependencies": {
-    "@vue/cli-plugin-babel": "^4.2.0",
-    "@vue/cli-plugin-eslint": "^4.2.0",
-    "@vue/cli-plugin-router": "^4.2.0",
-    "@vue/cli-plugin-vuex": "^4.2.0",
-    "@vue/cli-service": "^4.2.0",
+    "@vue/cli-plugin-babel": "^4.2.3",
+    "@vue/cli-plugin-eslint": "^4.2.3",
+    "@vue/cli-plugin-router": "^4.2.3",
+    "@vue/cli-plugin-vuex": "^4.2.3",
+    "@vue/cli-service": "^4.2.3",
     "@vue/eslint-config-prettier": "^6.0.0",
     "babel-eslint": "^10.0.3",
     "eslint": "^6.7.2",
diff --git a/app/src/components/NodesLayer.vue b/app/src/components/NodesLayer.vue
index 0c89324..ed33a22 100644
--- a/app/src/components/NodesLayer.vue
+++ b/app/src/components/NodesLayer.vue
@@ -24,6 +24,10 @@
               :id="nodeid"
             ></textarea>
           </div>
+          <h3>Reactions</h3>
+          <div v-for="(emojis, index) in configEmoji" :key="index">
+            <p class="allemoji" v-if="nodeid == emojis.docid">{{ emojis.emojitext }}</p>
+          </div>
           <!-- <div v-for="(value, index) in configPositions" v-bind:key="index">
           <p v-if="nodeid == value.nodeid">
             {{ localx }}, ({{ value.xpos }}) {{ localy }}
@@ -66,7 +70,8 @@ export default {
   },
   computed: mapState({
     myNodes: state => state.myNodes,
-    configPositions: state => state.configPositions
+    configPositions: state => state.configPositions,
+    configEmoji: state => state.configEmoji
   }),
   methods: {
     onActivated() {
diff --git a/app/src/components/OtherNodeslayer.vue b/app/src/components/OtherNodeslayer.vue
index 3c17b1b..4ebb566 100644
--- a/app/src/components/OtherNodeslayer.vue
+++ b/app/src/components/OtherNodeslayer.vue
@@ -15,6 +15,57 @@
         style="border: 1px solid black; background-color: rgb(205, 234, 255);"
       >
         <p :id="nodeid" :inner-html.prop="nodetext | marked">{{ nodeid }}</p>
+        <h3>Reactions</h3>
+        <div v-for="(emojis, index) in configEmoji" :key="index">
+          <p class="allemoji" v-if="nodeid == emojis.docid">{{ emojis.emojitext }}</p>
+        </div>
+        <div class="react" v-if="nodeid != undefined">
+          <h2>React</h2>
+          <div class="eeee">
+            <input :value="nodeid" name="id" readonly hidden />
+            <input id="emojifield" class="regular-input" v-model="input" readonly />
+
+            <emoji-picker @emoji="append" :search="search">
+              <div
+                class="emoji-invoker"
+                slot="emoji-invoker"
+                slot-scope="{ events: { click: clickEvent } }"
+                @click.stop="clickEvent"
+              >
+                <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
+                  <path d="M0 0h24v24H0z" fill="none" />
+                  <path
+                    d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"
+                  />
+                </svg>
+              </div>
+              <div slot="emoji-picker" slot-scope="{ emojis, insert, display }">
+                <div
+                  class="emoji-picker"
+                  :style="{ top: display.y + 'px', left: display.x + 'px' }"
+                >
+                  <div class="emoji-picker__search">
+                    <input type="text" v-model="search" v-focus />
+                  </div>
+                  <div>
+                    <div v-for="(emojiGroup, category) in emojis" :key="category">
+                      <h5>{{ category }}</h5>
+                      <div class="emojis">
+                        <span
+                          v-for="(emoji, emojiName) in emojiGroup"
+                          :key="emojiName"
+                          @click="insert(emoji)"
+                          :title="emojiName"
+                        >{{ emoji }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </emoji-picker>
+            <button @click="sentReact()">Send Reaction</button>
+          </div>
+        </div>
       </vue-draggable-resizable>
     </div>
   </div>
@@ -22,11 +73,15 @@
 
 <script>
 import { mapState } from 'vuex'
+import EmojiPicker from 'vue-emoji-picker'
 import marked from 'marked'
 
 export default {
   name: 'otherNodeslayer',
 
+  components: {
+    EmojiPicker
+  },
   props: {
     nodeid: String,
     nodetext: String,
@@ -40,7 +95,9 @@ export default {
       width: this.nodewidth,
       height: this.nodeheight,
       localx: 0,
-      localy: 0
+      localy: 0,
+      input: '',
+      search: ''
     }
   },
 
@@ -51,7 +108,8 @@ export default {
   mounted() {},
   computed: mapState({
     otherNodes: state => state.otherNodes,
-    configPositions: state => state.configPositions
+    configPositions: state => state.configPositions,
+    configEmoji: state => state.configEmoji
   }),
   methods: {
     onActivated() {
@@ -98,6 +156,26 @@ export default {
         }
       }
       this.$store.dispatch('movePos', { localnodeid, x, y, width, height })
+    },
+    append(emoji) {
+      this.input += emoji
+    },
+    sentReact(docid, emojitext) {
+      emojitext = this.input
+      docid = this.nodeid
+      this.$store.dispatch('addEmoji', {
+        docid,
+        emojitext
+      })
+
+      this.input = ''
+    }
+  },
+  directives: {
+    focus: {
+      inserted(el) {
+        el.focus()
+      }
     }
   }
 }
@@ -109,4 +187,70 @@ export default {
   background-color: aquamarine;
   position: absolute;
 }
+
+.emoji-invoker {
+  top: -0.5rem;
+  right: 0.5rem;
+  width: 1.5rem;
+  height: 1.5rem;
+  margin-top: 1em;
+  border-radius: 50%;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+.emoji-invoker:hover {
+  transform: scale(1.1);
+}
+.emoji-invoker > svg {
+  fill: #b1c6d0;
+}
+
+.emoji-picker {
+  z-index: 1;
+  font-family: Montserrat;
+  border: 1px solid #ccc;
+  width: 18rem;
+  height: 20rem;
+  overflow: scroll;
+  padding: 1rem;
+  box-sizing: border-box;
+  border-radius: 0.5rem;
+  background: #fff;
+  box-shadow: 1px 1px 8px #c7dbe6;
+}
+.emoji-picker__search {
+  display: flex;
+}
+.emoji-picker__search > input {
+  flex: 1;
+  border-radius: 10rem;
+  border: 1px solid #ccc;
+  padding: 0.5rem 1rem;
+  outline: none;
+}
+.emoji-picker h5 {
+  margin-bottom: 0;
+  color: #b1b1b1;
+  text-transform: uppercase;
+  font-size: 0.8rem;
+  cursor: default;
+}
+.emoji-picker .emojis {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+}
+.emoji-picker .emojis:after {
+  content: '';
+  flex: auto;
+}
+.emoji-picker .emojis span {
+  padding: 0.2rem;
+  cursor: pointer;
+  border-radius: 5px;
+}
+.emoji-picker .emojis span:hover {
+  background: #ececec;
+  cursor: pointer;
+}
 </style>
diff --git a/app/src/store/index.js b/app/src/store/index.js
index a491a8f..81fa567 100644
--- a/app/src/store/index.js
+++ b/app/src/store/index.js
@@ -359,14 +359,58 @@ const store = new Vuex.Store({
             // pouchdb.put({  })
           }
         })
+    },
+    GET_EMOJI(state) {
+      console.log
+      pouchdb
+        .get(state.global_emoji_name)
+        .then(function(doc) {
+          state.configEmoji = doc.emojis
+        })
+        .catch(function(err) {
+          console.log(err)
+          if (err.status == 404) {
+            return pouchdb.put({
+              _id: state.global_emoji_name,
+              emojis: []
+            })
+          }
+        })
+    },
+    ADD_EMOJI(state, e) {
+      var uniqueid =
+        Math.random()
+          .toString(36)
+          .substring(2, 15) +
+        Math.random()
+          .toString(36)
+          .substring(2, 15)
+      pouchdb.get(state.global_emoji_name).then(function(doc) {
+        doc.emojis.push({
+          id: uniqueid,
+          docid: e.docid,
+          emojitext: e.emojitext
+        })
+        return pouchdb
+          .put({
+            _id: state.global_emoji_name,
+            _rev: doc._rev,
+            emojis: doc.emojis
+          })
+          .catch(function(err) {
+            console.log(err)
+          })
+      })
     }
   },
+
   actions: {
     syncDB: () => {
       pouchdb.replicate.from(remote).on('complete', function() {
         store.commit('GET_ALL_NODES')
         store.commit('GET_MY_NODES')
         store.commit('GET_POSITIONS')
+        store.commit('GET_EMOJI')
         // turn on two-way, continuous, retriable sync
         pouchdb
           .sync(remote, { live: true, retry: true, attachments: true })
@@ -375,6 +419,7 @@ const store = new Vuex.Store({
             store.commit('GET_ALL_NODES')
             store.commit('GET_MY_NODES')
             store.commit('GET_POSITIONS')
+            store.commit('GET_EMOJI')
           })
           .on('paused', function() {
             // replication paused (e.g. replication up to date, user went offline)
@@ -416,6 +461,12 @@ const store = new Vuex.Store({
     deleteFlag: ({ commit }, e) => {
       // var text = e.target.value
       commit('DELETE_FLAG', e)
+    },
+    addEmoji: ({ commit }, { docid, emojitext }) => {
+      commit('ADD_EMOJI', {
+        docid,
+        emojitext
+      })
     }
   },
   modules: {}
diff --git a/app/yarn.lock b/app/yarn.lock
index bc8b8d3..e183bd9 100644
--- a/app/yarn.lock
+++ b/app/yarn.lock
@@ -783,9 +783,9 @@
     lodash.kebabcase "^4.1.1"
     svg-tags "^1.0.0"
 
-"@vue/babel-preset-app@^4.2.2":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-4.2.2.tgz#c7a0a685a5eb92e1b1538f8d1fc4f5ac00dccec1"
+"@vue/babel-preset-app@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-4.2.3.tgz#608b2c9f7ca677e793833662fc727ff9137a9a35"
   dependencies:
     "@babel/core" "^7.8.4"
     "@babel/helper-compilation-targets" "^7.8.4"
@@ -844,53 +844,53 @@
     "@vue/babel-plugin-transform-vue-jsx" "^1.1.2"
     camelcase "^5.0.0"
 
-"@vue/cli-overlay@^4.2.2":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-4.2.2.tgz#43184ab5b7bc491cac46e3c3e375ec4976fbea5f"
+"@vue/cli-overlay@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/cli-overlay/-/cli-overlay-4.2.3.tgz#fc26d2d48fe189e48d185910ff92433509789515"
 
-"@vue/cli-plugin-babel@^4.2.0":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-4.2.2.tgz#c65cad9921713b9233bab49306559c553a78ee1d"
+"@vue/cli-plugin-babel@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-babel/-/cli-plugin-babel-4.2.3.tgz#8633795126f4d78c517bff9a2539229c9e0c8db4"
   dependencies:
     "@babel/core" "^7.8.4"
-    "@vue/babel-preset-app" "^4.2.2"
-    "@vue/cli-shared-utils" "^4.2.2"
+    "@vue/babel-preset-app" "^4.2.3"
+    "@vue/cli-shared-utils" "^4.2.3"
     babel-loader "^8.0.6"
     cache-loader "^4.1.0"
     thread-loader "^2.1.3"
     webpack "^4.0.0"
 
-"@vue/cli-plugin-eslint@^4.2.0":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.2.2.tgz#ea0ecfc3f816102f61df3050d82f23909aa3df1b"
+"@vue/cli-plugin-eslint@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-eslint/-/cli-plugin-eslint-4.2.3.tgz#2a70df99aaa60697a13893e94034a1386f31c3ee"
   dependencies:
-    "@vue/cli-shared-utils" "^4.2.2"
+    "@vue/cli-shared-utils" "^4.2.3"
     eslint-loader "^2.1.2"
     globby "^9.2.0"
     inquirer "^6.3.1"
     webpack "^4.0.0"
     yorkie "^2.0.0"
 
-"@vue/cli-plugin-router@^4.2.0", "@vue/cli-plugin-router@^4.2.2":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-router/-/cli-plugin-router-4.2.2.tgz#2e510668341e3d2ff839a7b6a76e1250e1734223"
+"@vue/cli-plugin-router@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-router/-/cli-plugin-router-4.2.3.tgz#c64594e7e7e20ba76c71262f283b6199f5afd463"
   dependencies:
-    "@vue/cli-shared-utils" "^4.2.2"
+    "@vue/cli-shared-utils" "^4.2.3"
 
-"@vue/cli-plugin-vuex@^4.2.0", "@vue/cli-plugin-vuex@^4.2.2":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.2.2.tgz#f504b554e7edde945342e3c42cffad80ffa41933"
+"@vue/cli-plugin-vuex@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.2.3.tgz#aa1d8e824dd82b8718c0c40e3906c34ccd752516"
 
-"@vue/cli-service@^4.2.0":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-4.2.2.tgz#b2bc820a46c080a7564df23cf7e5c9592d1acc99"
+"@vue/cli-service@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/cli-service/-/cli-service-4.2.3.tgz#aabb8edfea5dbb59df5b42464f876158b43dd065"
   dependencies:
     "@intervolga/optimize-cssnano-plugin" "^1.0.5"
     "@soda/friendly-errors-webpack-plugin" "^1.7.1"
-    "@vue/cli-overlay" "^4.2.2"
-    "@vue/cli-plugin-router" "^4.2.2"
-    "@vue/cli-plugin-vuex" "^4.2.2"
-    "@vue/cli-shared-utils" "^4.2.2"
+    "@vue/cli-overlay" "^4.2.3"
+    "@vue/cli-plugin-router" "^4.2.3"
+    "@vue/cli-plugin-vuex" "^4.2.3"
+    "@vue/cli-shared-utils" "^4.2.3"
     "@vue/component-compiler-utils" "^3.0.2"
     "@vue/preload-webpack-plugin" "^1.1.0"
     "@vue/web-component-wrapper" "^1.2.0"
@@ -938,9 +938,9 @@
     webpack-dev-server "^3.10.2"
     webpack-merge "^4.2.2"
 
-"@vue/cli-shared-utils@^4.2.2":
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-4.2.2.tgz#953fec34115cb12d0820012a9d7400f8c27d6660"
+"@vue/cli-shared-utils@^4.2.3":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-4.2.3.tgz#13646452cc25b0ab68a57cb52cac27983cee39a4"
   dependencies:
     "@hapi/joi" "^15.0.1"
     chalk "^2.4.2"
@@ -7163,6 +7163,10 @@ vue-draggable-resizable@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/vue-draggable-resizable/-/vue-draggable-resizable-2.1.0.tgz#b590212aef3c07d040aeceda784438068170fb08"
 
+vue-emoji-picker@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/vue-emoji-picker/-/vue-emoji-picker-1.0.1.tgz#0c893b21fb2ac46c6b5803476d744ae3d420f98b"
+
 vue-eslint-parser@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.0.0.tgz#a4ed2669f87179dedd06afdd8736acbb3a3864d6"
@@ -7188,9 +7192,9 @@ vue-loader@^15.8.3:
     vue-hot-reload-api "^2.3.0"
     vue-style-loader "^4.1.0"
 
-vue-router@^3.1.5:
-  version "3.1.5"
-  resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.5.tgz#ff29b8a1e1306c526b52d4dc0532109f16c41231"
+vue-router@^3.1.6:
+  version "3.1.6"
+  resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.6.tgz#45f5a3a3843e31702c061dd829393554e4328f89"
 
 vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
   version "4.1.2"
-- 
GitLab