From 35027e775aaa596ead191f6c9ca96b993fbdd781 Mon Sep 17 00:00:00 2001 From: Art Chaidarun Date: Sat, 1 Jun 2024 03:59:33 -0400 Subject: [PATCH] Use async/await --- package.json | 4 +- src/actions.js | 5 +- src/util/api.js | 185 ++++++++++++++++++---------------------- webpack.config.babel.js | 8 +- 4 files changed, 94 insertions(+), 108 deletions(-) diff --git a/package.json b/package.json index 631be77..3c2b781 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "babel-core": "6.3.21", "babel-loader": "6.2.0", "babel-polyfill": "6.3.14", - "babel-preset-es2015": "6.3.13", + "babel-preset-es2017": "6.24.1", "babel-preset-react": "6.3.13", "chai": "3.4.1", "classnames": "2.2.1", @@ -24,7 +24,7 @@ }, "babel": { "presets": [ - "es2015", + "es2017", "react" ] }, diff --git a/src/actions.js b/src/actions.js index 71080dc..a25ccea 100644 --- a/src/actions.js +++ b/src/actions.js @@ -47,10 +47,11 @@ export const hydrateNewQuestion = initForNewUser => (dispatch, getState) => { track("GET_QUESTION"); return dispatch(hydrate(hydrateState, true)); }; - getQuestion(bankSize, seenQuestions, question => { + (async () => { + const question = await getQuestion(bankSize, seenQuestions); currentQuestion = question; !waiting && dispatchHydrate(); - }); + })(); delay && window.setTimeout(() => { if (currentQuestion) { diff --git a/src/util/api.js b/src/util/api.js index c6e5d98..2a3ae83 100644 --- a/src/util/api.js +++ b/src/util/api.js @@ -17,7 +17,7 @@ const normalizeString = html => { .trim(); }; -// Fisher-Yates +/** Fisher-Yates shuffle */ const shuffleString = string => { const a = string.split(""); const n = a.length; @@ -29,105 +29,90 @@ const shuffleString = string => { return a.join(""); }; -const LETTERS = `${"AEHINORST".repeat(3)}${"BCDFGKLMPUVWY".repeat(2)}JXQZ`; -const DIGITS = "0123456789"; - -export const getQuestion = (bankSize, seenQuestions, callback) => { - const retry = () => - window.setTimeout( - () => getQuestion(bankSize, seenQuestions, callback), - 125 - ); - const url = - "https://corsproxy.io/?" + - encodeURIComponent( - // Add timestamp to defeat corsproxy.io caching - "http://cluebase.lukelav.in/clues/random?_=" + Date.now() - ); - fetch(url) - .then(response => response.json()) - .then(({ data: [question] }) => { - let { - category, - clue: prompt, - id, - response: solution, - value: difficulty, - } = question; - if ([100, 300, 500].includes(difficulty)) { - // TODO: Normalize clue values based on the threshold date 2001-11-26, - // when Jeopardy doubled all clue values. jService included airdate in - // clue API repsonses, but Cluebase requires an additional API call for - // it :/ For now we just normalize only clues whose values imply that - // they must have aired before the threshold date - difficulty *= 2; - } - solution = normalizeString(solution) - .split("/")[0] - .replace(/\(.+\)/g, "") - .replace(/^an? /, "") - .replace(/\s+/g, " ") - .replace(/ { + const url = `https://corsproxy.io/?${encodeURIComponent( + // Add timestamp to defeat corsproxy.io caching + `http://cluebase.lukelav.in/clues/random?_=${Date.now()}` + )}`; + const response = await (await fetch(url)).json(); + let { + category, + clue: prompt, + id, + response: solution, + value: difficulty, + } = response.data[0]; + if ([100, 300, 500].includes(difficulty)) { + // TODO: Normalize clue values based on the threshold date 2001-11-26, + // when Jeopardy doubled all clue values. jService included airdate in + // clue API repsonses, but Cluebase requires an additional API call for + // it :/ For now we just normalize only clues whose values imply that + // they must have aired before the threshold date + difficulty *= 2; + } + solution = normalizeString(solution) + .split("/")[0] + .replace(/\(.+\)/g, "") + .replace(/^an? /, "") + .replace(/\s+/g, " ") + .replace(/= 400 && - difficulty <= 1600 && - difficulty !== 1000 && - prompt.length <= 140 && - solution.match(/\w/g).length <= bankSize && - // Avoid multiple-choice clues because they're too easy - !prompt.toLowerCase().includes(solution.toLowerCase()) && - !/\((cheryl|im|jimmy|jon|kelly|sarah|sofia) |(audio|video) clue|clue crew|following clip|(heard|seen) here/i.test( - prompt - ) && - !seenQuestions.includes(id); - if (!isValid) { - console.log( - "API call returned invalid data. Retry scheduled.", - question - ); - retry(); - return; - } + // Check question validity + const isValid = + difficulty >= 400 && + difficulty <= 1600 && + difficulty !== 1000 && + prompt.length <= 140 && + solution.match(/\w/g).length <= bankSize && + // Avoid multiple-choice clues because they're too easy + !prompt.toLowerCase().includes(solution.toLowerCase()) && + !/\((cheryl|im|jimmy|jon|kelly|sarah|sofia) |(audio|video) clue|clue crew|following clip|(heard|seen) here/i.test( + prompt + ) && + !seenQuestions.includes(id); + if (!isValid) { + console.log("API call returned invalid data. Retry scheduled.", response); + await new Promise(resolve => setTimeout(resolve, 125)); + return await getQuestion(bankSize, seenQuestions); + } - console.log(`API call succeeded. Solution: ${solution}`); - solution = solution.toUpperCase(); - const tileString = (() => { - const solutionChars = solution.match(/\w/g); - let tileString = solutionChars.join(""); - const digitRatio = - (tileString.match(/\d/g) || []).length / tileString.length; - for (let i = 0; i < bankSize - solutionChars.length; ++i) { - const pool = Math.random() < digitRatio ? DIGITS : LETTERS; - tileString += pool[Math.floor(Math.random() * pool.length)]; - } - return shuffleString(tileString); - })(); - const RUN_DELIMITER = '@#"'; - const filteredSolution = solution.replace(/[^\w]/g, ""); - callback({ - category, - difficulty, - filteredSolution, - guessTileIds: new Array(filteredSolution.length).fill(null), - id, - prompt, - selectedTileId: 0, - solutionRuns: solution - .trim() - .replace(/(\w+)/g, `${RUN_DELIMITER}$1${RUN_DELIMITER}`) - .split(RUN_DELIMITER) - .filter(run => run.length) - .map(run => (run.charAt(0).match(/\w/) ? run.length : run)), - solved: false, - tiles: tileString - .split("") - .map((char, id) => ({ char, id, used: false })), - tileString, - }); - }); + console.log(`API call succeeded. Solution: ${solution}`); + solution = solution.toUpperCase(); + const tileString = (() => { + const solutionChars = solution.match(/\w/g); + let tileString = solutionChars.join(""); + const digitRatio = + (tileString.match(/\d/g) || []).length / tileString.length; + for (let i = 0; i < bankSize - solutionChars.length; ++i) { + const pool = + Math.random() < digitRatio + ? "0123456789" + : `${"AEHINORST".repeat(3)}${"BCDFGKLMPUVWY".repeat(2)}JXQZ`; + tileString += pool[Math.floor(Math.random() * pool.length)]; + } + return shuffleString(tileString); + })(); + const RUN_DELIMITER = '@#"'; + const filteredSolution = solution.replace(/[^\w]/g, ""); + return { + category, + difficulty, + filteredSolution, + guessTileIds: new Array(filteredSolution.length).fill(null), + id, + prompt, + selectedTileId: 0, + solutionRuns: solution + .trim() + .replace(/(\w+)/g, `${RUN_DELIMITER}$1${RUN_DELIMITER}`) + .split(RUN_DELIMITER) + .filter(run => run.length) + .map(run => (run.charAt(0).match(/\w/) ? run.length : run)), + solved: false, + tiles: tileString.split("").map((char, id) => ({ char, id, used: false })), + tileString, + }; }; diff --git a/webpack.config.babel.js b/webpack.config.babel.js index 0d20ff1..09bc57e 100644 --- a/webpack.config.babel.js +++ b/webpack.config.babel.js @@ -1,6 +1,6 @@ -import cssnano from "cssnano"; -import ExtractTextPlugin from "extract-text-webpack-plugin"; -import webpack from "webpack"; +const cssnano = require("cssnano"); +const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const webpack = require("webpack"); const WEBPACK_DEV_PORT = 9000; @@ -24,7 +24,7 @@ const envConfig = PROD /* Base config */ -export default { +module.exports = { debug: !PROD, devServer: { contentBase: `${__dirname}/dist`,