2 // // Static informations:
6 // players: array of sid+id+name,
8 // increment: integer (seconds),
9 // type: string ("live" or "corr")
10 // // Game (dynamic) state:
12 // moves: array of Move objects,
13 // clocks: array of integers,
14 // initime: array of integers (when clock start running),
15 // score: string (several options; '*' == running),
18 import { store
} from "@/store";
20 function dbOperation(callback
) {
22 let DBOpenRequest
= window
.indexedDB
.open("vchess", 4);
24 DBOpenRequest
.onerror = function(event
) {
25 alert(store
.state
.tr
["Database error: stop private browsing, or update your browser"]);
26 callback("error", null);
29 DBOpenRequest
.onsuccess = function() {
30 db
= DBOpenRequest
.result
;
35 DBOpenRequest
.onupgradeneeded = function(event
) {
36 let db
= event
.target
.result
;
37 let objectStore
= db
.createObjectStore("games", { keyPath: "id" });
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 });
45 export const GameStorage
= {
46 // Optional callback to get error status
47 add: function(game
, callback
) {
48 dbOperation((err
,db
) => {
53 let transaction
= db
.transaction("games", "readwrite");
54 transaction
.oncomplete = function() {
58 transaction
.onerror = function(err
) {
59 // Duplicate key error (most likely)
62 transaction
.objectStore("games").add(game
);
66 // obj: chat, move, fen, clocks, score[Msg], initime, ...
67 update: function(gameId
, obj
) {
69 dbOperation((err
,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
];
83 objectStore
.put(game
); //save updated data
89 // Retrieve (all) running local games
90 getRunning: function(callback
) {
91 dbOperation((err
,db
) => {
93 .transaction("games", "readonly")
94 .objectStore("games");
95 let index
= objectStore
.index("score");
96 const range
= IDBKeyRange
.only("*");
98 index
.openCursor(range
).onsuccess = function(event
) {
99 let cursor
= event
.target
.result
;
100 if (!cursor
) callback(games
);
102 // If there is still another cursor to go, keep running this code
103 let g
= cursor
.value
;
104 // Do not retrieve moves or clocks (unused in list mode)
105 g
.movesCount
= g
.moves
.length
;
116 // Retrieve completed local games
117 getNext: function(upperDt
, callback
) {
118 dbOperation((err
,db
) => {
120 .transaction("games", "readonly")
121 .objectStore("games");
122 let index
= objectStore
.index("created");
123 const range
= IDBKeyRange
.upperBound(upperDt
);
125 index
.openCursor(range
).onsuccess = function(event
) {
126 let cursor
= event
.target
.result
;
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));
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
;
150 // Retrieve any game from its identifiers (locally or on server)
151 // NOTE: need callback because result is obtained asynchronously
152 get: function(gameId
, callback
) {
154 dbOperation((err
,db
) => {
155 let objectStore
= db
.transaction("games").objectStore("games");
156 objectStore
.get(gameId
).onsuccess = function(event
) {
157 // event.target.result is null if game not found
158 callback(event
.target
.result
);
163 // Delete a game in indexedDB
164 remove: function(gameId
, callback
) {
165 dbOperation((err
,db
) => {
167 let transaction
= db
.transaction("games", "readwrite");
168 transaction
.oncomplete = function() {
169 callback(); //everything's fine
171 transaction
.objectStore("games").delete(gameId
);