Some improvements (multi-tabs on same game seem fixed)
[vchess.git] / client / src / utils / gameStorage.js
CommitLineData
967a2686
BA
1// Game object: {
2// // Static informations:
11667c79 3// id: string
967a2686
BA
4// vname: string,
5// fenStart: string,
6// players: array of sid+id+name,
71468011 7// cadence: string,
967a2686 8// increment: integer (seconds),
910d631b 9// type: string ("live" or "corr")
967a2686
BA
10// // Game (dynamic) state:
11// fen: string,
12// moves: array of Move objects,
13// clocks: array of integers,
809ba2aa 14// initime: array of integers (when clock start running),
967a2686
BA
15// score: string (several options; '*' == running),
16// }
17
fd7aea36 18import { ajax } from "@/utils/ajax";
602d6bef 19import { store } from "@/store";
fd7aea36 20
6808d7a1 21function dbOperation(callback) {
967a2686
BA
22 let db = null;
23 let DBOpenRequest = window.indexedDB.open("vchess", 4);
24
25 DBOpenRequest.onerror = function(event) {
8477e53d
BA
26 alert(store.state.tr["Database error: stop private browsing, or update your browser"]);
27 callback("error",null);
967a2686
BA
28 };
29
6808d7a1 30 DBOpenRequest.onsuccess = function() {
967a2686 31 db = DBOpenRequest.result;
8477e53d 32 callback(null,db);
967a2686
BA
33 db.close();
34 };
35
36 DBOpenRequest.onupgradeneeded = function(event) {
37 let db = event.target.result;
11667c79 38 let objectStore = db.createObjectStore("games", { keyPath: "id" });
42c15a75 39 objectStore.createIndex("score", "score"); //to search by game result
6808d7a1 40 };
967a2686
BA
41}
42
6808d7a1 43export const GameStorage = {
967a2686 44 // Optional callback to get error status
6808d7a1 45 add: function(game, callback) {
8477e53d
BA
46 dbOperation((err,db) => {
47 if (err) {
48 callback("error");
49 return;
967a2686 50 }
8477e53d
BA
51 let transaction = db.transaction("games", "readwrite");
52 transaction.oncomplete = function() {
53 callback(); //everything's fine
54 };
967a2686
BA
55 let objectStore = transaction.objectStore("games");
56 objectStore.add(game);
57 });
58 },
59
60 // TODO: also option to takeback a move ?
dcd68c41 61 // obj: chat, move, fen, clocks, score[Msg], initime, ...
6808d7a1
BA
62 update: function(gameId, obj) {
63 if (Number.isInteger(gameId) || !isNaN(parseInt(gameId))) {
3d55deea 64 // corr: only move, fen and score
6808d7a1
BA
65 ajax("/games", "PUT", {
66 gid: gameId,
67 newObj: {
68 // Some fields may be undefined:
69 chat: obj.chat,
70 move: obj.move,
71 fen: obj.fen,
72 score: obj.score,
73 scoreMsg: obj.scoreMsg,
74 drawOffer: obj.drawOffer
3d55deea 75 }
6808d7a1
BA
76 });
77 } else {
3d55deea 78 // live
8477e53d 79 dbOperation((err,db) => {
6808d7a1
BA
80 let objectStore = db
81 .transaction("games", "readwrite")
82 .objectStore("games");
3d55deea 83 objectStore.get(gameId).onsuccess = function(event) {
8477e53d
BA
84 // Ignoring error silently: shouldn't happen now. TODO?
85 if (event.target.result) {
dcff8e82 86 let game = event.target.result;
57eb158f
BA
87 // Hidden tabs are delayed, to prevent multi-updates:
88 if (obj.moveIdx < game.moves.length) return;
8477e53d
BA
89 Object.keys(obj).forEach(k => {
90 if (k == "move") game.moves.push(obj[k]);
91 else game[k] = obj[k];
92 });
93 objectStore.put(game); //save updated data
94 }
6808d7a1 95 };
3d55deea
BA
96 });
97 }
967a2686
BA
98 },
99
fd7aea36 100 // Retrieve all local games (running, completed, imported...)
23ecf008 101 // light: do not retrieve moves or clocks (TODO: this is the only usage)
db1f1f9a 102 getAll: function(light, callback) {
8477e53d 103 dbOperation((err,db) => {
6808d7a1 104 let objectStore = db.transaction("games").objectStore("games");
fd7aea36
BA
105 let games = [];
106 objectStore.openCursor().onsuccess = function(event) {
107 let cursor = event.target.result;
108 // if there is still another cursor to go, keep running this code
6808d7a1 109 if (cursor) {
db1f1f9a
BA
110 let g = cursor.value;
111 if (light) {
112 g.movesCount = g.moves.length;
113 delete g.moves;
114 delete g.clocks;
115 delete g.initime;
db1f1f9a
BA
116 }
117 games.push(g);
fd7aea36 118 cursor.continue();
6808d7a1
BA
119 } else callback(games);
120 };
fd7aea36
BA
121 });
122 },
123
124 // Retrieve any game from its identifiers (locally or on server)
125 // NOTE: need callback because result is obtained asynchronously
6808d7a1 126 get: function(gameId, callback) {
fd7aea36 127 // corr games identifiers are integers
6808d7a1
BA
128 if (Number.isInteger(gameId) || !isNaN(parseInt(gameId))) {
129 ajax("/games", "GET", { gid: gameId }, res => {
92b82def
BA
130 let game = res.game;
131 game.moves.forEach(m => {
132 m.squares = JSON.parse(m.squares);
133 });
134 callback(game);
fd7aea36 135 });
8477e53d 136 }
6808d7a1 137 else {
8477e53d
BA
138 // Local game
139 dbOperation((err,db) => {
6808d7a1 140 let objectStore = db.transaction("games").objectStore("games");
967a2686 141 objectStore.get(gameId).onsuccess = function(event) {
8477e53d
BA
142 if (event.target.result)
143 callback(event.target.result);
6808d7a1 144 };
fd7aea36
BA
145 });
146 }
967a2686
BA
147 },
148
149 // Delete a game in indexedDB
6808d7a1 150 remove: function(gameId, callback) {
8477e53d
BA
151 dbOperation((err,db) => {
152 if (!err) {
153 let transaction = db.transaction(["games"], "readwrite");
967a2686
BA
154 transaction.oncomplete = function() {
155 callback({}); //everything's fine
6808d7a1 156 };
8477e53d 157 transaction.objectStore("games").delete(gameId);
967a2686 158 }
967a2686 159 });
6808d7a1 160 }
967a2686 161};