From: Benjamin Auder <benjamin.auder@somewhere> Date: Thu, 19 Mar 2020 13:24:01 +0000 (+0100) Subject: loadMore button for my local games as well X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/images/assets/doc/img/config.php?a=commitdiff_plain;h=934f7f70431e9892b3ea48ba199356b4f24eaf1b;p=vchess.git loadMore button for my local games as well --- diff --git a/client/src/App.vue b/client/src/App.vue index d8ae21d3..c71ebfc4 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -279,10 +279,19 @@ footer padding: 5px 0 .menuitem.somenews + animation: blinkNews 1s infinite; color: red &:link, &:visited, &:hover color: red +@keyframes blinkNews + 0%, 49% + background-color: yellow + padding: 3px + 50%, 100% + background-color: grey + padding: 3px + // Styles for diagrams and board (partial). // TODO: where to put that ? diff --git a/client/src/utils/compgameStorage.js b/client/src/utils/compgameStorage.js index 05a20749..a5c58af1 100644 --- a/client/src/utils/compgameStorage.js +++ b/client/src/utils/compgameStorage.js @@ -16,28 +16,28 @@ function dbOperation(callback) { DBOpenRequest.onerror = function(event) { alert(store.state.tr["Database error: stop private browsing, or update your browser"]); - callback("error",null); + callback("error", null); }; DBOpenRequest.onsuccess = function(event) { db = DBOpenRequest.result; - callback(null,db); + callback(null, db); db.close(); }; DBOpenRequest.onupgradeneeded = function(event) { let db = event.target.result; - let objectStore = db.createObjectStore("compgames", { keyPath: "vname" }); + db.createObjectStore("compgames", { keyPath: "vname" }); }; } export const CompgameStorage = { add: function(game) { dbOperation((err,db) => { - if (err) - return; - let transaction = db.transaction("compgames", "readwrite"); - let objectStore = transaction.objectStore("compgames"); + if (err) return; + let objectStore = db + .transaction("compgames", "readwrite") + .objectStore("compgames"); objectStore.add(game); }); }, @@ -66,7 +66,9 @@ export const CompgameStorage = { // NOTE: need callback because result is obtained asynchronously get: function(gameId, callback) { dbOperation((err,db) => { - let objectStore = db.transaction("compgames").objectStore("compgames"); + let objectStore = db + .transaction("compgames", "readonly") + .objectStore("compgames"); objectStore.get(gameId).onsuccess = function(event) { callback(event.target.result); }; @@ -77,8 +79,9 @@ export const CompgameStorage = { remove: function(gameId) { dbOperation((err,db) => { if (!err) { - let transaction = db.transaction(["compgames"], "readwrite"); - transaction.objectStore("compgames").delete(gameId); + db.transaction("compgames", "readwrite") + .objectStore("compgames") + .delete(gameId); } }); } diff --git a/client/src/utils/gameStorage.js b/client/src/utils/gameStorage.js index 12986679..fc179f19 100644 --- a/client/src/utils/gameStorage.js +++ b/client/src/utils/gameStorage.js @@ -23,19 +23,22 @@ function dbOperation(callback) { DBOpenRequest.onerror = function(event) { alert(store.state.tr["Database error: stop private browsing, or update your browser"]); - callback("error",null); + callback("error", null); }; DBOpenRequest.onsuccess = function() { db = DBOpenRequest.result; - callback(null,db); + callback(null, db); db.close(); }; DBOpenRequest.onupgradeneeded = function(event) { let db = event.target.result; let objectStore = db.createObjectStore("games", { keyPath: "id" }); - objectStore.createIndex("score", "score"); //to search by game result + // To sarch games by score (useful for running games) + objectStore.createIndex("score", "score", { unique: false }); + // To search by date intervals. Two games cannot start at the same time + objectStore.createIndex("created", "created", { unique: true }); }; } @@ -49,13 +52,14 @@ export const GameStorage = { } let transaction = db.transaction("games", "readwrite"); transaction.oncomplete = function() { - callback(); //everything's fine + // Everything's fine + callback(); }; transaction.onerror = function(err) { - callback(err); //duplicate key error (most likely) + // Duplicate key error (most likely) + callback(err); }; - let objectStore = transaction.objectStore("games"); - objectStore.add(game); + transaction.objectStore("games").add(game); }); }, @@ -82,15 +86,20 @@ export const GameStorage = { }); }, - // Retrieve all local games (running, completed, imported...) - getAll: function(callback) { + // Retrieve (all) running local games + getRunning: function(callback) { dbOperation((err,db) => { - let objectStore = db.transaction("games").objectStore("games"); + let objectStore = db + .transaction("games", "readonly") + .objectStore("games"); + let index = objectStore.index("score"); + const range = IDBKeyRange.only("*"); let games = []; - objectStore.openCursor().onsuccess = function(event) { + index.openCursor(range).onsuccess = function(event) { let cursor = event.target.result; - // if there is still another cursor to go, keep running this code - if (cursor) { + if (!cursor) callback(games); + else { + // If there is still another cursor to go, keep running this code let g = cursor.value; // Do not retrieve moves or clocks (unused in list mode) g.movesCount = g.moves.length; @@ -99,7 +108,41 @@ export const GameStorage = { delete g.initime; games.push(g); cursor.continue(); - } else callback(games); + } + }; + }); + }, + + // Retrieve completed local games + getNext: function(upperDt, callback) { + dbOperation((err,db) => { + let objectStore = db + .transaction("games", "readonly") + .objectStore("games"); + let index = objectStore.index("created"); + const range = IDBKeyRange.upperBound(upperDt); + let games = []; + index.openCursor(range).onsuccess = function(event) { + let cursor = event.target.result; + if (!cursor) { + // Most recent games first: + games = games.sort((g1, g2) => g2.created - g1.created); + // TODO: 20 games showed per request is arbitrary + callback(games.slice(0, 20)); + } + else { + // If there is still another cursor to go, keep running this code + let g = cursor.value; + if (g.score != "*") { + // Do not retrieve moves or clocks (unused in list mode) + g.movesCount = g.moves.length; + delete g.moves; + delete g.clocks; + delete g.initime; + games.push(g); + } + cursor.continue(); + } }; }); }, @@ -121,7 +164,7 @@ export const GameStorage = { remove: function(gameId, callback) { dbOperation((err,db) => { if (!err) { - let transaction = db.transaction(["games"], "readwrite"); + let transaction = db.transaction("games", "readwrite"); transaction.oncomplete = function() { callback(); //everything's fine }; diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 68c8c94d..1a86d9b8 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -445,6 +445,8 @@ export default { const data = JSON.parse(msg.data); switch (data.code) { case "pollclients": + // TODO: shuffling and random filtering on server, if + // the room is really crowded. data.sockIds.forEach(sid => { if (sid != this.st.user.sid) { this.people[sid] = { focus: true }; diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index d11da8b1..47a86975 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -585,6 +585,8 @@ export default { // Since people can be both in Hall and Game, // need to track "askIdentity" requests: let identityAsked = {}; + // TODO: shuffling and random filtering on server, if + // the room is really crowded. data.sockIds.forEach(s => { const page = s.page || "/"; if (s.sid != this.st.user.sid && !identityAsked[s.sid]) { diff --git a/client/src/views/MyGames.vue b/client/src/views/MyGames.vue index a9e69f65..60783941 100644 --- a/client/src/views/MyGames.vue +++ b/client/src/views/MyGames.vue @@ -14,18 +14,18 @@ main @show-game="showGame" @abortgame="abortGame" ) - div(v-show="display=='corr'") - GameList( - ref="corrgames" - :games="corrGames" - @show-game="showGame" - @abortgame="abortGame" - ) - button#loadMoreBtn( - v-if="hasMore" - @click="loadMore()" - ) - | {{ st.tr["Load more"] }} + GameList( + v-show="display=='corr'" + ref="corrgames" + :games="corrGames" + @show-game="showGame" + @abortgame="abortGame" + ) + button#loadMoreBtn( + v-show="hasMore[display]" + @click="loadMore(display)" + ) + | {{ st.tr["Load more"] }} </template> <script> @@ -47,10 +47,13 @@ export default { display: "live", liveGames: [], corrGames: [], - // timestamp of last showed (oldest) corr game: - cursor: Number.MAX_SAFE_INTEGER, + // timestamp of last showed (oldest) game: + cursor: { + live: Number.MAX_SAFE_INTEGER, + corr: Number.MAX_SAFE_INTEGER + }, // hasMore == TRUE: a priori there could be more games to load - hasMore: true, + hasMore: { live: true, corr: true }, conn: null, connexionString: "" }; @@ -93,7 +96,7 @@ export default { } this.setDisplay(showType); }; - GameStorage.getAll(localGames => { + GameStorage.getRunning(localGames => { localGames.forEach(g => g.type = "live"); this.decorate(localGames); this.liveGames = localGames; @@ -113,29 +116,19 @@ export default { }); this.decorate(this.corrGames); // Now ask completed games (partial list) - ajax( - "/completedgames", - "GET", - { - credentials: true, - data: { cursor: this.cursor }, - success: (res2) => { - const L = res2.games.length; - if (L > 0) { - this.cursor = res2.games[L - 1].created; - let completedGames = res2.games; - completedGames.forEach(g => g.type = "corr"); - this.decorate(completedGames); - this.corrGames = this.corrGames.concat(completedGames); - adjustAndSetDisplay(); - } - } - } + this.loadMore( + "live", + () => this.loadMore("corr", adjustAndSetDisplay) ); } } ); - } else adjustAndSetDisplay(); + } else { + this.loadMore( + "live", + () => this.loadMore("corr", adjustAndSetDisplay) + ); + } }); }, beforeDestroy: function() { @@ -287,25 +280,40 @@ export default { ); } }, - loadMore: function() { - ajax( - "/completedgames", - "GET", - { - credentials: true, - data: { cursor: this.cursor }, - success: (res) => { - const L = res.games.length; - if (L > 0) { - this.cursor = res.games[L - 1].created; - let moreGames = res.games; - moreGames.forEach(g => g.type = "corr"); - this.decorate(moreGames); - this.corrGames = this.corrGames.concat(moreGames); - } else this.hasMore = false; + loadMore: function(type, cb) { + if (type == "corr") { + ajax( + "/completedgames", + "GET", + { + credentials: true, + data: { cursor: this.cursor["corr"] }, + success: (res) => { + const L = res.games.length; + if (L > 0) { + this.cursor["corr"] = res.games[L - 1].created; + let moreGames = res.games; + moreGames.forEach(g => g.type = "corr"); + this.decorate(moreGames); + this.corrGames = this.corrGames.concat(moreGames); + } else this.hasMore["corr"] = false; + if (!!cb) cb(); + } } - } - ); + ); + } else if (type == "live") { + GameStorage.getNext(this.cursor["live"], localGames => { + const L = localGames.length; + if (L > 0) { + // Add "-1" because IDBKeyRange.upperBound seems to include boundary + this.cursor["live"] = localGames[L - 1].created - 1; + localGames.forEach(g => g.type = "live"); + this.decorate(localGames); + this.liveGames = this.liveGames.concat(localGames); + } else this.hasMore["live"] = false; + if (!!cb) cb(); + }); + } } } }; diff --git a/client/src/views/Problems.vue b/client/src/views/Problems.vue index cfc0637e..2d5526d0 100644 --- a/client/src/views/Problems.vue +++ b/client/src/views/Problems.vue @@ -144,11 +144,11 @@ export default { problems: { "mine": [], "others": [] }, // timestamp of oldest showed problem: cursor: { - "mine": Number.MAX_SAFE_INTEGER, - "others": Number.MAX_SAFE_INTEGER + mine: Number.MAX_SAFE_INTEGER, + others: Number.MAX_SAFE_INTEGER }, // hasMore == TRUE: a priori there could be more problems to load - hasMore: { "mine": true, "others": true }, + hasMore: { mine: true, others: true }, onlyMine: false, showOne: false, infoMsg: "",