From 5f918a278904266a2a66a3c8e2a3655f37c2d2a7 Mon Sep 17 00:00:00 2001 From: Benjamin Auder <benjamin.auder@somewhere> Date: Mon, 30 Mar 2020 15:06:08 +0200 Subject: [PATCH] Draft game upload logic (unwritten yet for Analysis mode) --- client/public/images/icons/SOURCE | 1 + client/public/images/icons/upload.svg | 126 ++++++++++++++++++++++++++ client/src/components/Board.vue | 12 +-- client/src/components/GameList.vue | 6 +- client/src/components/UploadGame.vue | 44 +++++++++ client/src/translations/en.js | 3 + client/src/translations/es.js | 3 + client/src/translations/fr.js | 3 + client/src/utils/compgameStorage.js | 8 +- client/src/utils/gameStorage.js | 15 ++- client/src/utils/importgameStorage.js | 113 +++++++++++++++++++++++ client/src/views/Analyse.vue | 3 +- client/src/views/Game.vue | 9 +- client/src/views/MyGames.vue | 47 +++++++++- 14 files changed, 367 insertions(+), 26 deletions(-) create mode 100644 client/public/images/icons/upload.svg create mode 100644 client/src/components/UploadGame.vue create mode 100644 client/src/utils/importgameStorage.js diff --git a/client/public/images/icons/SOURCE b/client/public/images/icons/SOURCE index 4d22cabf..235d0b93 100644 --- a/client/public/images/icons/SOURCE +++ b/client/public/images/icons/SOURCE @@ -10,6 +10,7 @@ https://www.onlinewebfonts.com/icon/256756 https://www.flaticon.com/free-icon/forward_2413353?term=forward&page=1&position=59 https://www.flaticon.com/free-icon/right_565870?term=forward&page=1&position=31 https://www.flaticon.com/free-icon/download_724933?term=download&page=1&position=3 +https://www.flaticon.com/free-icon/upload_725008?term=upload&page=1&position=14 https://www.flaticon.com/free-icon/resize_512182?term=resize&page=1&position=49 https://www.flaticon.com/free-icon/clear_565313?term=delete&page=1&position=33 https://www.flaticon.com/free-icon/clear_1632708?term=delete&page=1&position=3 diff --git a/client/public/images/icons/upload.svg b/client/public/images/icons/upload.svg new file mode 100644 index 00000000..4e8253a0 --- /dev/null +++ b/client/public/images/icons/upload.svg @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Capa_1" + x="0px" + y="0px" + viewBox="0 0 512 512" + style="enable-background:new 0 0 512 512;" + xml:space="preserve" + sodipodi:docname="upload.svg" + inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata + id="metadata49"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs47" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="960" + inkscape:window-height="1060" + id="namedview45" + showgrid="false" + inkscape:zoom="1.3037281" + inkscape:cx="260.33898" + inkscape:cy="256" + inkscape:window-x="0" + inkscape:window-y="20" + inkscape:window-maximized="0" + inkscape:current-layer="Capa_1" /> +<g + id="g6" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> + <g + id="g4"> + <path + d="m 380.032,133.472 -112,-128 C 264.992,2.016 260.608,0 256,0 c -4.608,0 -8.992,2.016 -12.032,5.472 l -112,128 c -4.128,4.736 -5.152,11.424 -2.528,17.152 2.592,5.696 8.288,9.376 14.56,9.376 h 64 v 208 c 0,8.832 7.168,16 16,16 h 64 c 8.832,0 16,-7.168 16,-16 V 160 h 64 c 6.272,0 11.968,-3.648 14.56,-9.376 2.592,-5.728 1.632,-12.448 -2.528,-17.152 z" + id="path2" + inkscape:connector-curvature="0" /> + </g> +</g> +<g + id="g12" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> + <g + id="g10"> + <path + d="m 432,352 v 96 H 80 V 352 H 16 v 128 c 0,17.696 14.336,32 32,32 h 416 c 17.696,0 32,-14.304 32,-32 V 352 Z" + id="path8" + inkscape:connector-curvature="0" /> + </g> +</g> +<g + id="g14" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g16" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g18" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g20" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g22" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g24" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g26" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g28" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g30" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g32" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g34" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g36" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g38" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g40" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +<g + id="g42" + transform="matrix(1.0639192,0,0,1,-15.596299,0)"> +</g> +</svg> \ No newline at end of file diff --git a/client/src/components/Board.vue b/client/src/components/Board.vue index cf0a70c7..639938b2 100644 --- a/client/src/components/Board.vue +++ b/client/src/components/Board.vue @@ -303,9 +303,9 @@ export default { elementArray.push(gameDiv); if (!!this.vr.reserve) elementArray.push(reserveBottom); const boardElt = document.querySelector(".game"); - // Square width might be undefine (at first drawing), + // boardElt might be undefine (at first drawing), // but it won't be used in this case. - const squareWidth = boardElt.offsetWidth / sizeY; + const squareWidth = (!!boardElt ? boardElt.offsetWidth / sizeY : 42); if (this.choices.length > 0 && !!boardElt) { // No choices to show at first drawing const offset = [boardElt.offsetTop, boardElt.offsetLeft]; @@ -446,10 +446,10 @@ export default { attrs: { id: "arrow", markerWidth: (2 * arrowWidth) + "px", - markerHeight: (2 * arrowWidth) + "px", + markerHeight: (3 * arrowWidth) + "px", markerUnits: "userSpaceOnUse", refX: "0", - refY: arrowWidth + "px", + refY: (1.5 * arrowWidth) + "px", orient: "auto" } }, @@ -460,8 +460,8 @@ export default { "class": { "arrow-head": true }, attrs: { d: ( - "M0,0 L0," + (2 * arrowWidth) + " " + - "L" + (2 * arrowWidth) + "," + arrowWidth + " z" + "M0,0 L0," + (3 * arrowWidth) + " L" + + (2 * arrowWidth) + "," + (1.5 * arrowWidth) + " z" ) } } diff --git a/client/src/components/GameList.vue b/client/src/components/GameList.vue index a83eb85a..5596e448 100644 --- a/client/src/components/GameList.vue +++ b/client/src/components/GameList.vue @@ -28,6 +28,7 @@ div <script> import { store } from "@/store"; import { GameStorage } from "@/utils/gameStorage"; +import { ImportgameStorage } from "@/utils/importgameStorage"; import { ajax } from "@/utils/ajax"; export default { name: "my-game-list", @@ -124,12 +125,15 @@ export default { : "Abort and remove game?"; if (confirm(this.st.tr[message])) { const afterDelete = () => { - if (game.score == "*") this.$emit("abortgame", game); + if (game.score == "*" && game.type != "import") + this.$emit("abortgame", game); this.$set(this.deleted, game.id, true); }; if (game.type == "live") // Effectively remove game: GameStorage.remove(game.id, afterDelete); + else if (game.type == "import") + ImportgameStorage.remove(game.id, afterDelete); else { const mySide = game.players[0].id == this.st.user.id diff --git a/client/src/components/UploadGame.vue b/client/src/components/UploadGame.vue new file mode 100644 index 00000000..269cc04d --- /dev/null +++ b/client/src/components/UploadGame.vue @@ -0,0 +1,44 @@ +<template lang="pug"> +div + input#upload(type="file" @change="upload") + button#uploadBtn( + @click="uploadTrigger()" + aria-label="store.state.tr['Upload a game']" + ) + img.inline(src="/images/icons/upload.svg") +</template> + +<script> +export default { + name: "my-upload-game", + methods: { + uploadTrigger: function() { + document.getElementById("upload").click(); + }, + upload: function(e) { + const file = (e.target.files || e.dataTransfer.files)[0]; + var reader = new FileReader(); + reader.onloadend = ev => { + this.parseAndEmit(ev.currentTarget.result); + }; + reader.readAsText(file); + }, + parseAndEmit: function(pgn) { + // TODO: header gives game Info, third secton the moves + let game = {}; + // mark sur ID pour dire import : I_ + this.$emit("game-uploaded", game); + } + } +}; +</script> + +<style lang="sass" scoped> +input#upload + display: none + +img.inline + height: 22px + @media screen and (max-width: 767px) + height: 18px +</style> diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 7de0b9c1..e1d6189e 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -50,6 +50,7 @@ export const translations = { green: "green", Hall: "Hall", "Highlight last move": "Highlight last move", + "Imported games": "Imported games", Instructions: "Instructions", "Invalid email": "Invalid email", "It's your turn!": "It's your turn!", @@ -85,6 +86,7 @@ export const translations = { News: "News", "No challenges found :( Click on 'New game'!": "No challenges found :( Click on 'New game'!", "No games found :( Send a challenge!": "No games found :( Send a challenge!", + "No identifier found: use the upload button in analysis mode": "No identifier found: use the upload button in analysis mode", "No more problems": "No more problems", "No subject. Send anyway?": "No subject. Send anyway?", "Notifications by email": "Notifications by email", @@ -137,6 +139,7 @@ export const translations = { Time: "Time", "Undetermined result": "Undetermined result", Update: "Update", + "Upload a game": "Upload a game", "User creation failed. Try again": "User creation failed. Try again", "User name": "User name", "User name or email already in use": "User name or email already in use", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 3a4ece50..d14f4606 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -50,6 +50,7 @@ export const translations = { green: "verde", Hall: "Salón", "Highlight last move": "Resaltar el último movimiento", + "Imported games": "Partidas importadas", Instructions: "Instrucciones", "Invalid email": "Email inválido", "It's your turn!": "¡Es su turno!", @@ -85,6 +86,7 @@ export const translations = { News: "Noticias", "No challenges found :( Click on 'New game'!": "No se encontró ningún desafÃo :( ¡Haz clic en 'Nueva partida'!", "No games found :( Send a challenge!": "No se encontró partidas :( ¡EnvÃa un desafÃo!", + "No identifier found: use the upload button in analysis mode": "No se encontró ningún identificador: use el botón enviar en modo de análisis", "No more problems": "No mas problemas", "No subject. Send anyway?": "Sin asunto. ¿Enviar sin embargo?", "Notifications by email": "Notificaciones por email", @@ -137,6 +139,7 @@ export const translations = { Time: "Tiempo", "Undetermined result": "Resultado indeterminado", Update: "Actualización", + "Upload a game": "Enviar una partida", "User creation failed. Try again": "Error al crear cuenta. inténtelo de nuevo", "User name": "Nombre de usuario", "User name or email already in use": "Nombre de usuario o correo electrónico ya en uso", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 3ed54e76..2d7b535b 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -50,6 +50,7 @@ export const translations = { green: "vert", Hall: "Salon", "Highlight last move": "Mettre en valeur le dernier coup", + "Imported games": "Parties importées", Instructions: "Instructions", "Invalid email": "Email invalide", "It's your turn!": "à vous de jouer !", @@ -85,6 +86,7 @@ export const translations = { News: "Nouvelles", "No challenges found :( Click on 'New game'!": "Aucun défi trouvé :( Cliquez sur 'Nouvelle partie' !", "No games found :( Send a challenge!": "Aucune partie trouvée :( Envoyez un défi !", + "No identifier found: use the upload button in analysis mode": "Pas d'identifiant trouvé : utilisez le bouton d'envoi en mode analyse", "No more problems": "Plus de problèmes", "No subject. Send anyway?": "Pas de sujet. Envoyer quand-même ??", "Notifications by email": "Notifications par email", @@ -137,6 +139,7 @@ export const translations = { Time: "Temps", "Undetermined result": "Résultat indéterminé", Update: "Mise à jour", + "Upload a game": "Envoyer une partie", "User creation failed. Try again": "Ãchec de la création du compte. Réessayez", "User name": "Nom d'utilisateur", "User name or email already in use": "Nom d'utilisateur ou email déjà utilisés", diff --git a/client/src/utils/compgameStorage.js b/client/src/utils/compgameStorage.js index e30a69fb..655fd6bf 100644 --- a/client/src/utils/compgameStorage.js +++ b/client/src/utils/compgameStorage.js @@ -38,7 +38,7 @@ function dbOperation(callback) { export const CompgameStorage = { add: function(game) { - dbOperation((err,db) => { + dbOperation((err, db) => { if (err) return; let objectStore = db .transaction("compgames", "readwrite") @@ -49,7 +49,7 @@ export const CompgameStorage = { // obj: move and/or fen update: function(gameId, obj) { - dbOperation((err,db) => { + dbOperation((err, db) => { let objectStore = db .transaction("compgames", "readwrite") .objectStore("compgames"); @@ -70,7 +70,7 @@ export const CompgameStorage = { // Retrieve any game from its identifier (variant name) // NOTE: need callback because result is obtained asynchronously get: function(gameId, callback) { - dbOperation((err,db) => { + dbOperation((err, db) => { let objectStore = db .transaction("compgames", "readonly") .objectStore("compgames"); @@ -82,7 +82,7 @@ export const CompgameStorage = { // Delete a game in indexedDB remove: function(gameId) { - dbOperation((err,db) => { + dbOperation((err, db) => { if (!err) { db.transaction("compgames", "readwrite") .objectStore("compgames") diff --git a/client/src/utils/gameStorage.js b/client/src/utils/gameStorage.js index 62ad1fab..109c8251 100644 --- a/client/src/utils/gameStorage.js +++ b/client/src/utils/gameStorage.js @@ -53,7 +53,7 @@ function dbOperation(callback) { export const GameStorage = { // Optional callback to get error status add: function(game, callback) { - dbOperation((err,db) => { + dbOperation((err, db) => { if (!!err) { callback("error"); return; @@ -74,7 +74,7 @@ export const GameStorage = { // obj: chat, move, fen, clocks, score[Msg], initime, ... update: function(gameId, obj) { // live - dbOperation((err,db) => { + dbOperation((err, db) => { let objectStore = db .transaction("games", "readwrite") .objectStore("games"); @@ -98,7 +98,7 @@ export const GameStorage = { // Retrieve (all) running local games getRunning: function(callback) { - dbOperation((err,db) => { + dbOperation((err, db) => { let objectStore = db .transaction("games", "readonly") .objectStore("games"); @@ -125,7 +125,7 @@ export const GameStorage = { // Retrieve completed local games getNext: function(upperDt, callback) { - dbOperation((err,db) => { + dbOperation((err, db) => { let objectStore = db .transaction("games", "readonly") .objectStore("games"); @@ -157,11 +157,10 @@ export const GameStorage = { }); }, - // Retrieve any game from its identifiers (locally or on server) + // Retrieve any game from its identifier. // NOTE: need callback because result is obtained asynchronously get: function(gameId, callback) { - // Local game - dbOperation((err,db) => { + dbOperation((err, db) => { let objectStore = db.transaction("games").objectStore("games"); objectStore.get(gameId).onsuccess = function(event) { // event.target.result is null if game not found @@ -172,7 +171,7 @@ export const GameStorage = { // Delete a game in indexedDB remove: function(gameId, callback) { - dbOperation((err,db) => { + dbOperation((err, db) => { if (!err) { let transaction = db.transaction("games", "readwrite"); transaction.oncomplete = function() { diff --git a/client/src/utils/importgameStorage.js b/client/src/utils/importgameStorage.js new file mode 100644 index 00000000..e5ad6e22 --- /dev/null +++ b/client/src/utils/importgameStorage.js @@ -0,0 +1,113 @@ +// Game object struct: see gameStorgae.js + +import { store } from "@/store"; + +function dbOperation(callback) { + let db = null; + let DBOpenRequest = window.indexedDB.open("vchess_import", 4); + + DBOpenRequest.onerror = function(event) { + alert(store.state.tr[ + "Database error: stop private browsing, or update your browser"]); + callback("error", null); + }; + + DBOpenRequest.onsuccess = function() { + db = DBOpenRequest.result; + callback(null, db); + db.close(); + }; + + DBOpenRequest.onupgradeneeded = function(event) { + let db = event.target.result; + let upgradeTransaction = event.target.transaction; + let objectStore = undefined; + if (!db.objectStoreNames.contains("importgames")) + objectStore = db.createObjectStore("importgames", { keyPath: "id" }); + else + objectStore = upgradeTransaction.objectStore("importgames"); + if (!objectStore.indexNames.contains("created")) + // To search by date intervals. Two games could start at the same time + objectStore.createIndex("created", "created", { unique: false }); + }; +} + +export const ImportgameStorage = { + // Optional callback to get error status + add: function(game, callback) { + dbOperation((err, db) => { + if (!!err) { + callback("error"); + return; + } + let transaction = db.transaction("importgames", "readwrite"); + transaction.oncomplete = function() { + // Everything's fine + callback(); + }; + transaction.onerror = function(err) { + // Duplicate key error (most likely) + callback(err); + }; + transaction.objectStore("importgames").add(game); + }); + }, + + // Retrieve next imported games + getNext: function(upperDt, callback) { + dbOperation((err, db) => { + let objectStore = db + .transaction("importgames", "readonly") + .objectStore("importgames"); + let index = objectStore.index("created"); + const range = IDBKeyRange.upperBound(upperDt); + let games = []; + index.openCursor(range).onsuccess = function(event) { + let cursor = event.target.result; + if (!cursor) { + // Most recent games first: + games = games.sort((g1, g2) => g2.created - g1.created); + // TODO: 20 games showed per request is arbitrary + callback(games.slice(0, 20)); + } + else { + // If there is still another cursor to go, keep running this code + let g = cursor.value; + // Do not retrieve moves or clocks (unused in list mode) + g.movesCount = g.moves.length; + delete g.moves; + delete g.clocks; + delete g.initime; + games.push(g); + cursor.continue(); + } + }; + }); + }, + + // Retrieve any game from its identifier. + // NOTE: need callback because result is obtained asynchronously + get: function(gameId, callback) { + dbOperation((err, db) => { + let objectStore = + db.transaction("importgames").objectStore("importgames"); + objectStore.get(gameId).onsuccess = function(event) { + // event.target.result is null if game not found + callback(event.target.result); + }; + }); + }, + + // Delete a game in indexedDB + remove: function(gameId, callback) { + dbOperation((err, db) => { + if (!err) { + let transaction = db.transaction("importgames", "readwrite"); + transaction.oncomplete = function() { + callback(); //everything's fine + }; + transaction.objectStore("importgames").delete(gameId); + } + }); + } +}; diff --git a/client/src/views/Analyse.vue b/client/src/views/Analyse.vue index 1605b584..2f087b99 100644 --- a/client/src/views/Analyse.vue +++ b/client/src/views/Analyse.vue @@ -19,6 +19,8 @@ import BaseGame from "@/components/BaseGame.vue"; import { store } from "@/store"; export default { name: "my-analyse", + // TODO: game import ==> require some adjustments, like + // the ability to analyse from a list of moves... components: { BaseGame }, @@ -27,7 +29,6 @@ export default { return { st: store.state, gameRef: { - //given in URL (rid = remote ID) vname: "", fen: "" }, diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 68afce35..2a565f68 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -153,6 +153,7 @@ import BaseGame from "@/components/BaseGame.vue"; import Chat from "@/components/Chat.vue"; import { store } from "@/store"; import { GameStorage } from "@/utils/gameStorage"; +import { ImportgameStorage } from "@/utils/importgameStorage"; import { ppt } from "@/utils/datetime"; import { notify } from "@/utils/notifications"; import { ajax } from "@/utils/ajax"; @@ -1193,8 +1194,12 @@ export default { } } ); - } else - // Local game (or live remote) + } + else if (!!this.gameRef.match(/^I_/)) + // Game import (maybe remote) + ImportgameStorage.get(this.gameRef, callback); + else + // Local live game (or remote) GameStorage.get(this.gameRef, callback); }, re_setClocks: function() { diff --git a/client/src/views/MyGames.vue b/client/src/views/MyGames.vue index 3c3b9b5a..7522871f 100644 --- a/client/src/views/MyGames.vue +++ b/client/src/views/MyGames.vue @@ -7,6 +7,8 @@ main | {{ st.tr["Live games"] }} button.tabbtn#corrGames(@click="setDisplay('corr',$event)") | {{ st.tr["Correspondance games"] }} + button.tabbtn#importGames(@click="setDisplay('import',$event)") + | {{ st.tr["Imported games"] }} GameList( ref="livegames" v-show="display=='live'" @@ -21,16 +23,24 @@ main @show-game="showGame" @abortgame="abortGame" ) + GameList( + v-show="display=='import'" + ref="importgames" + :games="importGames" + @show-game="showGame" + ) button#loadMoreBtn( v-show="hasMore[display]" @click="loadMore(display)" ) | {{ st.tr["Load more"] }} + UploadGame(@game-uploaded="addGameImport") </template> <script> import { store } from "@/store"; import { GameStorage } from "@/utils/gameStorage"; +import { ImportgameStorage } from "@/utils/importgameStorage"; import { ajax } from "@/utils/ajax"; import { getScoreMessage } from "@/utils/scoring"; import params from "@/parameters"; @@ -39,7 +49,8 @@ import GameList from "@/components/GameList.vue"; export default { name: "my-my-games", components: { - GameList + GameList, + UploadGame }, data: function() { return { @@ -47,13 +58,19 @@ export default { display: "live", liveGames: [], corrGames: [], + importGames: [], // timestamp of last showed (oldest) game: cursor: { live: Number.MAX_SAFE_INTEGER, + "import": Number.MAX_SAFE_INTEGER, corr: Number.MAX_SAFE_INTEGER }, // hasMore == TRUE: a priori there could be more games to load - hasMore: { live: true, corr: store.state.user.id > 0 }, + hasMore: { + live: true, + "import": true, + corr: (store.state.user.id > 0) + }, conn: null, connexionString: "", socketCloseListener: 0 @@ -161,6 +178,13 @@ export default { elt.previousElementSibling.classList.remove("active"); else elt.nextElementSibling.classList.remove("active"); }, + addGameImport(game) { + if (!game.id) { + alert(this.st.tr[ + "No identifier found: use the upload button in analysis mode"]); + } + else this.importGames.push(game); + }, tryShowNewsIndicator: function(type) { if ( (type == "live" && this.display == "corr") || @@ -190,6 +214,7 @@ export default { socketMessageListener: function(msg) { if (!this.conn) return; const data = JSON.parse(msg.data); + // NOTE: no imported games here let gamesArrays = { "corr": this.corrGames, "live": this.liveGames @@ -240,7 +265,7 @@ export default { } }, showGame: function(game) { - if (game.type == "live" || !game.myTurn) { + if (game.type != "corr" || !game.myTurn) { this.$router.push("/game/" + game.id); return; } @@ -277,6 +302,7 @@ export default { ); } } + // NOTE: no imported games here else if (!game.deletedByWhite || !game.deletedByBlack) { // Set score if game isn't deleted on server: ajax( @@ -315,7 +341,8 @@ export default { } } ); - } else if (type == "live") { + } + else if (type == "live") { GameStorage.getNext(this.cursor["live"], localGames => { const L = localGames.length; if (L > 0) { @@ -328,6 +355,18 @@ export default { if (!!cb) cb(); }); } + else if (type == "import") { + ImportgameStorage.getNext(this.cursor["import"], importGames => { + const L = importGames.length; + if (L > 0) { + // Add "-1" because IDBKeyRange.upperBound includes boundary + this.cursor["import"] = importGames[L - 1].created - 1; + importGames.forEach(g => g.type = "import"); + this.importGames = this.importGames.concat(importGames); + } else this.hasMore["import"] = false; + if (!!cb) cb(); + }); + } } } }; -- 2.44.0