diff --git a/CHANGELOG.md b/CHANGELOG.md index f0a1ea2..940886d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,3 +50,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bingo button not shown on refresh - bingo chat style (images too large) - backend now returns precise error messages +- setting words won't result in deleting all of them and resaving +- words can now only be set when no round is active diff --git a/routes/bingo.js b/routes/bingo.js index 4c53961..93cbc0e 100644 --- a/routes/bingo.js +++ b/routes/bingo.js @@ -236,6 +236,16 @@ class BingoDataManager { return await this._queryFirstResult(this.queries.addWord.sql, [lobbyId, word]); } + /** + * Removes a word from the lobby + * @param lobbyId {Number} - the id of the lobby + * @param wordId {Number} - the id of the word + * @returns {Promise<*>} + */ + async removeWordFromLobby(lobbyId, wordId) { + return await this._queryFirstResult(this.queries.removeLobbyWord.sql, [lobbyId, wordId]); + } + /** * Returns all words used in a lobby * @param lobbyId {Number} - the id of the lobby @@ -303,6 +313,15 @@ class BingoDataManager { return await this._queryFirstResult(this.queries.addGrid.sql, [playerId, lobbyId, roundId]); } + /** + * Clears all grids for a lobby. + * @param lobbyId {Number} - the id of the lobby + * @returns {Promise<*>} + */ + async clearGrids(lobbyId) { + return await this._queryFirstResult(this.queries.clearLobbyGrids.sql, [lobbyId]); + } + /** * Adds a word to a grid with specific location * @param gridId {Number} - the id of the gird @@ -1018,18 +1037,63 @@ class LobbyWrapper { return (result && result.player_id); } + /** + * Adds a word to the lobby + * @param word + * @returns {Promise} + */ + async addWord(word) { + await bdm.addWordToLobby(this.id, word); + } + + /** + * Removes a word from the lobby + * @param wordId + * @returns {Promise} + */ + async removeWord(wordId) { + await bdm.removeWordFromLobby(this.id, wordId); + } + /** * Sets the words of the lobby * @param words * @returns {Promise} */ async setWords(words) { - if (words.length > 0) { - await bdm.clearLobbyWords(this.id); - for (let word of words) - // eslint-disable-next-line no-await-in-loop - await bdm.addWordToLobby(this.id, word); + if (words.length > 0 && !await this.roundActive()) { + let {newWords, removedWords} = await this._filterWords(words); + for (let word of newWords) + await this.addWord(word); + for (let word of removedWords) + await this.removeWord(word.id); + } + } + + /** + * Filters the bingo words + * @param words + * @returns {Promise<{removedWords: *[], newWords: *[]}>} + * @private + */ + async _filterWords(words) { + let curWords = await this.words(); + let currentWords = []; + let currentWordContent = []; + for (let word of curWords) { + currentWordContent.push(await word.content()); + currentWords.push({ + id: word.id, + content: (await word.content()) + }); } + let newWords = words.filter(x => (!currentWordContent.includes(x))); + let removedWords = currentWords.filter(x => !words.includes(x.content)); + + return { + newWords: newWords, + removedWords: removedWords + }; } /** @@ -1074,6 +1138,9 @@ class LobbyWrapper { let currentRound = await this.currentRound(); await currentRound.updateStatus(status); await bdm.addInfoMessage(this.id, `Admin set round status to ${status}`); + + if (status === 'FINISHED') + await bdm.clearGrids(this.id); return currentRound; } } @@ -1463,6 +1530,7 @@ router.graphqlResolver = async (req, res) => { let username = await new PlayerWrapper(playerId).username(); if (result) { await bdm.addInfoMessage(lobbyId, `**${username}** won!`); + await bdm.clearGrids(lobbyId); return currentRound; } else { res.status(500); diff --git a/sql/bingo/createBingoTables.sql b/sql/bingo/createBingoTables.sql index c6d7c3b..c6061d7 100644 --- a/sql/bingo/createBingoTables.sql +++ b/sql/bingo/createBingoTables.sql @@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS bingo.grids ( -- grids_words table CREATE TABLE IF NOT EXISTS bingo.grid_words ( grid_id serial references bingo.grids(id) ON DELETE CASCADE, - word_id serial references bingo.words(id) ON DELETE CASCADE, + word_id serial references bingo.words(id) ON DELETE RESTRICT, grid_row integer NOT NULL, grid_column integer NOT NULL, submitted boolean DEFAULT false, diff --git a/sql/bingo/queries.yaml b/sql/bingo/queries.yaml index 2638aa2..1056fbf 100644 --- a/sql/bingo/queries.yaml +++ b/sql/bingo/queries.yaml @@ -160,6 +160,13 @@ addWord: clearLobbyWords: sql: DELETE FROM bingo.words WHERE lobby_id = $1; +# deletes a word of a lobby +# params: +# - {Number} - the id of the lobby +# - {Number} - the id of the word +removeLobbyWord: + sql: DELETE FROM bingo.words WHERE lobby_id = $1 AND id = $2; + # returns all words for a bingo game (lobby) # params: # - {Number} - the id of the bingo lobby @@ -180,6 +187,12 @@ getWordInfo: addGrid: sql: INSERT INTO bingo.grids (player_id, lobby_id, round_id) VALUES ($1, $2, $3) RETURNING *; +# deletes all grids of a lobby +# params: +# - {Number} - the id of the lobby +clearLobbyGrids: + sql: DELETE FROM bingo.grids WHERE lobby_id = $1; + # returns the grid entry for a player and lobby id # params: # - {Number} - the id of the player