From 4eba2494e8b3d8fe3026947e092993645cc6bde6 Mon Sep 17 00:00:00 2001 From: Dan Wilt Date: Sun, 18 Feb 2018 08:50:12 +0000 Subject: [PATCH] sorting out mapping --- .prettierrc | 3 +- firebaseFunctions/firestore.js | 50 +++-- firebaseFunctions/index.js | 28 ++- public/index.html | 2 +- questions.json | 126 +++++++++--- .../QuestionAnswer.component.js | 36 ++-- src/components/Quiz/Quiz.component.js | 30 +-- src/components/Quiz/Quiz.container.js | 5 +- .../QuizQuestion/QuizQuestion.component.js | 67 +++---- src/registerServiceWorker.js | 179 +++++++++--------- 10 files changed, 320 insertions(+), 206 deletions(-) diff --git a/.prettierrc b/.prettierrc index 613af3b..6941be0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,4 @@ { - "arrow-parens": "always" + "arrow-parens": "always", + "tabWidth": 4 } diff --git a/firebaseFunctions/firestore.js b/firebaseFunctions/firestore.js index 966914a..af21ec4 100644 --- a/firebaseFunctions/firestore.js +++ b/firebaseFunctions/firestore.js @@ -3,24 +3,44 @@ const admin = require(`firebase-admin`); admin.initializeApp(functions.config().firebase); -export async function getAllQuestions() { - return admin - .firestore() - .collection(`questions`) - .get(); +export async function addAnswer({ email, question, answer, version }) { + return admin + .firestore() + .collection(`results`) + .doc(email) + .collection(`results`) + .doc(version) + .set({ + [question]: answer + }); } -export async function createQuestion(question) { - return admin - .firestore() - .collection(`questions`) - .add(question); +export async function getAllQuestions(version) { + return admin + .firestore() + .collection(`questions`) + .doc(version) + .collection(`questions`) + .get(); } -export async function getQuestion(id) {} +export async function addQuestion({ question, version }) { + await admin + .firestore() + .collection(`questions`) + .doc(version) + .set( + { + [question.id]: question + }, + { + merge: true + } + ); +} -export async function deleteAllQuestions() { - return getAllQuestions().then(({ docs }) => - Promise.all(docs.map((doc) => doc.ref.delete())) - ); +export async function deleteAllQuestions(version) { + return getAllQuestions(version).then(({ docs }) => + Promise.all(docs.map((doc) => doc.ref.delete())) + ); } diff --git a/firebaseFunctions/index.js b/firebaseFunctions/index.js index c1e8746..ef9c04d 100644 --- a/firebaseFunctions/index.js +++ b/firebaseFunctions/index.js @@ -1,15 +1,29 @@ const functions = require(`firebase-functions`); -import { deleteAllQuestions, createQuestion } from './firestore'; +import { + deleteAllQuestions, + addQuestion, + addAnswer, +} from "./firestore"; + +exports.addAnswer = functions.https.onRequest(async (request, response) => { + const { email, question, answer, version } = request.body; + + await addAnswer({ email, question, answer, version }); + + response.send(`Answer added!`); +}); exports.createQuestions = functions.https.onRequest( - async (request, response) => { - const questions = request.body; + async (request, response) => { + const { version, questions } = request.body; - await deleteAllQuestions(questions); + await deleteAllQuestions(version); - await Promise.all(questions.map((question) => createQuestion(question))); + await Promise.all( + questions.map((question) => addQuestion({ question, version })) + ); - response.send(`Questions updated!`); - } + response.send(`Questions updated for version ${version}!`); + } ); diff --git a/public/index.html b/public/index.html index 63ed778..b22b479 100644 --- a/public/index.html +++ b/public/index.html @@ -6,7 +6,7 @@ diff --git a/questions.json b/questions.json index 4341434..7d54f24 100644 --- a/questions.json +++ b/questions.json @@ -1,27 +1,99 @@ -[ - { - "id": "basic-variable", - "question": "Select the answer below that would set a variable named `myVariable` to `3`", - "incorrectFeedback": "This a very basic question and if this answer wasn't on accident, the rest of the quiz is probably going to be very difficult. Start by reading up on the [fundamentals of Javascript](https://www.w3schools.com/js/)", - "codeFigure":"var name = \"Bill\";\n\nfunction caller(func){\n var myObject = {\n name: \"Joe\"\n};\n\n return func(myObject);\n}\n\n function callee(param) {\n return param.name\n}\n\nvar c = caller(callee);", - "answers": [ - { - "type": "code", - "text": "myVariable(3)" - }, - { - "type": "code", - "text": "var myVariable = 3;" - }, - { - "type": "code", - "text": "var myVariable[3]" - }, - { - "type": "code", - "text": "var myVariable = {\n 3\n}" - } - ], - "correctAnswer": 1 - } -] +{ + "version": "1", + "questions": [ + { + "id": "basic-variable", + "question": "Select the answer below that would set a variable named `myVariable` to `3`:", + "incorrectFeedback": "This a very basic question and if this answer wasn't on accident, the rest of the quiz is probably going to be very difficult. Start by reading up on the [fundamentals of Javascript](https://www.w3schools.com/js/)", + "answers": [ + { + "type": "code", + "text": "myVariable(3)" + }, + { + "type": "code", + "text": "var myVariable = 3;" + }, + { + "type": "code", + "text": "var myVariable[3]" + }, + { + "type": "code", + "text": "var myVariable = {\n 3\n}" + } + ], + "correctAnswer": 1 + }, + { + "id": "null-vs-undefined", + "question": "What is the difference between `null` and `undefined`?", + "incorrectFeedback": "There are a surprising number of developers who don't know this. Burn the difference into memory because it's extremely important to understand.", + "answers": [ + { + "type": "markdown", + "text": "`null` is a blank value that has to be assigned where `undefined` is assigned to any variable/property which hasn't been given a value" + }, + { + "type": "markdown", + "text": "`null` is equivalent to `0` where `undefined` is a falsey value of `0`" + }, + { + "type": "markdown", + "text": "`null` is equivalent to `false` when comparing objects and `undefined` is equivalent to `false` when comparing numbers" + }, + { + "type": "markdown", + "text": "There is no difference" + } + ], + "correctAnswer": 0 + }, + { + "id": "variable-pointers", + "question": "What does `y` equal and why?", + "incorrectFeedback": "This is another thing that trips up developers. The key to remember here is that variables are just *pointers*. When `x` gets set to `3`, `y` is still pointing at the *value* of `x` - not `x` itself.", + "codeFigure":"var x = 4;\nvar y = x;\nx = 3;", + "answers": [ + { + "type": "markdown", + "text": "`3`: Because `y` is a reference to `x` and `x` is `3`" + }, + { + "type": "markdown", + "text": "`undefined`: Because `y` was set to `x` but then `x` changed so now `y` is a dead reference" + }, + { + "type": "markdown", + "text": "`4`: Because `y` was set to the value of `x`, not `x` itself" + } + ], + "correctAnswer": 2 + }, + { + "id": "invoke-function", + "question": "Invoke/call the function:", + "incorrectFeedback": "This is another basic question that you should be able to answer if you're going to be doing Javascript development.", + "codeFigure":"function myFunction(){\n\n}", + "answers": [ + { + "type": "markdown", + "text": "`(myFunction)`" + }, + { + "type": "markdown", + "text": "`myFunction||`" + }, + { + "type": "markdown", + "text": "`myFunction()`" + }, + { + "type": "markdown", + "text": "`|myFunction|`" + } + ], + "correctAnswer": 2 + } + ] +} diff --git a/src/components/QuestionAnswer/QuestionAnswer.component.js b/src/components/QuestionAnswer/QuestionAnswer.component.js index 45eab28..9abebeb 100644 --- a/src/components/QuestionAnswer/QuestionAnswer.component.js +++ b/src/components/QuestionAnswer/QuestionAnswer.component.js @@ -9,22 +9,22 @@ import PropTypes from "prop-types"; import "./QuestionAnswer.css"; export default class QuestionAnswer extends PureComponent { - static propTypes = { - text: PropTypes.string.isRequired, - type: PropTypes.oneOf([`code`, `markdown`]), - }; - - static defaultProps = { - type: `markdown`, - }; - - render() { - const { type, text } = this.props; - - return type === `code` ? ( - {text} - ) : ( - {text} - ); - } + static propTypes = { + text: PropTypes.string.isRequired, + type: PropTypes.oneOf([`code`, `markdown`]), + }; + + static defaultProps = { + type: `markdown`, + }; + + render() { + const { type, text } = this.props; + + return type === `code` ? ( + {text} + ) : ( + {text} + ); + } } diff --git a/src/components/Quiz/Quiz.component.js b/src/components/Quiz/Quiz.component.js index 09ab399..b31d8c6 100644 --- a/src/components/Quiz/Quiz.component.js +++ b/src/components/Quiz/Quiz.component.js @@ -9,21 +9,21 @@ import PropTypes from "prop-types"; import "./Quiz.css"; export default class Quiz extends PureComponent { - static propTypes = { - questions: PropTypes.object, - }; + static propTypes = { + questions: PropTypes.object, + }; - render() { - const { questions } = this.props; + render() { + const { questions } = this.props; - return questions && !isEmptyObject(questions) ? ( -
- {Object.values(questions).map((question) => ( -
- -
- ))} -
- ) : null; - } + return questions && !isEmptyObject(questions) ? ( +
+ {Object.values(questions).map((question) => ( +
+ +
+ ))} +
+ ) : null; + } } diff --git a/src/components/Quiz/Quiz.container.js b/src/components/Quiz/Quiz.container.js index 1ff6936..5375bd0 100644 --- a/src/components/Quiz/Quiz.container.js +++ b/src/components/Quiz/Quiz.container.js @@ -7,8 +7,9 @@ import { firestoreConnect } from "react-redux-firebase"; import Quiz from "./Quiz.component"; export default compose( - firestoreConnect([`questions`]), + firestoreConnect([`questions/1`]), connect((state) => ({ - questions: state.firestore.data.questions, + questions: + state.firestore.data.questions && state.firestore.data.questions[1], })) )(Quiz); diff --git a/src/components/QuizQuestion/QuizQuestion.component.js b/src/components/QuizQuestion/QuizQuestion.component.js index 7d9879c..d8d1615 100644 --- a/src/components/QuizQuestion/QuizQuestion.component.js +++ b/src/components/QuizQuestion/QuizQuestion.component.js @@ -13,36 +13,39 @@ import PropTypes from "prop-types"; import "./QuizQuestion.css"; export default class QuizQuestion extends PureComponent { - static propTypes = { - codeFigure: PropTypes.string, - question: PropTypes.string.isRequired, - answers: PropTypes.array, - incorrectFeedback: PropTypes.node, - }; - - render() { - const { codeFigure, question, answers } = this.props; - - return ( -
- {codeFigure && ( -
- {codeFigure} -
- )} -
- {question} -
-
    - {answers.map((answer = {}, i) => { - return ( -
  1. - -
  2. - ); - })} -
-
- ); - } + static propTypes = { + id: PropTypes.string.isRequired, + codeFigure: PropTypes.string, + question: PropTypes.string.isRequired, + answers: PropTypes.array.isRequired, + incorrectFeedback: PropTypes.node, + }; + + render() { + const { codeFigure, question, answers } = this.props; + + return ( +
+ {codeFigure && ( +
+ + {codeFigure} + +
+ )} +
+ {question} +
+
    + {answers.map((answer = {}, i) => { + return ( +
  1. + +
  2. + ); + })} +
+
+ ); + } } diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js index d97bb39..38164f2 100644 --- a/src/registerServiceWorker.js +++ b/src/registerServiceWorker.js @@ -9,109 +9,112 @@ // This link also includes instructions on opting out of this behavior. const isLocalhost = Boolean( - window.location.hostname === `localhost` || - // [::1] is the IPv6 localhost address. - window.location.hostname === `[::1]` || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) + window.location.hostname === `localhost` || + // [::1] is the IPv6 localhost address. + window.location.hostname === `[::1]` || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) ); export default function register() { - if (process.env.NODE_ENV === `production` && `serviceWorker` in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 - return; - } + if (process.env.NODE_ENV === `production` && `serviceWorker` in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 + return; + } - window.addEventListener(`load`, () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + window.addEventListener(`load`, () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - if (isLocalhost) { - // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl); + if (isLocalhost) { + // This is running on localhost. Lets check if a service worker still exists or not. + checkValidServiceWorker(swUrl); - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - `This web app is being served cache-first by a service ` + - `worker. To learn more, visit https://goo.gl/SC7cgQ` - ); + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + `This web app is being served cache-first by a service ` + + `worker. To learn more, visit https://goo.gl/SC7cgQ` + ); + }); + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl); + } }); - } else { - // Is not local host. Just register service worker - registerValidSW(swUrl); - } - }); - } + } } function registerValidSW(swUrl) { - navigator.serviceWorker - .register(swUrl) - .then((registration) => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - installingWorker.onstatechange = () => { - if (installingWorker.state === `installed`) { - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and - // the fresh content will have been added to the cache. - // It's the perfect time to display a "New content is - // available; please refresh." message in your web app. - console.log(`New content is available; please refresh.`); - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log(`Content is cached for offline use.`); - } - } - }; - }; - }) - .catch((error) => { - console.error(`Error during service worker registration:`, error); - }); + navigator.serviceWorker + .register(swUrl) + .then((registration) => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === `installed`) { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log( + `New content is available; please refresh.` + ); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log(`Content is cached for offline use.`); + } + } + }; + }; + }) + .catch((error) => { + console.error(`Error during service worker registration:`, error); + }); } function checkValidServiceWorker(swUrl) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then((response) => { - // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get(`content-type`).indexOf(`javascript`) === -1 - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then((registration) => { - registration.unregister().then(() => { - window.location.reload(); - }); + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then((response) => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get(`content-type`).indexOf(`javascript`) === + -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then((registration) => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl); + } + }) + .catch(() => { + console.log( + `No internet connection found. App is running in offline mode.` + ); }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl); - } - }) - .catch(() => { - console.log( - `No internet connection found. App is running in offline mode.` - ); - }); } export function unregister() { - if (`serviceWorker` in navigator) { - navigator.serviceWorker.ready.then((registration) => { - registration.unregister(); - }); - } + if (`serviceWorker` in navigator) { + navigator.serviceWorker.ready.then((registration) => { + registration.unregister(); + }); + } }