35
loading...
This website collects cookies to deliver better user experience
yarn create react-app trivia
cd trivia
yarn add @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── reportWebVitals.js
│ └── setupTests.js
└── yarn.lock
yarn add faunadb
npm install -g fauna-shell
$ fauna cloud-login
For email login, enter your email below, and then your password.
For login with 3rd-party identity providers like Github or Netlify, please acquire a key from
Dashboard > Security and enter it below instead.
Email: [email protected]
Password: **********
fauna create-database trivia
$ fauna shell trivia
Starting shell for database trivia
Connected to https://db.fauna.com
Type Ctrl+D or .exit to exit the shell
trivia>
trivia> CreateCollection({ name: "questions" })
trivia> CreateCollection({ name: "answers" })
trivia> CreateCollection({ name: "scores" })
qna> CreateIndex({
name: "unique_question_user",
unique: true,
serialized: true,
source: Collection("answers"),
terms: [
{
field: ["data", "user_id"]
},
{
field: ["data", "question_id"]
}
]
})
#SAMPLE RESPONSE…...
{
ref: Index("unique_question_user"),
ts: 1610301037970000,
active: true,
serialized: true,
name: 'unique_question_user',
unique: true,
source: Collection("answers"),
terms: [
{ field: [ 'data', 'user_id' ] },
{ field: [ 'data', 'question_id' ] }
],
partitions: 1
}
CreateIndex({
name: "question_by_id",
source: Collection("questions"),
terms: [
{
field: ["data", "id"]
}
]
})
CreateIndex({
name: "score_by_user",
source: Collection("scores"),
terms: [
{
field: ["data", "user_id"]
}
]
})
yarn global add netlify-cli -g
CreateKey({
name: "trivia-app",
role: "server"
})
# Example result.
# NOTE: Make sure you copy and store the secret!
# {
# ref: Ref(Keys(), "280185139463529993"),
# ts: 1603464278974000,
# role: 'server',
# secret: '<FaunaDB secret key>’',
# hashed_secret: ...
# }
netlify env:set FAUNADB_SERVER_SECRET “<FaunaDB secret key>”
[build]
command = "npm run build"
functions = "functions/"
publish = "build"
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
force = true
const faunadb = require('faunadb');
const q = faunadb.query
const client = new faunadb.Client({
secret: process.env.FAUNADB_SERVER_SECRET,
});
module.exports = { client, q };
const { client, q } = require("../src/lib/fauna");
exports.handler = async (event, context) => {
try {
let { question, answer, options } = JSON.parse(event.body);
let results = await client.query(
q.Create(q.Collection("questions"), {data: { question, answer, options },}),
);
return {statusCode: 200, body: JSON.stringify({ id: results.ref.id, data: results.data }),};
} catch (err) {
return { statusCode: 500, body: JSON.stringify({ error: err.toString() }) };
}
};
const { client, q } = require("../src/lib/fauna");
exports.handler = async (event, context) => {
try {
let {id} = event.queryStringParameters
let results = await client.query(q.Get(q.Ref(q.Collection("questions"), id )));
return { statusCode: 200, body: JSON.stringify({ id: results.ref.id, data: results.data }),};
} catch (err) {
return { statusCode: 500, body: JSON.stringify({ error: err.toString() }) };
}
};
// Docs on event and context https://www.netlify.com/docs/functions/#the-handler-method
const { client, q } = require("../src/lib/fauna");
exports.handler = async (event, context) => {
try {
let { question_id, answer, user_id } = JSON.parse(event.body);
// ensure no missing values
if (!(question_id && answer && user_id)) {
return {
statusCode: 500,
body: JSON.stringify({
error: "Fields question_id & answer & user_id required ",
}),
};
}
let results = await client.query(
q.Get(q.Ref(q.Collection("questions"), question_id)),
);
let question = results.data;
let isCorrect = false;
if (question.answer === answer) isCorrect = true;
try {
let query = await client.query(
q.Create(q.Collection("answers"), {
data: {
question_id,
user_id,
isCorrect: isCorrect,
response: answer,
},
}),
);
query.data.correct = question.correct_answer;
if (isCorrect) {
// update the user's score if correct
try {
let score = await client.query(
q.Get(q.Ref(q.Collection("scores"), process.env.LEADERBOARD_ID)),
);
console.log("S", score,)
let req = await client.query(
q.Update(q.Ref(q.Collection("scores"), process.env.LEADERBOARD_ID), {
data: { [user_id]: ( (user_id in score.data) ? (score.data[user_id] + 10) : 10) },
}),
);
} catch (error) {
console.log(error)
return {
statusCode: 500, body: JSON.stringify({ error: error.toString() }),};
}
}
return {
statusCode: 200,
body: JSON.stringify({ ref: query.ref.id, data: query.data }),
};
} catch (error) {
if (error.message === "instance not unique") {
return {
statusCode: 500,
body: JSON.stringify({ error: "Question is already answered" }),
};
}
return {
statusCode: 500,
body: JSON.stringify({ error: error.toString() }),
};
}
} catch (err) {
return { statusCode: 500, body: JSON.stringify({ error: err.toString() }) };
}
};
import {Box, Stack, useMediaQuery} from '@chakra-ui/react'
import {useEffect, useState} from 'react'
import {query as q, Client} from 'faunadb'
import rw from 'random-words'
function App() {
let [isMobile] = useMediaQuery("(max-width:600px)");
let [leaderboard, setLeaderboard] = useState(null)
let client = new Client({
secret: process.env.REACT_APP_FAUNA_CLIENT_SECRET
})
let stream
const startStream = () => {
stream = client.stream.document(q.Ref(q.Collection('scores'), process.env.REACT_APP_LEADERBOARD_ID))
.on('snapshot', snapshot => {
console.log("S", snapshot)
setLeaderboard(snapshot.data)
})
.on('version', version => {
console.log("V", version)
setLeaderboard(version.document.data)
})
.on('error', error => {
console.log('Error:', error)
stream.close()
setTimeout(startStream, 1000)
})
.start()
}
useEffect(()=>{
if(! window.localStorage.getItem("user_id")){
window.localStorage.setItem("user_id", `${rw()}_${Math.floor((Math.random() * 999) + 900)}` )
}
startStream()
}, [])
return (
<div className="">
<Stack direction={isMobile ? "column" : "column"} p="64">
<h3>Leaderboard</h3>
{leaderboard && Object.keys(leaderboard).map((k)=>{
console.log(k,)
return <><h4>{`${k} ------------ ${leaderboard[k]}`}</h4><br/></>
})}
</Stack>
</div>
);
}
export default App;