Add Knightrelay1. Some fixes. Move odd 'isAttackedBy_multiple_colors' to Checkered...
[vchess.git] / client / src / views / Hall.vue
index f4fc172..eb8464a 100644 (file)
@@ -185,12 +185,17 @@ main
           :showBoth="true"
           @show-game="showGame"
         )
-        GameList(
-          v-show="gdisplay=='corr'"
-          :games="filterGames('corr')"
-          :showBoth="true"
-          @show-game="showGame"
-        )
+        div(v-show="gdisplay=='corr'")
+          GameList(
+            :games="filterGames('corr')"
+            :showBoth="true"
+            @show-game="showGame"
+          )
+          button#loadMoreBtn(
+            v-if="hasMore"
+            @click="loadMore()"
+          )
+            | {{ st.tr["Load more"] }}
 </template>
 
 <script>
@@ -218,6 +223,10 @@ export default {
       st: store.state,
       cdisplay: "live", //or corr
       gdisplay: "live",
+      // timestamp of last showed (oldest) corr game:
+      cursor: Number.MAX_SAFE_INTEGER,
+      // hasMore == TRUE: a priori there could be more games to load
+      hasMore: true,
       games: [],
       challenges: [],
       people: {},
@@ -321,29 +330,31 @@ export default {
     this.setDisplay('g', showGtype);
     // Ask server for current corr games (all but mines)
     ajax(
-      "/games",
+      "/observedgames",
       "GET",
       {
-        data: { uid: this.st.user.id, excluded: true },
+        data: {
+          uid: this.st.user.id,
+          cursor: this.cursor
+        },
         success: (response) => {
-          if (
-            response.games.length > 0 &&
-            this.games.length == 0 &&
-            this.gdisplay == "live"
-          ) {
-            document
-              .getElementById("btnGcorr")
-              .classList.add("somethingnew");
+          const L = response.games.length;
+          if (L > 0) {
+            this.cursor = response.games[L - 1].created;
+            if (this.games.length == 0 && this.gdisplay == "live") {
+              document
+                .getElementById("btnGcorr")
+                .classList.add("somethingnew");
+            }
           }
           this.games = this.games.concat(
             response.games.map(g => {
-              const type = this.classifyObject(g);
               const vname = this.getVname(g.vid);
               return Object.assign(
                 {},
                 g,
                 {
-                  type: type,
+                  type: "corr",
                   vname: vname
                 }
               );
@@ -594,8 +605,9 @@ export default {
             if (!s.page)
               // Peer is in Hall
               this.send("askchallenges", { target: s.sid });
-            // Peer is in Game
-            else this.send("askgame", { target: s.sid, page: page });
+            // Peer is in Game: ask only if live game
+            else if (!page.match(/\/[0-9]+$/))
+              this.send("askgame", { target: s.sid, page: page });
           });
           break;
         }
@@ -607,7 +619,9 @@ export default {
             this.people[data.from] = { pages: [{ path: page, focus: true }] };
             if (data.code == "connect")
               this.send("askchallenges", { target: data.from });
-            else this.send("askgame", { target: data.from, page: page });
+            // Ask game only if live:
+            else if (!page.match(/\/[0-9]+$/))
+              this.send("askgame", { target: data.from, page: page });
           } else {
             // Append page if not already in list
             if (!(this.people[data.from].pages.find(p => p.path == page)))
@@ -637,15 +651,14 @@ export default {
           } else {
             // Remove the matching live game if now unreachable
             const gid = data.page.match(/[a-zA-Z0-9]+$/)[0];
-            const gidx = this.games.findIndex(g => g.id == gid);
-            if (gidx >= 0) {
-              const game = this.games[gidx];
-              if (
-                game.type == "live" &&
-                game.rids.length == 1 &&
-                game.rids[0] == data.from
-              ) {
-                this.games.splice(gidx, 1);
+            // Corr games are always reachable:
+            if (!gid.match(/^[0-9]+$/)) {
+              const gidx = this.games.findIndex(g => g.id == gid);
+              // NOTE: gidx should always be >= 0 (TODO?)
+              if (gidx >= 0) {
+                const game = this.games[gidx];
+                ArrayFun.remove(game.rids, rid => rid == data.from);
+                if (game.rids.length == 0) this.games.splice(gidx, 1);
               }
             }
           }
@@ -676,7 +689,7 @@ export default {
           alert(this.st.tr["New connexion detected: tab now offline"]);
           break;
         case "askidentity": {
-          // Request for identification (TODO: anonymous shouldn't need to reply)
+          // Request for identification
           const me = {
             // Decompose to avoid revealing email
             name: this.st.user.name,
@@ -762,12 +775,12 @@ export default {
           }
           break;
         }
-        case "game": //individual request
+        case "game": // Individual request
         case "newgame": {
           const game = data.data;
           // Ignore games where I play (will go in MyGames page)
           if (game.players.every(p =>
-            p.sid != this.st.user.sid || p.uid != this.st.user.id))
+            p.sid != this.st.user.sid && p.id != this.st.user.id))
           {
             let locGame = this.games.find(g => g.id == game.id);
             if (!locGame) {
@@ -831,6 +844,36 @@ export default {
       this.conn.addEventListener("message", this.socketMessageListener);
       this.conn.addEventListener("close", this.socketCloseListener);
     },
+    loadMore: function() {
+      ajax(
+        "/observedgames",
+        "GET",
+        {
+          data: {
+            uid: this.st.user.id,
+            cursor: this.cursor
+          },
+          success: (res) => {
+            const L = res.games.length;
+            if (L > 0) {
+              this.cursor = res.games[L - 1].created;
+              let moreGames = res.games.map(g => {
+                const vname = this.getVname(g.vid);
+                return Object.assign(
+                  {},
+                  g,
+                  {
+                    type: "corr",
+                    vname: vname
+                  }
+                );
+              });
+              this.games = this.games.concat(moreGames);
+            } else this.hasMore = false;
+          }
+        }
+      );
+    },
     // Challenge lifecycle:
     addChallenge: function(chall) {
       // NOTE about next condition: see "askchallenges" case.
@@ -907,8 +950,9 @@ export default {
         else if (
           ctype == "live" &&
           Object.values(this.people).every(p => p.name != this.newchallenge.to)
-        )
+        ) {
           error = this.newchallenge.to + " " + this.st.tr["is not online"];
+        }
       }
       if (error) {
         alert(error);
@@ -974,7 +1018,6 @@ export default {
         });
         // Add new challenge:
         chall.from = {
-          // Decompose to avoid revealing email
           sid: this.st.user.sid,
           id: this.st.user.id,
           name: this.st.user.name
@@ -1008,7 +1051,7 @@ export default {
           {
             data: { chall: chall },
             success: (response) => {
-              finishAddChallenge(response.cid);
+              finishAddChallenge(response.id);
             }
           }
         );
@@ -1024,7 +1067,6 @@ export default {
     finishProcessingChallenge: function(c) {
       if (c.accepted) {
         c.seat = {
-          // Again, avoid c.seat = st.user to not reveal email
           sid: this.st.user.sid,
           id: this.st.user.id,
           name: this.st.user.name
@@ -1094,24 +1136,17 @@ export default {
     },
     // NOTE: when launching game, the challenge is already being deleted
     launchGame: function(c) {
-      let players =
+      // White player index 0, black player index 1:
+      const players =
         !!c.mycolor
           ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat])
           : shuffle([c.from, c.seat]);
-      // Convention for players IDs in stored games is 'uid'
-      players.forEach(p => {
-        let pWithUid = p;
-        pWithUid["uid"] = p.id;
-        delete pWithUid["id"];
-      });
       // These game informations will be shared
       let gameInfo = {
         id: getRandString(),
         fen: c.fen || V.GenRandInitFen(c.randomness),
-        // White player index 0, black player index 1:
-        players: c.mycolor
-          ? (c.mycolor == "w" ? [c.seat, c.from] : [c.from, c.seat])
-          : shuffle([c.from, c.seat]),
+        randomness: c.randomness, //for rematch
+        players: players,
         vid: c.vid,
         cadence: c.cadence
       };
@@ -1120,19 +1155,26 @@ export default {
         if (!!oppsid)
           // Opponent is online
           this.send("startgame", { data: gameInfo, target: oppsid });
-        // Send game info (only if live) to everyone except me and opponent
-        // TODO: this double message send could be avoided.
-        this.send("newgame", { data: gameInfo, oppsid: oppsid });
-        // Also to MyGames page:
+        // If new corr game, notify Hall (except opponent and me)
+        if (c.type == "corr") {
+          this.send(
+            "newgame",
+            {
+              data: gameInfo,
+              excluded: [this.st.user.sid, oppsid]
+            }
+          );
+        }
+        // Notify MyGames page:
         this.send(
           "notifynewgame",
           {
             data: gameInfo,
-            targets: gameInfo.players.map(p => {
-              return { sid: p.sid, uid: p.uid };
-            })
+            targets: gameInfo.players
           }
         );
+        // NOTE: no need to send the game to the room, since I'll connect
+        // on game just after, the main Hall will be notified.
       };
       if (c.type == "live") {
         notifyNewgame();
@@ -1144,11 +1186,14 @@ export default {
           "POST",
           {
             // cid is useful to delete the challenge:
-            data: { gameInfo: gameInfo, cid: c.id },
+            data: {
+              gameInfo: gameInfo,
+              cid: c.id
+            },
             success: (response) => {
-              gameInfo.id = response.gameId;
+              gameInfo.id = response.id;
               notifyNewgame();
-              this.$router.push("/game/" + response.gameId);
+              this.$router.push("/game/" + response.id);
             }
           }
         );
@@ -1175,13 +1220,12 @@ export default {
         () => {
           GameStorage.add(game, (err) => {
             // If an error occurred, game is not added: a tab already
-            // added the game and (if focused) is redirected toward it.
-            // If no error and the tab is hidden: do not show anything.
-            if (!err && !document.hidden) {
-              if (this.st.settings.sound)
-                new Audio("/sounds/newgame.flac").play().catch(() => {});
-              this.$router.push("/game/" + gameInfo.id);
-            }
+            // added the game. Maybe a focused one, maybe not.
+            // We know for sure that it emitted the gong start sound.
+            // ==> Do not play it again.
+            if (!err && this.st.settings.sound)
+              new Audio("/sounds/newgame.flac").play().catch(() => {});
+            this.$router.push("/game/" + gameInfo.id);
           });
         },
         document.hidden ? 500 + 1000 * Math.random() : 0
@@ -1293,6 +1337,10 @@ tr > td
   h4
     margin: 5px 0
 
+button#loadMoreBtn
+  display: block
+  margin: 0 auto
+
 td.remove-preset
   background-color: lightgrey
   text-align: center