From: Benjamin Auder <benjamin.auder@somewhere>
Date: Sat, 18 Jan 2020 13:07:22 +0000 (+0100)
Subject: Chat is working
X-Git-Url: https://git.auder.net/variants/Chakart/doc/scripts/css/DESCRIPTION?a=commitdiff_plain;h=5c8e044f7bab43ca8573fdf631f9b87daeda3ad0;p=vchess.git

Chat is working
---

diff --git a/client/src/components/Chat.vue b/client/src/components/Chat.vue
index 364f574c..b4970f70 100644
--- a/client/src/components/Chat.vue
+++ b/client/src/components/Chat.vue
@@ -1,69 +1,69 @@
 <template lang="pug">
-div
-  div
+.row
+  .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+    // TODO: Chat modal sur petit écran, dans la page pour grand écran
     .card.smallpad
       h4 Chat
-      p(v-for="chat in chats" :class={
-        "my-chatmsg": "chat.uid==user.id",
-        "opp-chatmsg": "opponents.any(o => o.id == chat.uid)"}
-        v-html="chat.msg")
-      input#inputChat(type="text" placeholder="st.tr['Type here']"
+      p(v-for="chat in chats" :class="classObject(chat)" v-html="chat.msg")
+      input#inputChat(type="text" :placeholder="st.tr['Type here']"
         @keyup.enter="sendChat")
       button#sendChatBtn(@click="sendChat") {{ st.tr["Send"] }}
 </template>
 
 <script>
-// TODO: myname, opponents (optional, different style), people
-// --> also show messages like "X offers draw" ?
+import { store } from "@/store";
+
 export default {
   name: "my-chat",
-  props: ["opponents","people"],
+  props: ["players"],
   data: function() {
     return {
+      st: store.state,
       chats: [], //chat messages after human game
     };
   },
-//  // TODO: Chat modal sur petit écran, dans la page pour grand écran
-//  created: function() {
-//    const socketMessageListener = msg => {
-//      const data = JSON.parse(msg.data);
-//      switch (data.code)
-//      {
-//        case "newchat":
-//          // TODO: new chat just arrived: data contain all informations
-//          // (uid, name, message; no need for timestamp, we can use local time here)
-//          this.chats.push({msg:data.msg, author:this.oppid});
-//          break;
-//        // TODO: distinguish these (dis)connect events from their analogs in game.js
-//        // TODO: implement and harmonize: opponents and people are arrays, not objects ?!
-//        case "connect":
-//          this.players.push({name:data.name, id:data.uid});
-//          break;
-//        case "disconnect":
-//          const pIdx = this.players.findIndex(p => p.id == data.uid);
-//          this.players.splice(pIdx);
-//          break;
-//      }
-//    };
-//    const socketCloseListener = () => {
-//      this.conn.addEventListener('message', socketMessageListener);
-//      this.conn.addEventListener('close', socketCloseListener);
-//    };
-//    this.conn.onmessage = socketMessageListener;
-//    this.conn.onclose = socketCloseListener;
-//  },
-//  methods: {
-//    // TODO: complete this component
-//    sendChat: function() {
-//      let chatInput = document.getElementById("input-chat");
-//      const chatTxt = chatInput.value;
-//      chatInput.value = "";
-//      this.chats.push({msg:chatTxt, author:this.myid});
-//      this.conn.send(JSON.stringify({
-//        code:"newchat", oppid: this.oppid, msg: chatTxt}));
-//    },
-////    startChat: function(e) {
-////      document.getElementById("modal-chat").checked = true;
-////    },
+  created: function() {
+    const socketMessageListener = msg => {
+      const data = JSON.parse(msg.data);
+      if (data.code == "newchat") //only event at this level
+      {
+        this.chats.push({msg:data.msg,
+          name:data.name || "@nonymous", sid:data.sid});
+      }
+    };
+    const socketCloseListener = () => {
+      store.socketCloseListener(); //reinitialize connexion (in store.js)
+      this.st.conn.addEventListener('message', socketMessageListener);
+      this.st.conn.addEventListener('close', socketCloseListener);
+    };
+    this.st.conn.onmessage = socketMessageListener;
+    this.st.conn.onclose = socketCloseListener;
   },
-});
+  methods: {
+    classObject: function(chat) {
+      return {
+        "my-chatmsg": chat.sid == this.st.user.sid,
+        "opp-chatmsg": this.players.some(
+          p => p.sid == chat.sid && p.sid != this.st.user.sid)
+      };
+    },
+    sendChat: function() {
+      let chatInput = document.getElementById("inputChat");
+      const chatTxt = chatInput.value;
+      chatInput.value = "";
+      const chat = {msg:chatTxt, name: this.st.user.name || "@nonymous",
+        sid:this.st.user.sid};
+      this.chats.push(chat);
+      this.st.conn.send(JSON.stringify({
+        code:"newchat", msg:chatTxt, name:chat.name}));
+    },
+  },
+};
+</script>
+
+<style lang="sass">
+.my-chatmsg
+  color: grey
+.opp-chatmsg
+  color: black
+</style>
diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue
index 6a67400d..8fbc9d0b 100644
--- a/client/src/views/Game.vue
+++ b/client/src/views/Game.vue
@@ -18,13 +18,12 @@
       button(@click="() => abortGame()") Abort
       button(@click="resign") Resign
     textarea(v-if="game.score=='*'" v-model="corrMsg")
