X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Futils%2Fstorage.js;h=2e7a84fb421d91bb5894760914d70cebab6dfb69;hb=d4036efea5b57656478affd7d71f53dcea0f8017;hp=8008777458328773e08ae777bccc58fc06537241;hpb=8d61fc4ab7373b4a576f3f9108cdf7768ae27096;p=vchess.git diff --git a/client/src/utils/storage.js b/client/src/utils/storage.js index 80087774..2e7a84fb 100644 --- a/client/src/utils/storage.js +++ b/client/src/utils/storage.js @@ -1,52 +1,214 @@ -// 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"; -function setStorage(myid, oppid, gameId, variant, mycolor, fenStart) +// TODO: show game structure +//const newItem = [ +// { gameId: "", players: [], timeControl: "", clocks: [] } +//]; + +function dbOperation(callback) { - localStorage.setItem("myid", myid); - localStorage.setItem("oppid", oppid); - localStorage.setItem("gameId", gameId); - localStorage.setItem("variant", variant); - localStorage.setItem("mycolor", mycolor); - localStorage.setItem("fenStart", fenStart); - localStorage.setItem("moves", []); + 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" }); + } } -function updateStorage(move) +// Optional callback to get error status +function addGame(game, callback) { - let moves = JSON.parse(localStorage.getItem("moves")); - moves.push(move); - localStorage.setItem("moves", JSON.stringify(moves)); + 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); + }); } -// "computer mode" clearing is done through the menu -function clearStorage() +// Clear current live game from localStorage +function clear() { + localStorage.removeItem("gameInfo"); + localStorage.removeItem("gameState"); +} + +// Current live game: +function getCurrent() { - delete localStorage["myid"]; - delete localStorage["oppid"]; - delete localStorage["gameId"]; - delete localStorage["variant"]; - delete localStorage["mycolor"]; - delete localStorage["fenStart"]; - delete localStorage["moves"]; + return Object.assign({}, + JSON.parse(localStorage.getItem("gameInfo")), + JSON.parse(localStorage.getItem("gameState"))); } -function getGameFromStorage(gameId) +// Only called internally after a score update +function transferToDb() { - let game = {}; - if (localStorage.getItem("gameId") === gameId) - { - // Retrieve running game from localStorage - game.score = localStorage.getItem("score"); - game.oppid = localStorage.getItem("oppid"); - game.oppname = localStorage.getItem("oppname"); - game.mycolor = localStorage.getItem("mycolor"); - game.fenStart = localStorage.getItem("fenStart"); - game.moves = localStorage.getItem("moves"); - } - else - { - // Find the game in indexedDB: TODO - } + addGame(getCurrent(), (err) => { + if (!!err.errmsg) + return err; + clear(); + }); } + +export const GameStorage = +{ + // localStorage: + init: function(o) + { + // 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), + initime: (o.initime ? Date.now() : undefined), + score: "*", + }; + + localStorage.setItem("gameInfo", JSON.stringify(gameInfo)); + localStorage.setItem("gameState", JSON.stringify(gameState)); + }, + + getInitime: function() + { + const gameState = JSON.parse(localStorage.getItem("gameState")); + return gameState.initime; + }, + + // localStorage: + // TODO: also option to takeback a move ? + // NOTE: for live games only (all on server for corr) + update: function(o) //colorIdx, move, fen, addTime, initime, score + { + let gameState = JSON.parse(localStorage.getItem("gameState")); + if (!!o.move) + { + gameState.moves.push(o.move); + gameState.fen = o.fen; + if (!!o.addTime) //NaN if first move in game + gameState.clocks[o.colorIdx] += o.addTime; + } + if (!!o.initime) //just a flag (true) + gameState.initime = Date.now(); + if (!!o.score) + gameState.score = o.score; + localStorage.setItem("gameState", JSON.stringify(gameState)); + if (!!o.score && o.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) + { + dbOperation((db) => { + let objectStore = db.transaction('games').objectStore('games'); + if (!gameId) //retrieve all + { + let games = []; + objectStore.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + // if there is still another cursor to go, keep running this code + if (cursor) + { + games.push(cursor.value); + cursor.continue(); + } + else + callback(games); + } + } + else //just one game + { + objectStore.get(gameId).onsuccess = function(event) { + callback(event.target.result); + } + } + }); + }, + + // Delete a game in indexedDB + remove: function(gameId, callback) + { + dbOperation((db) => { + let transaction = db.transaction(["games"], "readwrite"); + if (callback) + { + transaction.oncomplete = function() { + callback({}); //everything's fine + } + transaction.onerror = function() { + callback({errmsg: "game removal failed: " + transaction.error}); + }; + } + transaction.objectStore("games").delete(gameId); + }); + }, + + // 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 + if (!!rid) + { + // 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...) + } + + const gameInfoStr = localStorage.getItem("gameInfo"); + if (gameInfoStr) + { + const gameInfo = JSON.parse(gameInfoStr); + if (gameInfo.gameId == gid) + { + const gameState = JSON.parse(localStorage.getItem("gameState")); + return callback(Object.assign({}, gameInfo, gameState)); + } + } + + // Game is local and not running + GameStorage.getLocal(gid, callback); + }, +};