Skip to content

Commit

Permalink
feat: uses netlify functions and firestore for lib templates
Browse files Browse the repository at this point in the history
  • Loading branch information
Clive-Ward-Cron committed Dec 3, 2024
1 parent 08870d5 commit 2f4d4b7
Show file tree
Hide file tree
Showing 11 changed files with 17,209 additions and 14 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/js/firebase/firebase-config.json
/seed
Dockerfile
.dockerignore
# Local Netlify folder
.netlify
*.env*
*node_modules
32 changes: 32 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "netlify dev",
"type": "node",
"request": "launch",
"skipFiles": ["<node_internals>/**"],
"outFiles": ["${workspaceFolder}/.netlify/functions-serve/**/*.js"],
"program": "${workspaceFolder}/node_modules/.bin/netlify",
"args": ["dev"],
"console": "integratedTerminal",
"env": { "BROWSER": "none" },
"serverReadyAction": {
"pattern": "Server now ready on (https?://[\\w:.-]+)",
"uriFormat": "%s",
"action": "debugWithChrome"
}
},
{
"name": "netlify functions:serve",
"type": "node",
"request": "launch",
"skipFiles": ["<node_internals>/**"],
"outFiles": ["${workspaceFolder}/.netlify/functions-serve/**/*.js"],
"program": "${workspaceFolder}/node_modules/.bin/netlify",
"args": ["functions:serve"],
"console": "integratedTerminal"
}
]
}

9 changes: 4 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ <h2 class="title">Welcome to Libzreader!</h2>
<p class="libz">
Libzreader is my take on a classic word game that I enjoyed playing as a kid. Follow the word prompts to
build your own wacky story, then click the "READ" button to have your device read your story aloud to
you!<br /><br />This project uses Speech Synthesis from the Web Speech API and an external API from
<a href="https://github.com/HermanFassett/madlibz" target="_blank">github.com/HermanFassett/madlibz</a> for
the stories and prompts.
you!<br /><br />This project uses Speech Synthesis from the Web Speech API with Netlify Functions for accessing
the stories and prompts stored in Google's Firebase Firestore.
</p>
</div>
<div class="prompt-container">
Expand All @@ -46,9 +45,9 @@ <h2 class="title">Welcome to Libzreader!</h2>
</div>
</main>
<footer>
&copy; Copyright <span id="copy-year">2021</span>
&copy; Copyright <span id="copy-year">2024</span>
<a href="https://www.ward-cron.design" target="_blank">Clive Ward-Cron</a>
</footer>
<script src="main.js"></script>
<script src="js/main.js" type="module"></script>
</body>
</html>
47 changes: 38 additions & 9 deletions main.js → js/main.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Intro text for the website
const introTitle = "Welcome to Libzreader!";
const introContent = `Libzreader is my take on a classic word game that I enjoyed playing as a kid. Follow the word prompts to build your own wacky story, then click the "READ" button to have your device read your story aloud to you!<br><br>This project uses Speech Synthesis from the Web Speech API and an external API from <a href="https://github.com/HermanFassett/madlibz" target="_blank">github.com/HermanFassett/madlibz</a> for the stories and prompts.`;
const introContent = `Libzreader is my take on a classic word game that I enjoyed playing as a kid. Follow the word prompts to build your own wacky story, then click the "READ" button to have your device read your story aloud to you!<br><br>This project uses Speech Synthesis from the Web Speech API with Netlify Functions for accessing
the stories and prompts stored in Google's Firebase Firestore.`;

// Elements that are used and modified
const enterBtn = document.querySelector("#enter");
Expand All @@ -14,6 +15,7 @@ const titleEl = document.querySelector(".title");
const libz = document.querySelector(".libz");
const libzreader = document.querySelector(".libzreader");
const copyYear = document.querySelector("#copy-year");
const count = sessionStorage.getItem("templateCount") ?? await getCount()

