X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Futils%2Fstorage.js;h=48435b51175b2efb4fd5b03ec3c15a51735e929e;hb=4b0384faf03d3842ba2f80ccd6d104c5e34a355e;hp=937d8c78e96add51502406a3f55cf59248a9273b;hpb=d263438699b50b97b60fa8112fbc1e030b41f771;p=vchess.git diff --git a/client/src/utils/storage.js b/client/src/utils/storage.js index 937d8c78..48435b51 100644 --- a/client/src/utils/storage.js +++ b/client/src/utils/storage.js @@ -1,64 +1,199 @@ -// TODO: general methods to access/retrieve from storage, to be generalized -// https://developer.mozilla.org/fr/docs/Web/API/API_IndexedDB -// https://dexie.org/ +import { extractTime } from "@/utils/timeControl"; + +// TODO: show game structure +//const newItem = [ +// { gameId: "", players: [], timeControl: "", clocks: [] } +//]; + +function dbOperation(callback) +{ + let db = null; + let DBOpenRequest = window.indexedDB.open("vchess", 4); + + DBOpenRequest.onerror = function(event) { + alert("Database error: " + event.target.errorCode); + }; + + DBOpenRequest.onsuccess = function(event) { + db = DBOpenRequest.result; + callback(db); + db.close(); + }; + + DBOpenRequest.onupgradeneeded = function(event) { + let db = event.target.result; + db.onerror = function(event) { + alert("Error while loading database: " + event.target.errorCode); + }; + // Create objectStore for vchess->games + db.createObjectStore("games", { keyPath: "gameId" }); + } +} + +// Optional callback to get error status +function addGame(game, callback) +{ + dbOperation((db) => { + let transaction = db.transaction(["games"], "readwrite"); + if (callback) + { + transaction.oncomplete = function() { + callback({}); //everything's fine + } + transaction.onerror = function() { + callback({errmsg: "addGame failed: " + transaction.error}); + }; + } + let objectStore = transaction.objectStore("games"); + objectStore.add(game); + }); +} + +// Clear current live game from localStorage +function clear() { + localStorage.deleteItem("gameInfo"); + localStorage.deleteItem("gameState"); +} + +// Current live game: +function getCurrent() +{ + return Object.assign({}, + JSON.parse(localStorage.getItem("gameInfo")), + JSON.parse(localStorage.getItem("gameState"))); +} + +// Only called internally after a score update +function transferToDb() +{ + addGame(getCurrent(), (err) => { + if (!!err.errmsg) + return err; + clear(); + }); +} export const GameStorage = { - init: function(myid, oppid, gameId, variant, mycolor, fenStart) + // localStorage: + init: function(o) { - localStorage.setItem("myid", myid); - localStorage.setItem("gameId", gameId); - localStorage.setItem("vname", variant); - localStorage.setItem("mycolor", mycolor); - localStorage.setItem("fenStart", fenStart); - localStorage.setItem("moves", []); + // Extract times (in [milli]seconds), set clocks, store in localStorage + const tc = extractTime(o.timeControl); + + // game infos: constant + const gameInfo = + { + gameId: o.gameId, + vname: o.vname, + fenStart: o.fenStart, + players: o.players, + timeControl: o.timeControl, + increment: tc.increment, + mode: "live", //function for live games only + }; + + // game state: will be updated + const gameState = + { + fen: o.fenStart, + moves: [], + clocks: [...Array(o.players.length)].fill(tc.mainTime), + started: [...Array(o.players.length)].fill(false), + score: "*", + }; + + localStorage.setItem("gameInfo", JSON.stringify(gameInfo)); + localStorage.setItem("gameState", JSON.stringify(gameState)); }, - // TODO: also option to takeback a move ? - update: function(move) + // localStorage: + // TODO: also option to takeback a move ? Is fen included in move ? + // NOTE: for live games only (all on server for corr) + update: function(fen, moves, clocks, started, score) { - let moves = JSON.parse(localStorage.getItem("moves")); - moves.push(move); - localStorage.setItem("moves", JSON.stringify(moves)); + let gameState = JSON.parse(localStorage.getItem("gameState")); + if (!!fen) + { + gameState.moves = moves; + gameState.fen = fen; + gameState.clocks = clocks; + } + if (!!started) + gameState.started = started; + if (!!score) + gameState.score = score; + localStorage.setItem("gameState", JSON.stringify(gameState)); + if (!!score && score != "*") + transferToDb(); //game is over + }, + + // indexedDB: + // Since DB requests are asynchronous, require a callback using the result + // TODO: option for remote retrieval (third arg, or just "gameRef") + getLocal: function(gameId, callback) + { + let games = []; + dbOperation((db) => { + // TODO: if gameId is provided, limit search to gameId (just .get(gameId). ...) + let objectStore = db.transaction('games').objectStore('games'); + objectStore.openCursor().onsuccess = function(event) { + var cursor = event.target.result; + // if there is still another cursor to go, keep runing this code + if (cursor) + { + games.push(cursor.value); + cursor.continue(); + } + else + callback(games); + } + }); }, - // "computer mode" clearing is done through the menu - clear: function() + // Delete a game in indexedDB + remove: function(gameId, callback) { - // TODO: refresh, and implement "transfert" function (to indexedDB) - delete localStorage["myid"]; - delete localStorage["oppid"]; - delete localStorage["gameId"]; - delete localStorage["variant"]; - delete localStorage["mycolor"]; - delete localStorage["fenStart"]; - delete localStorage["moves"]; + dbOperation((db) => { + let transaction = db.transaction(["games"], "readwrite"); + if (callback) + { + transaction.oncomplete = function() { + callback({}); //everything's fine + } + transaction.onerror = function() { + callback({errmsg: "deleteGame failed: " + transaction.error}); + }; + } + transaction.objectStore("games").delete(gameId); + }); }, - get: function(gameRef) + // Retrieve any live game from its identifiers (remote or not, running or not) + // NOTE: need callback because result might be obtained asynchronously + get: function(gameRef, callback) { const gid = gameRef.id; const rid = gameRef.rid; //may be blank - let game = {}; - if (localStorage.getItem("gameId") === gid) + if (!!rid) { - // Retrieve running game from localStorage - game.score = localStorage.getItem("score"); - game.mycolor = localStorage.getItem("mycolor"); - game.fenStart = localStorage.getItem("fenStart"); - game.fen = localStorage.getItem("fen"); - game.moves = JSON.parse(localStorage.getItem("moves")); - game.players = JSON.parse(localStorage.getItem("players")); - game.started = JSON.parse(localStorage.getItem("started")); - game.clocks = JSON.parse(localStorage.getItem("clocks")); - game.timeControl = localStorage.getItem("timeControl"); - game.increment = localStorage.getItem("increment"); - game.mode = "live"; + // TODO: send request to server which forward to user sid == rid, + // need to listen to "remote game" event in main hall ? + return callback({}); //means "the game will arrive later" (TODO...) } - else + + const gameInfoStr = localStorage.getItem("gameInfo"); + if (gameInfoStr) { - // Find the game in indexedDB, on server or remotely: TODO + const gameInfo = JSON.parse(gameInfoStr); + if (gameInfo.gameId == gid) + { + const gameState = JSON.parse(localStorage.getItem("gameState")); + return callback(Object.assign({}, gameInfo, gameState)); + } } - return game; + + // Game is local and not running + GameStorage.getLocal(gid, callback); }, };