-    Chat(
+    Chat(:players="game.players")
 </template>
 
 <!--
 // ==> après, implémenter/vérifier les passages de challenges + parties en cours
 // observer,
-// when send to chat (or a move), reach only this group (send gid along)
 -->
 
 <script>
@@ -40,6 +39,7 @@ export default {
   name: 'my-game',
   components: {
     BaseGame,
+    Chat,
   },
   // gameRef: to find the game in (potentially remote) storage
   data: function() {
diff --git a/server/package-lock.json b/server/package-lock.json
index 8ad645de..16d7de67 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -52,11 +52,11 @@
       }
     },
     "ajv": {
-      "version": "6.10.2",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
-      "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+      "version": "6.11.0",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz",
+      "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==",
       "requires": {
-        "fast-deep-equal": "^2.0.1",
+        "fast-deep-equal": "^3.1.1",
         "fast-json-stable-stringify": "^2.0.0",
         "json-schema-traverse": "^0.4.1",
         "uri-js": "^4.2.2"
@@ -389,9 +389,9 @@
       "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
     },
     "aws4": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
+      "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
     },
     "babel-runtime": {
       "version": "6.26.0",
@@ -1652,14 +1652,14 @@
       }
     },
     "fast-deep-equal": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
+      "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
     },
     "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
     },
     "file-uri-to-path": {
       "version": "1.0.0",
@@ -2440,7 +2440,7 @@
     },
     "get-stream": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
       "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
       "dev": true
     },
@@ -2568,7 +2568,7 @@
     },
     "got": {
       "version": "6.7.1",
-      "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
+      "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz",
       "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
       "dev": true,
       "requires": {
@@ -2750,9 +2750,9 @@
       },
       "dependencies": {
         "readable-stream": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz",
-          "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==",
+          "version": "3.5.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz",
+          "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==",
           "requires": {
             "inherits": "^2.0.3",
             "string_decoder": "^1.1.1",
@@ -3033,7 +3033,7 @@
     },
     "is-obj": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
       "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
       "dev": true
     },
@@ -3584,9 +3584,7 @@
     "nan": {
       "version": "2.14.0",
       "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
-      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
-      "dev": true,
-      "optional": true
+      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
     },
     "nanomatch": {
       "version": "1.2.13",
@@ -3752,14 +3750,22 @@
       }
     },
     "npm-bundled": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