// The utterance object for speech synth
const utterance = new SpeechSynthesisUtterance();
Expand All @@ -23,7 +25,7 @@ const delay = 250;
const vowels = ["a", "e", "i", "o", "u"];
let words = [];
let speech = [];
let lib, blanks, title, value;
let lib, blanks, title, value, voices;
let isReading = false;
let allWordsEntered = false;
// To track what el needs the click event fired on a keyup
Expand Down Expand Up @@ -326,13 +328,12 @@ function setVoice() {
async function fetchLib() {
readBtn.setAttribute("disabled", "");
try {
const res = await fetch("https://madlibz.herokuapp.com/api/random");
const body = await res.json();
if (body.title === "Hello ____!") return fetchLib();
blanks = [...body.blanks];
title = body.title;
value = body.value;
return body;
const template = await getTemplate()

blanks = [...template.blanks];
title = template.title;
value = template.value;
return template;
} catch (e) {
console.log(e);
titleEl.innerText = "Unable to Fetch Lib";
Expand All @@ -341,6 +342,32 @@ async function fetchLib() {
}
}

async function getTemplate() {
const randomTemplateId = Math.floor((Math.random() * count) + 1)
let template = JSON.parse(localStorage.getItem(`templateId${randomTemplateId}`))
if (!template) {
const response = await fetch(".netlify/functions/getTemplate", {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: randomTemplateId
})
})
template = await response.json();
localStorage.setItem(`templateId${randomTemplateId}`, JSON.stringify(template))
}
return template
}

async function getCount() {
const response = await fetch('.netlify/functions/getTemplateCount')
const count = await response.json()
sessionStorage.setItem("templateCount",count)
return count
}

// ----------- Event listeners -----------
// triggers button animation
wordInput.addEventListener("keydown", btnToggle);
Expand All @@ -367,6 +394,7 @@ if (speechSynthesis.onvoiceschanged !== undefined) {
// Fetches an inital lib to start with on page load.
(async () => {
try {

speechSynthesis.cancel();
copyYear.innerText = new Date().getFullYear();
lib = await fetchLib();
Expand All @@ -382,6 +410,7 @@ if (speechSynthesis.onvoiceschanged !== undefined) {
}
});
} catch (e) {
console.log(e)
return;
}
})();
2 changes: 2 additions & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[functions]
node_bundler = "esbuild"
32 changes: 32 additions & 0 deletions netlify/functions/firebase/init.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
import { getFirestore, collection, getDocs, getCountFromServer, query, where, limit } from 'firebase/firestore'

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional

const firebaseConfig = {
apiKey: process.env.API_KEY,
authDomain: process.env.AUTH_DOMAIN,
projectId: process.env.PROJECT_ID,
storageBucket: process.env.STORAGE_BUCKET,
messagingSenderId: process.env.MESSAGING_SENDER_ID,
appId: process.env.APP_ID,
measurementId: process.env.MEASUREMENT_ID,
}

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

export {
db,
collection,
getDocs,
getCountFromServer,
query,
where,
limit,
}
15 changes: 15 additions & 0 deletions netlify/functions/getTemplate.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { db, collection, getDocs, getCountFromServer, query, where, limit } from "./firebase/init.mjs"

export default async function getTemplate(request) {
const data = await request.json()
const templatesRef = collection(db, "templates")
const q = query(templatesRef, where("id", "==", data.id), limit(1))
const querySnapshot = await getDocs(q)
const template = querySnapshot.docs.map(doc => doc.data())[0]

return new Response(JSON.stringify(template))
}

export const config = {
// path: "/api/getTemplate" //! Can't use this until Netilfy fixes their CLI
}
9 changes: 9 additions & 0 deletions netlify/functions/getTemplateCount.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { db, collection, getCountFromServer } from "./firebase/init.mjs"

export default async function getTemplateCount() {
const templatesRef = collection(db, "templates")
const snapshot = await getCountFromServer(templatesRef)
const count = snapshot.data().count

return new Response(count)
}
7 changes: 7 additions & 0 deletions netlify/functions/getTemplates.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { db, collection } from "./firebase/init.mjs"
export default async function getTemplates() {
const templatesRef = collection(db, 'templates');
const templateSnapShot = await getDocs(templatesRef);
const templateList = templateSnapShot.docs.map(doc => doc.data())
return new Response(JSON.stringify(templateList));
}
Loading

0 comments on commit 2f4d4b7

Please sign in to comment.