Draft or user bio support (ready in Hall, TODO in Game)
authorBenjamin Auder <benjamin.auder@somewhere>
Fri, 1 May 2020 18:31:02 +0000 (20:31 +0200)
committerBenjamin Auder <benjamin.auder@somewhere>
Fri, 1 May 2020 18:31:02 +0000 (20:31 +0200)
TODO
client/src/components/UserBio.vue [new file with mode: 0644]
client/src/store.js
client/src/translations/en.js
client/src/translations/es.js
client/src/translations/fr.js
client/src/variants/Rampage.js [new file with mode: 0644]
client/src/views/Hall.vue
server/db/create.sql
server/models/User.js
server/routes/users.js

diff --git a/TODO b/TODO
index cd69599..c2981ce 100644 (file)
--- a/TODO
+++ b/TODO
@@ -7,10 +7,10 @@ https://www.chessvariants.com/winning.dir/kinglet.html
 you need to win all the pawns to win the game
 kings are not royal
 
-https://www.chessvariants.com/diffmove.dir/balaklava.html
-
 Rampage Chess: Any Unit May move to any empty square that is guarded. A King cannot make a special move to get out of check.
 
+https://www.chessvariants.com/rules/pacifist-chess (1 et 2, utilisant somme values ou nb pièces)
+
 Swap chess: Friendly pieces may swap places
 
 https://www.chessvariants.com/crossover.dir/koopachess.html
diff --git a/client/src/components/UserBio.vue b/client/src/components/UserBio.vue
new file mode 100644 (file)
index 0000000..c954f3d
--- /dev/null
@@ -0,0 +1,90 @@
+<template lang="pug">
+div
+  input#modalBio.modal(type="checkbox")
+  div#bioDiv(
+    role="dialog"
+    data-checkbox="modalBio"
+  )
+    .card
+      div(v-if="st.user.id == id")
+        h3.section(@click="modeEdit = !modeEdit") Click to edit
+        textarea(
+          v-if="userBio !== undefined"
+          v-show="modeEdit"
+          v-model="userBio"
+        )
+        button#submitBtn(@click="sendBio()") Submit
+      div(
+        v-if="userBio !== undefined"
+        v-html="userBio"
+        @click="modeEdit = !modeEdit"
+      )
+      #dialog.text-center {{ st.tr[infoMsg] }}
+  span.clickable(@click="showBio()") {{ name }}
+</template>
+
+<script>
+import { store } from "@/store";
+import { ajax } from "@/utils/ajax";
+import { processModalClick } from "@/utils/modalClick.js";
+export default {
+  name: "my-user-bio",
+  props: ["id", "name"],
+  data: function() {
+    return {
+      st: store.state,
+      userBio: undefined,
+      infoMsg: "",
+      modeEdit: false
+    };
+  },
+  mounted: function() {
+    document.getElementById("bioDiv")
+      .addEventListener("click", processModalClick);
+  },
+  methods: {
+    showBio: function() {
+      this.infoMsg = "";
+      document.getElementById("modalBio").checked = true;
+      if (this.userBio === undefined) {
+        ajax(
+          "/userbio",
+          "GET",
+          {
+            data: { id: this.id },
+            success: (res) => {
+              this.userBio = res.bio;
+            }
+          }
+        );
+      }
+    },
+    sendBio: function() {
+      ajax(
+        "/userbio",
+        "PUT",
+        {
+          data: { bio: this.userBio },
+          success: () => {
+            this.infoMsg = this.st.tr["Modifications applied!"];
+          }
+        }
+      );
+    }
+  }
+};
+</script>
+
+<style lang="sass" scoped>
+[type="checkbox"].modal+div .card
+  max-width: 768px
+  max-height: 100%
+
+#submitBtn
+  width: 50%
+  margin: 0 auto
+
+#dialog
+  padding: 5px
+  color: blue
+</style>
index 33d0312..fa363a3 100644 (file)
@@ -46,7 +46,6 @@ export const store = {
       name: localStorage.getItem("myname") || "", //"" for "anonymous"
       email: "", //unknown yet
       notify: false, //email notifications
-      newsRead: localStorage.getItem("newsRead") || 0,
       sid: mysid
     };
     // Slow verification through the server:
