diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..fa5b149 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "net-ninja-pwa" + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f626852 --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +firebase-debug.log* + +# Firebase cache +.firebase/ + +# Firebase config + +# Uncomment this if you'd like others to create their own Firebase project. +# For a team working on the same Firebase project(s), it is recommended to leave +# it commented so all members can deploy to the same project(s) in .firebaserc. +# .firebaserc + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..e782939 --- /dev/null +++ b/firebase.json @@ -0,0 +1,10 @@ +{ + "hosting": { + "public": "public", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ] + } +} diff --git a/js/ui.js b/js/ui.js deleted file mode 100644 index 2f45663..0000000 --- a/js/ui.js +++ /dev/null @@ -1,8 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - // nav menu - const menus = document.querySelectorAll('.side-menu'); - M.Sidenav.init(menus, {edge: 'right'}); - // add recipe form - const forms = document.querySelectorAll('.side-form'); - M.Sidenav.init(forms, {edge: 'left'}); -}); \ No newline at end of file diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..829eda8 --- /dev/null +++ b/public/404.html @@ -0,0 +1,33 @@ + + + + + + Page Not Found + + + + +
+

404

+

Page Not Found

+

The specified file was not found on this website. Please check the URL for mistakes and try again.

+

Why am I seeing this?

+

This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html file in your project's configured public directory.

