loadMore button for my local games as well
[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
602d6bef 18import { store } from "@/store";
fd7aea36 19
6808d7a1 20function dbOperation(callback) {
967a2686
BA
21 let db = null;
22 let DBOpenRequest = window.indexedDB.open("vchess", 4);
23
24 DBOpenRequest.onerror = function(event) {
8477e53d 25 alert(store.state.tr["Database error: stop private browsing, or update your browser"]);
934f7f70 26 callback("error", null);
967a2686
BA
27 };
28
6808d7a1 29 DBOpenRequest.onsuccess = function() {
967a2686 30 db = DBOpenRequest.result;
934f7f70 31 callback(null, db);
967a2686
BA
32 db.close();
33 };
34
35 DBOpenRequest.onupgradeneeded = function(event) {
36 let db = event.target.result;
11667c79 37 let objectStore = db.createObjectStore("games", { keyPath: "id" });
934f7f70
BA
38 // To sarch games by score (useful for running games)
39 objectStore.createIndex("score", "score", { unique: false });
40 // To search by date intervals. Two games cannot start at the same time
41 objectStore.createIndex("created", "created", { unique: true });
6808d7a1 42 };
967a2686
BA
43}
44
6808d7a1 45export const GameStorage = {
967a2686 46 // Optional callback to get error status
6808d7a1 47 add: function(game, callback) {
8477e53d 48 dbOperation((err,db) => {
c292ebb2 49 if (!!err) {
8477e53d
BA
50 callback("error");
51 return;
967a2686 52 }
8477e53d
BA
53 let transaction = db.transaction("games", "readwrite");
54 transaction.oncomplete = function() {
934f7f70
BA
55 // Everything's fine
56 callback();
8477e53d 57 };
c292ebb2 58 transaction.onerror = function(err) {
934f7f70
BA
59 // Duplicate key error (most likely)
60 callback(err);
c292ebb2 61 };
934f7f70 62 transaction.objectStore("games").add(game);
967a2686
BA
63 });
64 },
65
dcd68c41 66 // obj: chat, move, fen, clocks, score[Msg], initime, ...
6808d7a1 67 update: function(gameId, obj) {
aae89b49
BA
68 // live
69 dbOperation((err,db) => {
70 let objectStore = db
71 .transaction("games", "readwrite")
72 .objectStore("games");
73 objectStore.get(gameId).onsuccess = function(event) {
74 // Ignoring error silently: shouldn't happen now. TODO?
75 if (event.target.result) {
76 let game = event.target.result;
77 // Hidden tabs are delayed, to prevent multi-updates:
78 if (obj.moveIdx < game.moves.length) return;
79 Object.keys(obj).forEach(k => {
80 if (k == "move") game.moves.push(obj[k]);
81 else game[k] = obj[k];
82 });
83 objectStore.put(game); //save updated data
3d55deea 84 }
aae89b49
BA
85 };
86 });
967a2686
BA
87 },
88
934f7f70
BA
89 // Retrieve (all) running local games
90 getRunning: function(callback) {
8477e53d 91 dbOperation((err,db) => {
934f7f70
BA
92 let objectStore = db
93 .transaction("games", "readonly")
94 .objectStore("games");
95 let index = objectStore.index("score");
96 const range = IDBKeyRange.only("*");
fd7aea36 97 let games = [];
934f7f70 98 index.openCursor(range).onsuccess = function(event) {
fd7aea36 99 let cursor = event.target.result;
934f7f70
BA
100 if (!cursor) callback(games);
101 else {
102 // If there is still another cursor to go, keep running this code
db1f1f9a 103 let g = cursor.value;
6b7b2cf7
BA
104 // Do not retrieve moves or clocks (unused in list mode)
105 g.movesCount = g.moves.length;
106 delete g.moves;
107 delete g.clocks;
108 delete g.initime;
db1f1f9a 109 games.push(g);
fd7aea36 110 cursor.continue();
934f7f70
BA
111 }
112 };
113 });
114 },
115
116 // Retrieve completed local games
117 getNext: function(upperDt, callback) {
118 dbOperation((err,db) => {
119 let objectStore = db
120 .transaction("games", "readonly")
121 .objectStore("games");
122 let index = objectStore.index("created");
123 const range = IDBKeyRange.upperBound(upperDt);
124 let games = [];
125 index.openCursor(range).onsuccess = function(event) {
126 let cursor = event.target.result;
127 if (!cursor) {
128 // Most recent games first:
129 games = games.sort((g1, g2) => g2.created - g1.created);
130 // TODO: 20 games showed per request is arbitrary
131 callback(games.slice(0, 20));
132 }
133 else {
134 // If there is still another cursor to go, keep running this code
135 let g = cursor.value;
136 if (g.score != "*") {
137 // Do not retrieve moves or clocks (unused in list mode)
138 g.movesCount = g.moves.length;
139 delete g.moves;
140 delete g.clocks;
141 delete g.initime;
142 games.push(g);
143 }
144 cursor.continue();
145 }
6808d7a1 146 };
fd7aea36
BA
147 });
148 },
149
150 // Retrieve any game from its identifiers (locally or on server)
151 // NOTE: need callback because result is obtained asynchronously
6808d7a1 152 get: function(gameId, callback) {
aae89b49
BA
153 // Local game
154 dbOperation((err,db) => {
155 let objectStore = db.transaction("games").objectStore("games");
156 objectStore.get(gameId).onsuccess = function(event) {
f54f4c26
BA
157 // event.target.result is null if game not found
158 callback(event.target.result);
aae89b49
BA
159 };
160 });
967a2686
BA
161 },
162
163 // Delete a game in indexedDB
6808d7a1 164 remove: function(gameId, callback) {
8477e53d
BA
165 dbOperation((err,db) => {
166 if (!err) {
934f7f70 167 let transaction = db.transaction("games", "readwrite");
967a2686 168 transaction.oncomplete = function() {
3b0f26c1 169 callback(); //everything's fine
6808d7a1 170 };
8477e53d 171 transaction.objectStore("games").delete(gameId);
967a2686 172 }
967a2686 173 });
6808d7a1 174 }
967a2686 175};