@@ -79,8 +78,6 @@ export const store = {
         localStorage.removeItem("myname");
       this.state.user.email = json.email;
       this.state.user.notify = json.notify;
-      if (!!json.newsRead && json.newsRead > this.state.user.newsRead)
-        this.state.user.newsRead = json.newsRead;
     });
     // Settings initialized with values from localStorage
     const getItemDefaultTrue = (item) => {
index 91667a7..a72cf61 100644 (file)
@@ -154,7 +154,6 @@ export const translations = {
   "Who's there?": "Who's there?",
   With: "With",
   with: "with",
-  "Write news": "Write news",
   "Wrong time control": "Wrong time control",
   "Your message": "Your message",
 
index daf09ae..10e5422 100644 (file)
@@ -154,7 +154,6 @@ export const translations = {
   "Who's there?": "¿Quién está ahí?",
   With: "Con",
   with: "con",
-  "Write news": "Escribir una news",
   "Wrong time control": "Cadencia errónea",
   "Your message": "Tu mensaje",
 
index 6b57c32..c4de144 100644 (file)
@@ -154,7 +154,6 @@ export const translations = {
   "Who's there?": "Qui est là ?",
   With: "Avec",
   with: "avec",
-  "Write news": "Écrire une news",
   "Wrong time control": "Cadence erronée",
   "Your message": "Votre message",
 
diff --git a/client/src/variants/Rampage.js b/client/src/variants/Rampage.js
new file mode 100644 (file)
index 0000000..31d4f35
--- /dev/null
@@ -0,0 +1,10 @@
+import { ChessRules } from "@/base_rules";
+
+// Plan : garder intact isAttacked,
+// ajouter "guarded" qui somme les attaques blanches et soustrait les attaques noires sur une case
+// (regarder toutes directions, OK)
+// --> boulot à faire sur chaque case vide, après chaque getPotentialMoves()
+// --> sauf si le roi est en échec (avant de jouer).
+
+export class RampageRules extends ChessRules {
+};
index 0e2c1c6..48af22e 100644 (file)
@@ -113,7 +113,7 @@ main
             v-for="sid in Object.keys(people)"
             v-if="!!people[sid].name"
           )
-            span {{ people[sid].name }}
+            UserBio(:id="people[sid].id" :name="people[sid].name")
             button.player-action(
               v-if="isGamer(sid)"
               @click="watchGame(sid)"
@@ -212,6 +212,7 @@ import params from "@/parameters";
 import { getRandString, shuffle, randInt } from "@/utils/alea";
 import { getDiagram } from "@/utils/printDiagram";
 import Chat from "@/components/Chat.vue";
+import UserBio from "@/components/UserBio.vue";
 import GameList from "@/components/GameList.vue";
 import ChallengeList from "@/components/ChallengeList.vue";
 import { GameStorage } from "@/utils/gameStorage";
@@ -220,6 +221,7 @@ export default {
   name: "my-hall",
   components: {
     Chat,
+    UserBio,
     GameList,
     ChallengeList
   },
index 84b72b9..15417b1 100644 (file)
@@ -16,7 +16,7 @@ create table Users (
   sessionToken varchar,
   created datetime,
   notify boolean,
-  newsRead datetime
+  bio text default ''
 );
 
 create table Problems (
index edb3c06..8a97d57 100644 (file)
@@ -13,7 +13,7 @@ const sendEmail = require('../utils/mailer');
  *   sessionToken: token in cookies for authentication
  *   notify: boolean (send email notifications for corr games)
  *   created: datetime
- *   newsRead: datetime
+ *   bio: text
  */
 
 const UserModel = {
@@ -58,6 +58,16 @@ const UserModel = {
     });
   },
 
+  getBio: function(id) {
+    db.serialize(function() {
+      const query =
+        "SELECT bio " +
+        "FROM Users " +
+        "WHERE id = " + id;
+      db.get(query, cb);
+    });
+  },
+
   /////////
   // MODIFY
 
@@ -71,13 +81,13 @@ const UserModel = {
     });
   },
 
-  setNewsRead: function(id) {
+  setBio: function(id, bio) {
     db.serialize(function() {
       const query =
         "UPDATE Users " +
-        "SET newsRead = " + Date.now() + " " +
+        "SET bio = ? " +
         "WHERE id = " + id;
-      db.run(query);
+      db.run(query, bio);
     });
   },
 
index 7898ac8..a9adc16 100644 (file)
@@ -4,6 +4,22 @@ const sendEmail = require('../utils/mailer');
 const genToken = require("../utils/tokenGenerator");
 const access = require("../utils/access");
 const params = require("../config/parameters");
+const sanitizeHtml = require('sanitize-html');
+
+router.get("/userbio", access.ajax, (req,res) => {
+  const uid = req.query["id"];
+  if (!!(uid.toString().match(/^[0-9]+$/))) {
+    UserModel.getBio(uid, (err, bio) => {
+      res.json(bio);
+    });
+  }
+});
+
+router.put('/userbio', access.logged, access.ajax, (req,res) => {
+  const bio = sanitizeHtml(req.body.bio);
+  UserModel.setBio(req.userId, bio);
+  res.json({});
+});
 
 router.post('/register', access.unlogged, access.ajax, (req,res) => {
   const name = req.body.name;
@@ -36,16 +52,14 @@ router.get("/whoami", access.ajax, (req,res) => {
       name: user.name,
       email: user.email,
       id: user.id,
-      notify: user.notify,
-      newsRead: user.newsRead
+      notify: user.notify
     });
   };
   const anonymous = {
     name: "",
     email: "",
     id: 0,
-    notify: false,
-    newsRead: 0
+    notify: false
   };
   if (!req.cookies.token) callback(anonymous);
   else if (req.cookies.token.match(/^[a-z0-9]+$/)) {
@@ -60,8 +74,8 @@ router.get("/users", access.ajax, (req,res) => {
   const ids = req.query["ids"];
   // NOTE: slightly too permissive RegExp
   if (ids.match(/^([0-9]+,?)+$/)) {
-    UserModel.getByIds(ids, (err,users) => {
-      res.json({users:users});
+    UserModel.getByIds(ids, (err, users) => {
+      res.json({ users:users });
     });
   }
 });
@@ -81,12 +95,6 @@ router.put('/update', access.logged, access.ajax, (req,res) => {
   }
 });
 
-// Special route to update newsRead timestamp:
-router.put('/newsread', access.logged, access.ajax, (req,res) => {
-  UserModel.setNewsRead(req.userId);
-  res.json({});
-});
-
 // Authentication-related methods:
 
 // to: object user (to who we send an email)