-      "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g=="
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
+      "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
+      "requires": {
+        "npm-normalize-package-bin": "^1.0.1"
+      }
+    },
+    "npm-normalize-package-bin": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+      "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
     },
     "npm-packlist": {
-      "version": "1.4.6",
-      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.6.tgz",
-      "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==",
+      "version": "1.4.7",
+      "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz",
+      "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==",
       "requires": {
         "ignore-walk": "^3.0.1",
         "npm-bundled": "^1.0.1"
@@ -4115,9 +4121,9 @@
       "dev": true
     },
     "postcss": {
-      "version": "7.0.23",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz",
-      "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==",
+      "version": "7.0.26",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.26.tgz",
+      "integrity": "sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==",
       "requires": {
         "chalk": "^2.4.2",
         "source-map": "^0.6.1",
@@ -4175,9 +4181,9 @@
       "dev": true
     },
     "psl": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz",
-      "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw=="
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
+      "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
     },
     "pstree.remy": {
       "version": "1.1.7",
@@ -4635,9 +4641,9 @@
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
     "sanitize-html": {
-      "version": "1.20.1",
-      "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz",
-      "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==",
+      "version": "1.21.1",
+      "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.21.1.tgz",
+      "integrity": "sha512-W6enXSVphVaVbmVbzVngBthR5f5sMmhq3EfPfBlzBzp2WnX8Rnk7NGpP7KmHUc0Y3MVk9tv/+CbpdHchX9ai7g==",
       "requires": {
         "chalk": "^2.4.1",
         "htmlparser2": "^3.10.0",
@@ -5003,20 +5009,13 @@
       }
     },
     "sqlite3": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.0.tgz",
-      "integrity": "sha512-RvqoKxq+8pDHsJo7aXxsFR18i+dU2Wp5o12qAJOV5LNcDt+fgJsc2QKKg3sIRfXrN9ZjzY1T7SNe/DFVqAXjaw==",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz",
+      "integrity": "sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg==",
       "requires": {
         "nan": "^2.12.1",
         "node-pre-gyp": "^0.11.0",
         "request": "^2.87.0"
-      },
-      "dependencies": {
-        "nan": {
-          "version": "2.14.0",
-          "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
-          "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
-        }
       }
     },
     "srcset": {
@@ -5125,7 +5124,7 @@
     },
     "strip-eof": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
       "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
       "dev": true
     },
@@ -5593,9 +5592,9 @@
       "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
     },
     "uuid": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
-      "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
     },
     "v8flags": {
       "version": "3.1.1",
diff --git a/server/package.json b/server/package.json
index 6dc2d7fd..02cf8678 100644
--- a/server/package.json
+++ b/server/package.json
@@ -14,9 +14,9 @@
     "node-cron": "^2.0.3",
     "nodemailer": "^5.1.1",
     "pug": "^2.0.4",
-    "sanitize-html": "^1.20.1",
+    "sanitize-html": "^1.21.1",
     "serve-favicon": "~2.5.0",
-    "sqlite3": "^4.1.0",
+    "sqlite3": "^4.1.1",
     "ws": "^6.2.1"
   },
   "devDependencies": {
diff --git a/server/sockets.js b/server/sockets.js
index 3637c8f4..c2fd552b 100644
--- a/server/sockets.js
+++ b/server/sockets.js
@@ -21,13 +21,16 @@ module.exports = function(wss) {
     if (!!clients[sid])
       return socket.send(JSON.stringify({code:"duplicate"}));
     clients[sid] = {sock: socket, page: query["page"]};
-    const notifyRoom = (page,code) => {
+    const notifyRoom = (page,code,obj) => {
       Object.keys(clients).forEach(k => {
         if (k != sid && clients[k].page == page)
-          clients[k].sock.send(JSON.stringify({code:code,sid:sid}));
+        {
+          clients[k].sock.send(JSON.stringify(Object.assign(
+            {code:code}, obj)));
+        }
       });
     };
-    notifyRoom(query["page"],"connect");
+    notifyRoom(query["page"],"connect",{sid:sid});
     socket.on("message", objtxt => {
       let obj = JSON.parse(objtxt);
       if (!!obj.target && !clients[obj.target])
@@ -41,9 +44,9 @@ module.exports = function(wss) {
               k != sid && clients[k].page == curPage)}));
           break;
         case "pagechange":
-          notifyRoom(clients[sid].page, "disconnect");
+          notifyRoom(clients[sid].page, "disconnect", {sid:sid});
           clients[sid].page = obj.page;
-          notifyRoom(obj.page, "connect");
+          notifyRoom(obj.page, "connect", {sid:sid});
           break;
         case "askidentity":
           clients[obj.target].sock.send(JSON.stringify(
@@ -82,8 +85,8 @@ module.exports = function(wss) {
             {code:"game", game:obj.game, from:sid}));
           break;
         case "newchat":
-          clients[obj.target].sock.send(JSON.stringify(
-            {code:"newchat",msg:obj.msg}));
+          notifyRoom(query["page"], "newchat",
+            {msg:obj.msg, name:obj.name, sid:sid})
           break;
         // TODO: WebRTC instead in this case (most demanding?)
         case "newmove":