Advance on users management. TODO: routes/users + debug + test
authorBenjamin Auder <benjamin.auder@somewhere>
Wed, 9 Jan 2019 00:04:07 +0000 (01:04 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Wed, 9 Jan 2019 00:04:07 +0000 (01:04 +0100)
16 files changed:
app.js
public/javascripts/components/upsertUser.js [new file with mode: 0644]
public/javascripts/index.js
public/javascripts/utils/ajax.js
public/javascripts/variant.js
public/stylesheets/_users.sass [new file with mode: 0644]
public/stylesheets/index.sass
public/stylesheets/layout.sass
sockets.js
views/index.pug
views/layout.pug
views/login_register.pug [deleted file]
views/logout_update.pug [deleted file]
views/modalLang.pug [new file with mode: 0644]
views/settings.pug
views/variant.pug

diff --git a/app.js b/app.js
index 3199af3..22c220b 100644 (file)
--- a/app.js
+++ b/app.js
@@ -46,6 +46,7 @@ app.use(express.static(path.join(__dirname, 'public')));
 // Before showing any page, check + save credentials
 app.use(function(req, res, next) {
        req.loggedIn = false;
+       res.locals.user = { name: "" };
        if (!req.cookies.token)
                return next();
        UserModel.getOne("sessionToken", req.cookies.token, function(err, user) {
diff --git a/public/javascripts/components/upsertUser.js b/public/javascripts/components/upsertUser.js
new file mode 100644 (file)
index 0000000..29444b4
--- /dev/null
@@ -0,0 +1,120 @@
+// Logic to login, or create / update a user (and also logout)
+Vue.component('my-upsert-user', {
+       props: ["initUser"], //to find the game in storage (assumption: it exists)
+       data: function() {
+               return {
+                       user: initUser, //initialized with prop value
+                       stage: (!initUser.email ? "Login" : "Update"),
+                       infoMsg: "",
+               };
+       },
+       template: `
+               <div>
+                       <input id="modalUser" class="modal" type="checkbox"/>
+                       <div role="dialog">
+                               <div class="card">
+                                       <label class="modal-close" for="modalUser">
+                                       <h3>{{ stage }}</h3>
+                                       <form id="userForm" @submit.prevent="submit">
+                                               <fieldset>
+                                                       <label for="useremail">Email</label>
+                                                       <input id="useremail" type="email" v-model="user.email"/>
+                                               <fieldset>
+                                                       <label for="username">Name</label>
+                                                       <input id="username" type="text" v-model="user.name"/>
+                                               </fieldset>
+                                               <fieldset>
+                                                       <label for="notifyNew">Notify new moves &amp; games</label>
+                                                       <input id="notifyNew" type="checkbox" v-model="user.notify"/>
+                                               <button id="submit" @click.prevent="submit">
+                                                       <span>{{ submitMessage }}</span>
+                                                       <i class="material-icons">send</i>
+                               <p v-if="stage!='Update'">
+                                       <button @click.prevent="toggleStage()">
+                                               <span>{{ stage=="Login" ? "Register" : "Login" }}</span>
+                                       </button>
+                                       <button>Logout</button>
+                               </p>
+                               <div id="dialog" :style="{display: displayInfo}">{{ infoMsg }}</div>
+                       </div>
+               </div>
+       `,
+       computed: {
+               submitMessage: function() {
+                       switch (this.stage)
+                       {
+                               case "Login":
+                                       return "Go";
+                               case "Register":
+                                       return "Send";
+                               case "Update":
+                                       return "Apply";
+                       }
+               },
+               displayInfo: function() {
+                       return (this.infoMsg.length > 0 ? "block" : "none");
+               },
+       },
+       methods: {
+               toggleStage: function() {
+                       this.stage = (this.stage == "Login" ? "Register" : "Login");
+               },
+               ajaxUrl: function() {
+                       switch (this.stage)
+                       {
+                               case "Login":
+                                       return "/sendtoken";
+                               case "Register":
+                                       return "/register";
+                               case "Update":
+                                       return "/update";
+                       }
+               },
+               ajaxMethod: function() {
+                       switch (this.stage)
+                       {
+                               case "Login":
+                                       return "GET";
+                               case "Register":
+                                       return "POST";
+                               case "Update":
+                                       return "PUT";
+                       }
+               },
+               infoMessage: function() {
+                       switch (this.stage)
+                       {
+                               case "Login":
+                                       return "Connection token sent. Check your emails!";
+                               case "Register":
+                                       return "Registration complete! Please check your emails.";
+                               case "Update":
+                                       return "Modifications applied!";
+                       }
+               },
+               submit: function() {
+                       // TODO: re-activate simple measures like this: (using time of click on modal)
+//                     const exitTime = new Date();
+//                     if (this.stage=="Register" && exitTime.getTime() - enterTime.getTime() < 5000)
+//                             return;
+                       if (!this.user.name.match(/[a-z0-9_]+/i))
+                               return alert("User name: only alphanumerics and underscore");
+                       this.infoMsg = "Processing... Please wait";
+                       ajax(this.ajaxUrl(), this.ajaxMethod(),
+                               this.stage == "Login" ? "PUT" : "POST", this.user,
+                               res => {
+                                       this.infoMsg = this.infoMessage();
+                                       if (this.stage != "Update")
+                                       {
+                                               this.user["email"] = "";
+                                               this.user["name"] = "";
+                                       }
+                                       setTimeout(() => {
+                                               this.infoMsg = "";
+                                               document.getElementById("modalUser").checked = false;
+                                       }, 2000);
+                               }
+                       );
+               },
+       }
+});
index 6859e8b..f59bc04 100644 (file)
@@ -1,6 +1,6 @@
 // Javascript for index page: mostly counters updating
 new Vue({
-       el: "#indexPage",
+       el: "#VueElement",
        data: {
                counts: {},
                curPrefix: "",
index ab288bc..a354690 100644 (file)
@@ -3,7 +3,7 @@ function toQueryString(data)
 {
        let data_str = "";
        Object.keys(data).forEach(k => {
-               data_str += k + "=" + data[k] + "&";
+               data_str += k + "=" + encodeURIComponent(data[k]) + "&";
        });
        return data_str.slice(0, -1); //remove last "&"
 }
index eac1ec0..1ec4d89 100644 (file)
@@ -1,5 +1,5 @@
 new Vue({
-       el: "#variantPage",
+       el: "#VueElement",
        data: {
                display: "room", //default: main hall
                gameid: "undefined", //...yet
diff --git a/public/stylesheets/_users.sass b/public/stylesheets/_users.sass
new file mode 100644 (file)
index 0000000..69bc438
--- /dev/null
@@ -0,0 +1,9 @@
+button#submit
+  display: inline-flex
+  //i
+    line-height: 36px
+  span
+    margin-right: 7px
+
+#dialog
+       clear: both
index e38f87d..d25ecb4 100644 (file)
@@ -11,7 +11,7 @@
   padding: 0
   box-sizing: border-box
   p
-    display: inline-block
+    display: flex
     padding: 3px
     border: 1px solid black;
     margin: 25px 15px 5px 7px
       margin-top: 10px
       font-size: 1em
 
-#settingsMenu, #introductionMenu
+#introductionMenu, #userMenu
   float: right
   @media screen and (max-width: 767px)
     .info-container
       p
         margin-right: 5px
 
+#flagMenu
+  float: right
+  margin-right: 10px
+  @media screen and (max-width: 767px)
+    margin-right: 5px
+  img
+    display: inline-block
+    height: 30px
+    padding-top: 27px
+    @media screen and (max-width: 767px)
+      padding-top: 8px
+
 // TODO: box-shadow or box-sizing ? https://stackoverflow.com/a/13517809
 .variant
   box-sizing: border-box
index dedab08..eae4542 100644 (file)
@@ -1,3 +1,5 @@
+@import users
+
 html, *
   font-family: "Open Sans", Arial, sans-serif
   --back-color: #f2f2f2
index e411050..60434b5 100644 (file)
@@ -13,6 +13,9 @@ function getJsonFromUrl(url) {
        return result;
 }
 
+// TODO: empêcher multi-log du même user (envoyer le user ID + secret en même temps que name et...)
+// --> si secret ne matche pas celui trouvé en DB, stop
+
 module.exports = function(wss) {
        db.serialize(function() {
                db.all("SELECT * FROM Variants", (err,variants) => {
index dd09593..5dfa903 100644 (file)
@@ -4,7 +4,7 @@ block css
        link(rel="stylesheet", href="/stylesheets/index.css")
 
 block content
-       .container#indexPage
+       .container
                case lang
                        when "en"
                                include welcome/en
@@ -19,10 +19,20 @@ block content
                                        .info-container
                                                p vchess.club
                                        img(src="/images/index/wildebeest.svg")
-                               #settingsMenu.clickable(
-                                               onClick="document.getElementById('modalSettings').checked=true")
+                               #flagMenu.clickable(
+                                               onClick="document.getElementById('modalLang').checked=true")
+                                       img(src="/images/flags/" + lang + ".svg")
+                               #userMenu.clickable(
+                                               onClick="document.getElementById('modalUser').checked=true")
                                        .info-container
-                                               p Settings
+                                               if !user.email
+                                                       p
+                                                               span Login
+                                                               i.material-icons person
+                                               else
+                                                       p
+                                                               span Update
+                                                               i.material-icons person
                                #introductionMenu.clickable(
                                                onClick="document.getElementById('modalWelcome').checked=true")
                                        .info-container
index 60b7adc..03249be 100644 (file)
@@ -33,13 +33,10 @@ html
                                include translations/es
                        when "fr"
                                include translations/fr
-               if !user
-                       include login_register
-               else
-                       include logout_update
                include contactForm
-               include settings
-               main
+               include modalLang
+               main#VueElement
+                       my-upsert-user(:user="user" :stage="stage")
                        block content
                footer.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2.text-center
                        div
@@ -56,6 +53,7 @@ html
                        script(src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js")
                else
                        script(src="https://cdn.jsdelivr.net/npm/vue")
+               script(src="/javascripts/components/upsertUser.js")
                script.
                        const translations = !{JSON.stringify(translations)};
                        const user = !{JSON.stringify(user)};
diff --git a/views/login_register.pug b/views/login_register.pug
deleted file mode 100644 (file)
index c831c8a..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-extends layout
-
-block css
-       link(rel="stylesheet", href="/stylesheets/login.css")
-
-block content
-       .mui-container
-               .row
-                       .mui-col.xs-12.mui-col-sm-8.mui-col-sm-offset-2.mui-col-md-6.mui-col-md-offset-3.mui-col-lg-4.mui-col-lg-offset-4.mui--z1.white.pad-updown.pad-sides
-                               form#loginForm(@submit.prevent="submit")
-                                       .mui-textfield.mui-textfield--float-label
-                                               input#email(type="email" ref="userEmail" v-model="user.email")
-                                               label#labEmail(for="email") Email
-                                       .mui-textfield.mui-textfield--float-label(v-show="stage == 'Register'")
-                                               input#name(type="text" v-model="user.name")
-                                               label#labName(for="name") Name
-                                       .mui--pull-left.space-bottom.space-top
-                                               button#submit.mui-btn.mui-btn--primary(@click.prevent="submit")
-                                                       span {{ stage=="Login" ? "Go" : "Send" }}
-                                                       i.material-icons.right send
-                                       .mui--pull-right.space-bottom.space-top
-                                               p
-                                                       button.mui-btn.mui-btn--accent(@click.prevent="toggleStage()")
-                                                               span {{ stage=="Login" ? "Register" : "Login" }}
-                                       #dialog.mui--hide.space-top
-
-block javascripts
-       script(src="//cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js")
-       script(src="/javascripts/utils/dialog.js")
-       script(src="/javascripts/utils/validation.js")
-       script(src="/javascripts/login.js")
diff --git a/views/logout_update.pug b/views/logout_update.pug
deleted file mode 100644 (file)
index 1b84483..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-extends layout
-
-block css
-       link(rel="stylesheet", href="/stylesheets/settings.css")
-
-block content
-       .mui-container-fluid
-               .mui-row
-                       .mui-col-xs-12.mui-col-sm-10.mui-col-sm-offset-1.mui-col-md-8.mui-col-md-offset-2.mui-col-lg-6.mui-col-lg-offset-3.mui--z1.white.pad-updown.pad-sides
-                               form#settingsForm(@submit.prevent="submit")
-                                       .mui-textfield.mui-textfield--float-label
-                                               input#email(type="email" ref="userEmail" v-model="user.email")
-                                               label#labEmail.active(for="email") Email
-                                       .mui-textfield.mui-textfield--float-label
-                                               input#name(type="text" v-model="user.name")
-                                               label#labName.active(for="name") Name
-                                       p
-                                               span Theme &nbsp;&nbsp;&nbsp;&nbsp;
-                                               button(v-for="theme in themes" class="theme-btn mui-btn grey"
-                                                               :class="themeClass(theme)" @click.prevent="toggleTheme(theme)")
-                                                       | {{ theme }}
-                                       .mui-radio
-                                               input#notify(type="checkbox" v-model="user.notify")
-                                               label(for="notify") Notify new moves &amp; games
-                                       button#submit.mui-btn.mui-btn--primary(@click.prevent="submit")
-                                               span Apply
-                                               i.material-icons.right send
-                                       #dialog.mui--hide.space-top
-
-block javascripts
-       script(src="//cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js")
-       script(src="/javascripts/utils/dialog.js")
-       script(src="/javascripts/utils/validation.js")
-       script.
-               var user = !{JSON.stringify(user)};
-       script(src="/javascripts/settings.js")
diff --git a/views/modalLang.pug b/views/modalLang.pug
new file mode 100644 (file)
index 0000000..b631182
--- /dev/null
@@ -0,0 +1,11 @@
+input#modalLang.modal(type="checkbox")
+div(role="dialog")
+       #language.card
+               label.modal-close(for="modalLang")
+               form
+                       fieldset
+                               label(for="langSelect")= translations["Language"]
+                               select#langSelect
+                                       each language,langCode in langName
+                                               option(value=langCode selected=(lang==langCode))
+                                                       =language
index 063e61a..8bf7de5 100644 (file)
@@ -3,18 +3,6 @@ div(role="dialog" aria-labelledby="settingsTitle")
        .card.smallpad(onChange="blabla(event)")
                label.modal-close(for="modalSettings")
                h3#settingsTitle.section= translations["Preferences"]
-               fieldset
-                       label(for="langSelect")= translations["Language"]
-                       // image avec drapeau + select language ici
-                       select#langSelect
-                               each language,langCode in langName
-                                       option(value=langCode selected=(lang==langCode))
-                                               =language
-               fieldset
-                       label(for="nameSetter")
-                               =translations["My name is..."]
-                       input#nameSetter(type="text" value=this.myname)
-               // theme sombre / clair
                // taille echiquier : TODO
                fieldset
                        label(for="setHints")= translations["Show hints?"]
index 9419998..5bfcba1 100644 (file)
@@ -4,7 +4,8 @@ block css
        link(rel="stylesheet" href="/stylesheets/variant.css")
 
 block content
-       .container#variantPage
+       include settings
+       .container
                .row
                        .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
                                label.drawer-toggle(for="drawer-control")
@@ -24,6 +25,14 @@ block content
                                        #settings.clickable(
                                                        onClick="document.getElementById('modalSettings').checked=true")
                                                i.material-icons settings
+                                       #userMenu.clickable(
+                                                       onClick="document.getElementById('modalUser').checked=true")
+                                               .info-container
+                                                       i.material-icons person
+                                                       if !user.email
+                                                               p Login
+                                                       else
+                                                               p Update
                .row
                        my-room(v-show="display=='room'")
                        my-game-list(v-show="display=='gameList'")