+
+ + diff --git a/css/materialize.min.css b/public/css/materialize.min.css similarity index 100% rename from css/materialize.min.css rename to public/css/materialize.min.css diff --git a/css/styles.css b/public/css/styles.css similarity index 100% rename from css/styles.css rename to public/css/styles.css diff --git a/img/dish.png b/public/img/dish.png similarity index 100% rename from img/dish.png rename to public/img/dish.png diff --git a/img/icons/icon-128x128.png b/public/img/icons/icon-128x128.png similarity index 100% rename from img/icons/icon-128x128.png rename to public/img/icons/icon-128x128.png diff --git a/img/icons/icon-144x144.png b/public/img/icons/icon-144x144.png similarity index 100% rename from img/icons/icon-144x144.png rename to public/img/icons/icon-144x144.png diff --git a/img/icons/icon-152x152.png b/public/img/icons/icon-152x152.png similarity index 100% rename from img/icons/icon-152x152.png rename to public/img/icons/icon-152x152.png diff --git a/img/icons/icon-192x192.png b/public/img/icons/icon-192x192.png similarity index 100% rename from img/icons/icon-192x192.png rename to public/img/icons/icon-192x192.png diff --git a/img/icons/icon-384x384.png b/public/img/icons/icon-384x384.png similarity index 100% rename from img/icons/icon-384x384.png rename to public/img/icons/icon-384x384.png diff --git a/img/icons/icon-512x512.png b/public/img/icons/icon-512x512.png similarity index 100% rename from img/icons/icon-512x512.png rename to public/img/icons/icon-512x512.png diff --git a/img/icons/icon-72x72.png b/public/img/icons/icon-72x72.png similarity index 100% rename from img/icons/icon-72x72.png rename to public/img/icons/icon-72x72.png diff --git a/img/icons/icon-96x96.png b/public/img/icons/icon-96x96.png similarity index 100% rename from img/icons/icon-96x96.png rename to public/img/icons/icon-96x96.png diff --git a/index.html b/public/index.html similarity index 60% rename from index.html rename to public/index.html index f41437f..ce6c7ea 100644 --- a/index.html +++ b/public/index.html @@ -11,8 +11,9 @@ - + + @@ -39,46 +40,7 @@
-
- recipe thumb -
-
Edame Noodles
-
Edame Beans, Noodels, Garlic oil
-
-
- delete_outline -
-
-
- recipe thumb -
-
Edame Noodles
-
Edame Beans, Noodels, Garlic oil
-
-
- delete_outline -
-
-
- recipe thumb -
-
Edame Noodles
-
Edame Beans, Noodels, Garlic oil
-
-
- delete_outline -
-
-
- recipe thumb -
-
Edame Noodles
-
Edame Beans, Noodels, Garlic oil
-
-
- delete_outline -
-
+
@@ -106,6 +68,24 @@
New Recipe
+ + + + + + \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js new file mode 100644 index 0000000..12ded7a --- /dev/null +++ b/public/js/app.js @@ -0,0 +1,5 @@ +if('serviceWorker' in navigator){ + navigator.serviceWorker.register('/sw.js') + .then(reg => console.log('service worker registered')) + .catch(err => console.log('service worker not registered', err)); +} \ No newline at end of file diff --git a/public/js/db.js b/public/js/db.js new file mode 100644 index 0000000..0ef13d2 --- /dev/null +++ b/public/js/db.js @@ -0,0 +1,50 @@ +// enable offline data +db.enablePersistence() + .catch(function(err) { + if (err.code == 'failed-precondition') { + // probably multible tabs open at once + console.log('persistance failed'); + } else if (err.code == 'unimplemented') { + // lack of browser support for the feature + console.log('persistance not available'); + } + }); + +// real-time listener +db.collection('recipes').onSnapshot(snapshot => { + snapshot.docChanges().forEach(change => { + if(change.type === 'added'){ + renderRecipe(change.doc.data(), change.doc.id); + } + if(change.type === 'removed'){ + removeRecipe(change.doc.id); + } + }); +}); + +// add new recipe +const form = document.querySelector('form'); +form.addEventListener('submit', evt => { + evt.preventDefault(); + + const recipe = { + name: form.title.value, + ingredients: form.ingredients.value + }; + + db.collection('recipes').add(recipe) + .catch(err => console.log(err)); + + form.title.value = ''; + form.ingredients.value = ''; +}); + +// remove a recipe +const recipeContainer = document.querySelector('.recipes'); +recipeContainer.addEventListener('click', evt => { + if(evt.target.tagName === 'I'){ + const id = evt.target.getAttribute('data-id'); + //console.log(id); + db.collection('recipes').doc(id).delete(); + } +}) \ No newline at end of file diff --git a/js/materialize.min.js b/public/js/materialize.min.js similarity index 100% rename from js/materialize.min.js rename to public/js/materialize.min.js diff --git a/public/js/ui.js b/public/js/ui.js new file mode 100644 index 0000000..dd9237f --- /dev/null +++ b/public/js/ui.js @@ -0,0 +1,35 @@ +const recipes = document.querySelector('.recipes'); + +document.addEventListener('DOMContentLoaded', function() { + // nav menu + const menus = document.querySelectorAll('.side-menu'); + M.Sidenav.init(menus, {edge: 'right'}); + // add recipe form + const forms = document.querySelectorAll('.side-form'); + M.Sidenav.init(forms, {edge: 'left'}); +}); + +// render recipe data +const renderRecipe = (data, id) => { + + const html = ` +
+ recipe thumb +
+
${data.name}
+
${data.ingredients}
+
+
+ delete_outline +
+
+ `; + recipes.innerHTML += html; + +}; + +// remove recipe +const removeRecipe = (id) => { + const recipe = document.querySelector(`.recipe[data-id=${id}]`); + recipe.remove(); +}; \ No newline at end of file diff --git a/manifest.json b/public/manifest.json similarity index 100% rename from manifest.json rename to public/manifest.json diff --git a/pages/about.html b/public/pages/about.html similarity index 93% rename from pages/about.html rename to public/pages/about.html index b3345f3..421973d 100644 --- a/pages/about.html +++ b/public/pages/about.html @@ -10,8 +10,9 @@ - + + @@ -43,6 +44,7 @@
About Food Ninja

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam voluptatibus omnis, ea doloremque exercitationem id necessitatibus. Voluptatem officiis cupiditate commodi totam, hic laborum est ducimus amet iure, non dignissimos illo.

+ \ No newline at end of file diff --git a/pages/contact.html b/public/pages/contact.html similarity index 92% rename from pages/contact.html rename to public/pages/contact.html index cd55b80..b6b232c 100644 --- a/pages/contact.html +++ b/public/pages/contact.html @@ -10,8 +10,9 @@ - + + @@ -48,6 +49,7 @@
Find us at:
+ \ No newline at end of file diff --git a/public/pages/fallback.html b/public/pages/fallback.html new file mode 100644 index 0000000..ea7e057 --- /dev/null +++ b/public/pages/fallback.html @@ -0,0 +1,50 @@ + + + + + + Food Ninja + + + + + + + + + + + + + + + + + + + +
+
OOPS!
+

Currently you can't view this page without a connection.

+ Go to the Homepage +
+ + + + + \ No newline at end of file diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..af30971 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,73 @@ +const staticCacheName = 'site-static-v4'; +const dynamicCacheName = 'site-dynamic-v4'; +const assets = [ + '/', + '/index.html', + '/js/app.js', + '/js/ui.js', + '/js/materialize.min.js', + '/css/styles.css', + '/css/materialize.min.css', + '/img/dish.png', + 'https://fonts.googleapis.com/icon?family=Material+Icons', + 'https://fonts.gstatic.com/s/materialicons/v47/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2', + '/pages/fallback.html' +]; + +// cache size limit function +const limitCacheSize = (name, size) => { + caches.open(name).then(cache => { + cache.keys().then(keys => { + if(keys.length > size){ + cache.delete(keys[0]).then(limitCacheSize(name, size)); + } + }); + }); +}; + +// install event +self.addEventListener('install', evt => { + //console.log('service worker installed'); + evt.waitUntil( + caches.open(staticCacheName).then((cache) => { + console.log('caching shell assets'); + cache.addAll(assets); + }) + ); +}); + +// activate event +self.addEventListener('activate', evt => { + //console.log('service worker activated'); + evt.waitUntil( + caches.keys().then(keys => { + //console.log(keys); + return Promise.all(keys + .filter(key => key !== staticCacheName && key !== dynamicCacheName) + .map(key => caches.delete(key)) + ); + }) + ); +}); + +// fetch events +self.addEventListener('fetch', evt => { + if(evt.request.url.indexOf('firestore.googleapis.com') === -1){ + evt.respondWith( + caches.match(evt.request).then(cacheRes => { + return cacheRes || fetch(evt.request).then(fetchRes => { + return caches.open(dynamicCacheName).then(cache => { + cache.put(evt.request.url, fetchRes.clone()); + // check cached items size + limitCacheSize(dynamicCacheName, 15); + return fetchRes; + }) + }); + }).catch(() => { + if(evt.request.url.indexOf('.html') > -1){ + return caches.match('/pages/fallback.html'); + } + }) + ); + } +}); \ No newline at end of file