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", 5);
24 DBOpenRequest
.onerror = function(event
) {
26 "Database error: stop private browsing, or update your browser"]);
27 callback("error", null);
30 DBOpenRequest
.onsuccess = function() {
31 db
= DBOpenRequest
.result
;
36 DBOpenRequest
.onupgradeneeded = function(event
) {
37 let db
= event
.target
.result
;
38 let upgradeTransaction
= event
.target
.transaction
;
39 let objectStore
= undefined;
40 if (!db
.objectStoreNames
.contains("games"))
41 objectStore
= db
.createObjectStore("games", { keyPath: "id" });
43 objectStore
= upgradeTransaction
.objectStore("games");
44 if (!objectStore
.indexNames
.contains("score"))
45 // To sarch games by score (useful for running games)
46 objectStore
.createIndex("score", "score", { unique: false });
47 if (!objectStore
.indexNames
.contains("created"))
48 // To search by date intervals. Two games cannot start at the same time
49 objectStore
.createIndex("created", "created", { unique: true });
53 export const GameStorage
= {
55 // Optional callback to get error status
56 add: function(game
, callback
) {
57 dbOperation((err
, db
) => {
62 let transaction
= db
.transaction("games", "readwrite");
63 transaction
.oncomplete = function() {
67 transaction
.onerror = function(err
) {
68 // Duplicate key error (most likely)
71 transaction
.objectStore("games").add(game
);
75 // obj: chat, move, fen, clocks, score[Msg], initime, ...
76 update: function(gameId
, obj
) {
77 dbOperation((err
, db
) => {
79 .transaction("games", "readwrite")
80 .objectStore("games");
81 objectStore
.get(gameId
).onsuccess = function(event
) {
82 // Ignoring error silently: shouldn't happen now. TODO?
83 if (!!event
.target
.result
) {
84 let game
= event
.target
.result
;
85 // Hidden tabs are delayed, to prevent multi-updates:
86 if (obj
.moveIdx
< game
.moves
.length
) return;
87 Object
.keys(obj
).forEach(k
=> {
88 if (k
== "move") game
.moves
.push(obj
[k
]);
89 else if (k
== "chat") game
.chats
.push(obj
[k
]);
90 else if (k
== "chatRead") game
.chatRead
= Date
.now();
91 else if (k
== "delchat") game
.chats
= [];
92 else if (k
== "playerName")
93 game
.players
[obj
[k
].idx
].name
= obj
[k
].name
;
94 else game
[k
] = obj
[k
];
96 objectStore
.put(game
); //save updated data
102 // Retrieve (all) running local games
103 getRunning: function(callback
) {
104 dbOperation((err
, db
) => {
106 .transaction("games", "readonly")
107 .objectStore("games");
108 let index
= objectStore
.index("score");
109 const range
= IDBKeyRange
.only("*");
111 index
.openCursor(range
).onsuccess = function(event
) {
112 let cursor
= event
.target
.result
;
113 if (!cursor
) callback(games
);
115 // If there is still another cursor to go, keep running this code
116 let g
= cursor
.value
;
117 // Do not retrieve moves or clocks (unused in list mode)
118 g
.movesCount
= g
.moves
.length
;
129 // Retrieve completed local games
130 getNext: function(upperDt
, callback
) {
131 dbOperation((err
, db
) => {
133 .transaction("games", "readonly")
134 .objectStore("games");
135 let index
= objectStore
.index("created");
136 const range
= IDBKeyRange
.upperBound(upperDt
);
138 index
.openCursor(range
).onsuccess = function(event
) {
139 let cursor
= event
.target
.result
;
141 // Most recent games first:
142 games
= games
.sort((g1
, g2
) => g2
.created
- g1
.created
);
143 // TODO: 20 games showed per request is arbitrary
144 callback(games
.slice(0, 20));
147 // If there is still another cursor to go, keep running this code
148 let g
= cursor
.value
;
149 if (g
.score
!= "*") {
150 // Do not retrieve moves or clocks (unused in list mode)
151 g
.movesCount
= g
.moves
.length
;
163 // Retrieve any game from its identifier.
164 // NOTE: need callback because result is obtained asynchronously
165 get: function(gameId
, callback
) {
166 dbOperation((err
, db
) => {
167 let objectStore
= db
.transaction("games").objectStore("games");
168 objectStore
.get(gameId
).onsuccess = function(event
) {
169 // event.target.result is null if game not found
170 callback(event
.target
.result
);
175 // Delete a game in indexedDB
176 remove: function(gameId
, callback
) {
177 dbOperation((err
, db
) => {
179 let transaction
= db
.transaction("games", "readwrite");
180 transaction
.oncomplete = function() {
181 callback(); //everything's fine
183 transaction
.objectStore("games").delete(gameId
);