├── .audit └── README.md ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── LICENSE └── images │ └── pwafireappreport.png ├── .gitignore ├── LICENSE ├── README.md ├── README1.md ├── _config.yml ├── app ├── .firebaserc ├── .gitignore ├── firebase.json ├── firestore.indexes.json ├── firestore.rules ├── functions │ ├── .eslintrc.json │ ├── index.js │ ├── package-lock.json │ └── package.json ├── package-lock.json ├── package.json ├── server.js ├── service-worker-config.js └── src │ ├── 404.html │ ├── add-article │ └── index.html │ ├── app.webmanifest │ ├── assets │ ├── css │ │ ├── article.css │ │ ├── auth.css │ │ └── main.css │ └── js │ │ ├── addfirestore.js │ │ ├── app.js │ │ ├── article.js │ │ ├── auth.js │ │ ├── display.js │ │ ├── login.js │ │ ├── render.js │ │ ├── sync.js │ │ ├── test.js │ │ ├── update.js │ │ └── view.js │ ├── auth │ └── index.html │ ├── images │ ├── banners │ │ └── pwafireappbanner.gif │ ├── home │ │ ├── pwafireapp-frog-icon.svg │ │ └── pwafireapp-frog-icon1.gif │ ├── icons │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-48x48.png │ │ ├── icon-512x512.png │ │ ├── icon-72x72.png │ │ └── icon-96x96.png │ ├── others │ │ ├── latest.svg │ │ ├── pwafireapp.svg │ │ ├── pwafireapp1.svg │ │ ├── round-hot_tub-24px.svg │ │ └── round-trending_up-24px.svg │ ├── pwafireapp-frog-icon.gif │ ├── pwafireapp.svg │ ├── sample.gif │ ├── sample.jpg │ ├── sample.png │ ├── sample.svg │ └── vectors │ │ └── pwafireappbanner.psd │ ├── index.html │ ├── latest │ ├── index.html │ └── sport1.html │ ├── profile │ ├── index.html │ └── share.html │ └── service-worker.js └── docs └── README.md /.audit/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### [100% Audit Pass]() 3 | 4 | ![PWA Fire App 100% Audit Pass](https://raw.githubusercontent.com/mayeedwin/pwafireapp/master/.github/images/pwafireappreport.png) 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | app/src/index.html/* linguist-vendored 2 | app/src/pages/index.html/* linguist-vendored 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 PWAFire.Org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/images/pwafireappreport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/.github/images/pwafireappreport.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.cache 3 | build/ 4 | *.log 5 | app/node_modules/ 6 | app/src/assets/js/firestore.js 7 | functions/ 8 | %RESOURCE_DIR%/ 9 | *.rules 10 | *.rules 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 PWAFire.Org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## We are creating a new version 2 | 3 | Check the [codelab available here](https://pwafire.org/developer/codelabs/get-started-with-pwafireapp/) to use the previous version of the sample app 4 | -------------------------------------------------------------------------------- /README1.md: -------------------------------------------------------------------------------- 1 | ### [PWA Fire App *Beta*](https://pwafire.org/developer/app/) 2 | 3 | Project [PWA Fire Bundle](https://github.com/mayeedwin/pwafire) is an open source javascript and json bundle that allows you to convert your existing website into a progressive web app or build one in a few. 4 | 5 | *** 6 | 7 | ### [What is PWA Fire App?](https://github.com/mayeedwin/pwafireapp) 8 | 9 | Progressive Web App starter App designed with [progressive web app best practices](https://developers.google.com/web/progressive-web-apps/checklist) and packaged ready for your [PWA Project](https://pwafire.org/developer/app/) 10 | 11 | It has two versions; 12 | 13 | 1. For project with build process, npm ; [as in this guide here](https://github.com/mayeedwin/pwafireapp/tree/master/docs) 14 | 15 | 2. For project without any build process; [config guide is here](https://pwafire.org/developer/pwa/started/) 16 | 17 | - [PWA Fire App Demo App](https://pwafireapp.firebaseapp.com/) has a 100% Audit Pass for Progressive Web App, Best Practices, Perfomance, Accessbility and SEO as shown in the [.audit dir here](https://github.com/mayeedwin/pwafireapp/tree/master/.audit) 18 | 19 | *** 20 | 21 | ### [App Structure]() 22 | 23 | Here is the basic skeleton for **PWA Fire App** that each of the two starter PWA Kits will conform to: 24 | 25 | - [ ] **For node-module or build process** 26 | 27 | For **node modules,** follow the setup guide as curated in [the doc folder here](https://github.com/mayeedwin/pwafireapp/tree/master/docs) to get started building a scalable progressive web app with **PWA Fire App.** 28 | 29 | ```b 30 | ├── app 31 | │   ├── build 32 | │   ├── node_modules 33 | │   ├── src 34 | │   ├── assets 35 | | ├── css 36 | | ├── js 37 | | ├── scss 38 | │   ├── images 39 | | ├── icons 40 | | ├── others 41 | | ├── pages 42 | │   ├── index.html 43 | │   ├── app.webmanifest 44 | │   ├── service-worker.js 45 | │   ├── package-lock.json 46 | │   ├── package.json 47 | │   ├── server.js 48 | │   └── sw-config.js 49 | 50 | ``` 51 | 52 | - [ ] **For without any build process like npm** 53 | 54 | If you do not have or do not want to use any build process such as ** npm**, make sure to 55 | remove all other files and that your app structure looks as below. Configure your **service-worker.js** 56 | file as in [this codelab here](https://pwafire.org/developer/pwa/started/#sw-config) 57 | 58 | ```bash 59 | ├── app 60 | │   ├── src 61 | │   ├── assets 62 | | ├── css 63 | | ├── js 64 | | ├── scss 65 | │   ├── images 66 | | ├── icons 67 | | ├── others 68 | | ├── pages 69 | │   ├── index.html 70 | │   ├── app.webmanifest 71 | │   ├── service-worker.js 72 | 73 | ``` 74 | 75 | *** 76 | 77 | #### [Getting started](https://pwafire.org/developer/codelabs/cloud-firestore-for-web) 78 | Get started on how to set up PWA Fire App [ in this setup guide ](https://github.com/mayeedwin/pwafireapp/tree/master/docs). 79 | 80 | *** 81 | 82 | #### [Engage us](https://twitter.com/pwafire) 83 | Donate a star, like, follow and contribute in any way. If you use [PWA Fire Developer Resources](https://pwafire.org/developer), kindly let us know. JUST simply [Tweet us](https://twitter.com/pwafire). 84 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /app/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "pwafireapp" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | 9 | # Firebase cache 10 | .firebase/ 11 | 12 | # Firebase config 13 | 14 | # Uncomment this if you'd like others to create their own Firebase project. 15 | # For a team working on the same Firebase project(s), it is recommended to leave 16 | # it commented so all members can deploy to the same project(s) in .firebaserc. 17 | # .firebaserc 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (http://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | 49 | # Optional npm cache directory 50 | .npm 51 | 52 | # Optional eslint cache 53 | .eslintcache 54 | 55 | # Optional REPL history 56 | .node_repl_history 57 | 58 | # Output of 'npm pack' 59 | *.tgz 60 | 61 | # Yarn Integrity file 62 | .yarn-integrity 63 | 64 | # dotenv environment variables file 65 | .env 66 | -------------------------------------------------------------------------------- /app/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | }, 10 | "firestore": { 11 | "rules": "firestore.rules", 12 | "indexes": "firestore.indexes.json" 13 | }, 14 | "functions": { 15 | "predeploy": [ 16 | "npm --prefix \"%RESOURCE_DIR%\" run lint" 17 | ], 18 | "source": "functions" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /app/firestore.rules: -------------------------------------------------------------------------------- 1 | // Allow read/write access on all documents to any user signed in to the application 2 | service cloud.firestore { 3 | match /databases/{database}/documents { 4 | match /{document=**} { 5 | allow write: if request.auth.uid != null; 6 | allow read; 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /app/functions/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | // Required for certain syntax usages 4 | "ecmaVersion": 6 5 | }, 6 | "plugins": [ 7 | "promise" 8 | ], 9 | "extends": "eslint:recommended", 10 | "rules": { 11 | // Removed rule "disallow the use of console" from recommended eslint rules 12 | "no-console": "off", 13 | 14 | // Removed rule "disallow multiple spaces in regular expressions" from recommended eslint rules 15 | "no-regex-spaces": "off", 16 | 17 | // Removed rule "disallow the use of debugger" from recommended eslint rules 18 | "no-debugger": "off", 19 | 20 | // Removed rule "disallow unused variables" from recommended eslint rules 21 | "no-unused-vars": "off", 22 | 23 | // Removed rule "disallow mixed spaces and tabs for indentation" from recommended eslint rules 24 | "no-mixed-spaces-and-tabs": "off", 25 | 26 | // Removed rule "disallow the use of undeclared variables unless mentioned in /*global */ comments" from recommended eslint rules 27 | "no-undef": "off", 28 | 29 | // Warn against template literal placeholder syntax in regular strings 30 | "no-template-curly-in-string": 1, 31 | 32 | // Warn if return statements do not either always or never specify values 33 | "consistent-return": 1, 34 | 35 | // Warn if no return statements in callbacks of array methods 36 | "array-callback-return": 1, 37 | 38 | // Require the use of === and !== 39 | "eqeqeq": 2, 40 | 41 | // Disallow the use of alert, confirm, and prompt 42 | "no-alert": 2, 43 | 44 | // Disallow the use of arguments.caller or arguments.callee 45 | "no-caller": 2, 46 | 47 | // Disallow null comparisons without type-checking operators 48 | "no-eq-null": 2, 49 | 50 | // Disallow the use of eval() 51 | "no-eval": 2, 52 | 53 | // Warn against extending native types 54 | "no-extend-native": 1, 55 | 56 | // Warn against unnecessary calls to .bind() 57 | "no-extra-bind": 1, 58 | 59 | // Warn against unnecessary labels 60 | "no-extra-label": 1, 61 | 62 | // Disallow leading or trailing decimal points in numeric literals 63 | "no-floating-decimal": 2, 64 | 65 | // Warn against shorthand type conversions 66 | "no-implicit-coercion": 1, 67 | 68 | // Warn against function declarations and expressions inside loop statements 69 | "no-loop-func": 1, 70 | 71 | // Disallow new operators with the Function object 72 | "no-new-func": 2, 73 | 74 | // Warn against new operators with the String, Number, and Boolean objects 75 | "no-new-wrappers": 1, 76 | 77 | // Disallow throwing literals as exceptions 78 | "no-throw-literal": 2, 79 | 80 | // Require using Error objects as Promise rejection reasons 81 | "prefer-promise-reject-errors": 2, 82 | 83 | // Enforce “for” loop update clause moving the counter in the right direction 84 | "for-direction": 2, 85 | 86 | // Enforce return statements in getters 87 | "getter-return": 2, 88 | 89 | // Disallow await inside of loops 90 | "no-await-in-loop": 2, 91 | 92 | // Disallow comparing against -0 93 | "no-compare-neg-zero": 2, 94 | 95 | // Warn against catch clause parameters from shadowing variables in the outer scope 96 | "no-catch-shadow": 1, 97 | 98 | // Disallow identifiers from shadowing restricted names 99 | "no-shadow-restricted-names": 2, 100 | 101 | // Enforce return statements in callbacks of array methods 102 | "callback-return": 2, 103 | 104 | // Require error handling in callbacks 105 | "handle-callback-err": 2, 106 | 107 | // Warn against string concatenation with __dirname and __filename 108 | "no-path-concat": 1, 109 | 110 | // Prefer using arrow functions for callbacks 111 | "prefer-arrow-callback": 1, 112 | 113 | // Return inside each then() to create readable and reusable Promise chains. 114 | // Forces developers to return console logs and http calls in promises. 115 | "promise/always-return": 2, 116 | 117 | //Enforces the use of catch() on un-returned promises 118 | "promise/catch-or-return": 2, 119 | 120 | // Warn against nested then() or catch() statements 121 | "promise/no-nesting": 1 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/functions/index.js: -------------------------------------------------------------------------------- 1 | const functions = require('firebase-functions'); 2 | const admin = require('firebase-admin'); 3 | 4 | admin.initializeApp(); 5 | const db = admin.firestore(); 6 | 7 | // // Create and Deploy Your First Cloud Functions 8 | // // https://firebase.google.com/docs/functions/write-firebase-functions 9 | // 10 | // exports.helloWorld = functions.https.onRequest((request, response) => { 11 | // response.send("Hello from Firebase!"); 12 | // }); 13 | 14 | const addUser = (userData, context) => { 15 | return db.collection('users').doc(userData.uid).set({ 16 | email: userData.email, 17 | displayName: userData.displayName, 18 | }).catch(console.error); 19 | }; 20 | 21 | module.exports = { 22 | authOnCreate: functions.auth.user().onCreate(addUser), 23 | }; 24 | -------------------------------------------------------------------------------- /app/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "lint": "eslint .", 6 | "serve": "firebase serve --only functions", 7 | "shell": "firebase functions:shell", 8 | "start": "npm run shell", 9 | "deploy": "firebase deploy --only functions", 10 | "logs": "firebase functions:log" 11 | }, 12 | "dependencies": { 13 | "firebase-admin": "~6.0.0", 14 | "firebase-functions": "^2.1.0" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^4.12.0", 18 | "eslint-plugin-promise": "^3.6.0" 19 | }, 20 | "private": true 21 | } 22 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Maye Edwin", 3 | "scripts": { 4 | "copy": "copyfiles -u 1 src/**/**/* src/**/* src/* build", 5 | "build": "npm run copy && workbox injectManifest service-worker-config.js", 6 | "start": "node server.js" 7 | }, 8 | "dependencies": { 9 | "express": "^4.15.4" 10 | }, 11 | "devDependencies": { 12 | "copyfiles": "^1.2.0", 13 | "workbox-cli": "^4.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | */ 8 | const express = require('express'); 9 | const app = express(); 10 | 11 | // This serves static files from the specified directory 12 | app.use(express.static(__dirname + '/build')); 13 | 14 | const server = app.listen(8081, () => { 15 | 16 | const host = server.address().address; 17 | const port = server.address().port; 18 | 19 | console.log('App listening at http://%s:%s', host, port); 20 | }); 21 | -------------------------------------------------------------------------------- /app/service-worker-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /* The base directory you wish to match globPatterns against, 3 | relative to the current working directory 4 | */ 5 | "globDirectory": "build/", 6 | "globPatterns": [ 7 | /* Edit to add all file to pre-cache; configure for your project as shown below 8 | e.g cache all .css files in the root folder 9 | */ 10 | "index.html", 11 | "app.webmanifest", 12 | "images/home/*.gif", 13 | // Pre-cache the default app icon needed for app installation 14 | "images/icons/icon-144x144.png", 15 | // Pre-cache app assets i.e css, scss, js 16 | "assets/js/*.js", 17 | "assets/css/*.css" 18 | ], 19 | /* The path and filename of the service worker file that will 20 | be created by the build process 21 | */ 22 | "swSrc": "src/service-worker.js", 23 | /* The path to the source service worker file that can contain 24 | your own customized code e.g cache strategies for different app resources 25 | */ 26 | "swDest": "build/service-worker.js", 27 | /* In addition to containing a match for 28 | injectionPointRegexp 29 | */ 30 | "globIgnores": [ 31 | "../service-worker-config.js" 32 | ] 33 | }; -------------------------------------------------------------------------------- /app/src/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page Not Found 7 | 8 | 23 | 24 | 25 |
26 |

404

27 |

Page Not Found

28 |

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

29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/add-article/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PWA Fire App - Latest Progressive Web Apps News 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 42 | 43 | 44 | 47 | 52 | 53 | 54 |
55 | 56 | 57 |
58 |

59 | What's trending for Web? 60 |

61 | 66 |
67 | 68 | 75 | 76 |
77 |
78 | 79 |
80 | 81 | 82 |
83 | 84 |
85 | 86 | 87 |
88 | 89 |
90 | 91 | 92 |
93 | 94 | 95 |
96 |
97 | 98 | 101 |
102 | 105 | 108 |
109 | 110 | 111 | 117 | 118 | 121 | 122 |
123 | 124 | 127 | 128 | 130 | 131 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /app/src/app.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "background_color": "#fff", 3 | "display": "standalone", 4 | "orientation":"portrait", 5 | "theme_color": "#fff", 6 | "short_name": "PWA Fire App", 7 | "name": "PWA Fire App", 8 | "description": "description or purpose of your progressive web app", 9 | "lang": "en-US", 10 | "share_target": { 11 | "action": "index.html", 12 | "params": { 13 | "title": "title", 14 | "text": "text", 15 | "url": "url" 16 | } 17 | }, 18 | "icons": [ 19 | { 20 | "src": "images/icons/icon-72x72.png", 21 | "sizes": "72x72", 22 | "type": "image/png" 23 | }, 24 | { 25 | "src": "images/icons/icon-96x96.png", 26 | "sizes": "96x96", 27 | "type": "image/png" 28 | }, 29 | { 30 | "src": "images/icons/icon-128x128.png", 31 | "sizes": "128x128", 32 | "type": "image/png" 33 | }, 34 | { 35 | "src": "images/icons/icon-144x144.png", 36 | "sizes": "144x144", 37 | "type": "image/png" 38 | }, 39 | { 40 | "src": "images/icons/icon-152x152.png", 41 | "sizes": "152x152", 42 | "type": "image/png" 43 | }, 44 | { 45 | "src": "images/icons/icon-192x192.png", 46 | "sizes": "192x192", 47 | "type": "image/png" 48 | }, 49 | { 50 | "src": "images/icons/icon-384x384.png", 51 | "sizes": "384x384", 52 | "type": "image/png" 53 | }, 54 | { 55 | "src": "images/icons/icon-512x512.png", 56 | "sizes": "512x512", 57 | "type": "image/png" 58 | } 59 | ], 60 | "start_url": "index.html?launcher=true", 61 | "scope": "/" 62 | } -------------------------------------------------------------------------------- /app/src/assets/css/article.css: -------------------------------------------------------------------------------- 1 | /* add some 2 | styles 3 | */ 4 | form { 5 | margin-top: 10px; 6 | } 7 | 8 | .row { 9 | margin: 25px 0; /* need some nice spacing here */ 10 | position: relative; /* this one is important */ 11 | } 12 | 13 | /*make label absolute and position it on input area*/ 14 | label { 15 | font-size: 13px; 16 | position: absolute; 17 | top: 15px; 18 | left: 2px; 19 | -webkit-transition: .1s; 20 | -o-transition: .1s; 21 | transition: .1s; 22 | } 23 | 24 | input { 25 | padding: 10px 0 10px; 26 | width: 100%; 27 | border: none; 28 | border-bottom: 1px solid #34495e; 29 | font-size: 13px; 30 | background: #ecf0f1; 31 | text-align: center; 32 | } 33 | 34 | /*magic happens here */ 35 | input:focus{ 36 | outline: none; 37 | border-color: #2980b9; 38 | } 39 | 40 | input:valid ~ label, /*detects if input has valid input*/ 41 | input:focus ~ label{ 42 | top: -20px; 43 | font-weight: 0; 44 | } 45 | 46 | 47 | .submitButton { 48 | background-color: inherit; 49 | border: 1px solid #c7c0c0; 50 | font-size: 1em; 51 | padding: 0.60em; 52 | border-radius: 25px; 53 | width: 150px; 54 | } -------------------------------------------------------------------------------- /app/src/assets/css/auth.css: -------------------------------------------------------------------------------- 1 | 2 | .auth 3 | 4 | { 5 | margin: 250px auto; 6 | 7 | } 8 | 9 | .firebaseui-tos { 10 | color: #757575; 11 | direction: ltr; 12 | font-size: 13px; 13 | line-height: 16px; 14 | margin-bottom: 24px; 15 | margin-top: 0; 16 | text-align: left; 17 | } 18 | 19 | .firebaseui-idp-button { 20 | direction: ltr; 21 | font-weight: 500; 22 | height: auto; 23 | line-height: normal; 24 | max-width: 190px; 25 | min-height: 40px; 26 | padding: 8px 16px; 27 | text-align: center; 28 | width: 100%; 29 | border-radius: 25px; 30 | } -------------------------------------------------------------------------------- /app/src/assets/css/main.css: -------------------------------------------------------------------------------- 1 | /** replace or + add your css**/ 2 | @import url('https://fonts.googleapis.com/css?family=Roboto'); 3 | 4 | body { 5 | display: block; 6 | margin: 0px; 7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif 8 | } 9 | 10 | .pwafireapp { 11 | height: 100%; 12 | min-height: 100%; 13 | } 14 | 15 | .navbar { 16 | display: flex; 17 | background: #fff; 18 | border-bottom: 0.75px solid #c7c0c0; 19 | padding: 15px; 20 | justify-content: center; 21 | position: fixed; 22 | flex-wrap: wrap; 23 | top: 0; 24 | left: 0; 25 | right: 0; 26 | z-index: 1000; 27 | } 28 | 29 | .main-div { 30 | min-height: 100%; 31 | width: 100%; 32 | display: flex; 33 | -webkit-box-pack: center; 34 | justify-content: center; 35 | -webkit-box-align: center; 36 | align-items: center; 37 | -webkit-box-orient: vertical; 38 | -webkit-box-direction: normal; 39 | -ms-flex-direction: column; 40 | flex-direction: column; 41 | text-align: center; 42 | } 43 | 44 | .app-logo { 45 | width: 50px; 46 | margin-top: 75px; 47 | } 48 | 49 | h3 { 50 | font-weight: 500; 51 | } 52 | 53 | #title { 54 | font-size: 18px; 55 | font-weight: 500; 56 | } 57 | 58 | #desc { 59 | text-align: center; 60 | text-decoration: wavy; 61 | text-rendering: optimizeSpeed; 62 | flood-color: rgb(49, 47, 47); 63 | } 64 | 65 | .hidden { 66 | display: none !important; 67 | } 68 | 69 | .share-par { 70 | display: none !important; 71 | } 72 | 73 | button[disabled] { 74 | opacity: 0.5; 75 | border: 1px solid rgba(74, 20, 140, 0.5) !important; 76 | } 77 | 78 | #installContainer { 79 | display: inline-flex; 80 | justify-content: center; 81 | width: 100%; 82 | margin-top: 25px; 83 | } 84 | 85 | #installContainer button { 86 | background-color: inherit; 87 | border: 1px solid #c7c0c0; 88 | font-size: 1em; 89 | padding: 0.60em; 90 | border-radius: 25px; 91 | width: 150px; 92 | } 93 | 94 | #customButton { 95 | background-color: inherit; 96 | border: 1px solid #c7c0c0; 97 | font-size: 0.75em; 98 | padding: 0.60em; 99 | border-radius: 25px; 100 | width: 100px; 101 | display: inline-block; 102 | margin: 7px; 103 | } 104 | 105 | #auth_in { 106 | background-color: inherit; 107 | border: 1px solid #c7c0c0; 108 | font-size: 0.75em; 109 | padding: 0.60em; 110 | border-radius: 25px; 111 | width: 100px; 112 | display: inline-block; 113 | margin: 7px; 114 | } 115 | 116 | *, 117 | ::after, 118 | ::before { 119 | box-sizing: unset; 120 | } 121 | 122 | /** set what happens on focus **/ 123 | #customButton { 124 | outline: 0px dotted; 125 | color: rgb(49, 47, 47); 126 | outline: 0px auto -webkit-focus-ring-color; 127 | } 128 | 129 | #auth_in:focus { 130 | outline: 0px dotted; 131 | color: rgb(49, 47, 47); 132 | outline: 0px auto -webkit-focus-ring-color; 133 | } 134 | 135 | 136 | button:focus { 137 | outline: 0px dotted; 138 | color: rgb(49, 47, 47); 139 | outline: 0px auto -webkit-focus-ring-color; 140 | } 141 | 142 | 143 | a { 144 | text-decoration: none; 145 | } 146 | 147 | .navigate { 148 | display: flex; 149 | justify-content: center; 150 | width: 100%; 151 | } 152 | 153 | #buttonInstall { 154 | margin-left: 1em; 155 | } 156 | 157 | 158 | /* snackbar.css by 159 | https://pwafire.org/developer/ */ 160 | 161 | /* The snackbar - position it at the bottom and in the middle 162 | of the screen */ 163 | 164 | 165 | #snackbar { 166 | visibility: hidden; 167 | background-color: rgb(68, 66, 66); 168 | color: #fff; 169 | text-align: center; 170 | border-radius: 7px; 171 | padding: 8px; 172 | z-index: 1; 173 | bottom: 30px; 174 | font-size: 14px; 175 | width: 295px; 176 | display: block; 177 | margin: 0 auto; 178 | } 179 | 180 | 181 | /* Show the snackbar when clicking on a button 182 | (class added with JavaScript) */ 183 | 184 | #snackbar.show { 185 | visibility: visible; 186 | /* Show the snackbar */ 187 | 188 | /* Add animation: Take 0.5 seconds to fade in and out the snackbar. 189 | However, delay the fade out process for 2.5 seconds */ 190 | 191 | -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; 192 | animation: fadein 0.5s, fadeout 0.5s 2.5s; 193 | text-align: center; 194 | } 195 | 196 | /* Animations to fade the snackbar in and out */ 197 | @-webkit-keyframes fadein { 198 | from { 199 | bottom: 0; 200 | opacity: 0; 201 | } 202 | 203 | to { 204 | bottom: 30px; 205 | opacity: 1; 206 | } 207 | } 208 | 209 | @keyframes fadein { 210 | from { 211 | bottom: 0; 212 | opacity: 0; 213 | } 214 | 215 | to { 216 | bottom: 30px; 217 | opacity: 1; 218 | } 219 | } 220 | 221 | @-webkit-keyframes fadeout { 222 | from { 223 | bottom: 30px; 224 | opacity: 1; 225 | } 226 | 227 | to { 228 | bottom: 0; 229 | opacity: 0; 230 | } 231 | } 232 | 233 | @keyframes fadeout { 234 | from { 235 | bottom: 30px; 236 | opacity: 1; 237 | } 238 | 239 | to { 240 | bottom: 0; 241 | opacity: 0; 242 | } 243 | } 244 | 245 | .footer { 246 | display: block; 247 | background: inherit; 248 | border-bottom: 0.50px solid #c7c0c0; 249 | padding: 10px; 250 | text-align: center; 251 | font-size: 12px; 252 | } 253 | 254 | .latest-news { 255 | align-items: center; 256 | text-align: left; 257 | margin: auto; 258 | padding-left: 10px; 259 | padding-right: 10px; 260 | overflow-y: scroll; 261 | height: 225px; 262 | border-radius: 10px; 263 | width: 75%; 264 | } 265 | 266 | .post-article { 267 | padding-left: 20px; 268 | padding-right: 20px; 269 | width: 75%; 270 | margin: 0 auto; 271 | } 272 | 273 | /* Portrait and Landscape */ 274 | @media screen and (device-width: 360px) and (device-height: 640px) and (-webkit-device-pixel-ratio: 3) { 275 | .main-div { 276 | margin: 0 auto; 277 | text-align: center; 278 | width: auto; 279 | padding-left: 20px; 280 | padding-right: 20px; 281 | } 282 | 283 | .latest-news { 284 | width: auto; 285 | margin: 10px; 286 | height: 250px; 287 | } 288 | 289 | .desktop { 290 | display: none; 291 | } 292 | 293 | .mobile { 294 | display: block; 295 | } 296 | 297 | .post-article { 298 | width: auto; 299 | } 300 | } 301 | 302 | .mobile { 303 | display: none; 304 | } 305 | 306 | /* Portrait and Landscape */ 307 | @media only screen and (-Webkit-min-device-pixel-ratio:1.5), 308 | only screen and (-moz-min-device-pixel-ratio:1.5), 309 | only screen and (min-device-pixel-ratio:1.5) { 310 | 311 | .main-div { 312 | margin: 0 auto; 313 | text-align: center; 314 | width: auto; 315 | padding-left: 5px; 316 | padding-right: 5px; 317 | } 318 | 319 | .header-intro { 320 | padding: 5px; 321 | display: block; 322 | 323 | } 324 | 325 | .latest-news { 326 | width: auto; 327 | margin: 10px; 328 | height: 250px; 329 | } 330 | 331 | .desktop { 332 | display: none; 333 | } 334 | 335 | .mobile { 336 | display: block; 337 | } 338 | 339 | #installContainer button { 340 | width: 120px; 341 | } 342 | 343 | #snackbar { 344 | width: auto; 345 | } 346 | 347 | .post-article { 348 | width: auto; 349 | } 350 | 351 | } 352 | 353 | /* ----------- iPad 3, 4 and Pro 9.7" ----------- */ 354 | 355 | /* Portrait and Landscape */ 356 | @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) { 357 | 358 | .latest-news { 359 | margin-left: 75px; 360 | margin-right: 75px; 361 | } 362 | 363 | #snackbar { 364 | width: 350px; 365 | } 366 | 367 | } 368 | 369 | 370 | .latest-news h2 { 371 | font-size: 15px; 372 | font-weight: 500; 373 | } 374 | 375 | .latest-news p { 376 | font-size: 13px; 377 | } 378 | 379 | article { 380 | 381 | border-bottom: 0.50px solid #c7c0c0; 382 | 383 | } 384 | 385 | /* Let's get this party started */ 386 | ::-webkit-scrollbar { 387 | width: 7px; 388 | } 389 | 390 | /* track */ 391 | ::-webkit-scrollbar-track { 392 | -webkit-border-radius: 10px; 393 | border-radius: 10px; 394 | } 395 | 396 | /* handle */ 397 | ::-webkit-scrollbar-thumb { 398 | -webkit-border-radius: 10px; 399 | border-radius: 10px; 400 | background: rgba(113, 122, 116, 0.4); 401 | } 402 | 403 | ::-webkit-scrollbar-thumb:window-inactive { 404 | background: rgba(113, 122, 116, 0.4); 405 | } -------------------------------------------------------------------------------- /app/src/assets/js/addfirestore.js: -------------------------------------------------------------------------------- 1 | // Initialize Firebase 2 | let config = { 3 | apiKey: "apiKey", 4 | authDomain: "authDomain", 5 | databaseURL: "databaseURL", 6 | projectId: "projectId", 7 | storageBucket: "storageBucket", 8 | messagingSenderId: "messagingSenderId" 9 | }; 10 | 11 | firebase.initializeApp(config); 12 | let firestore = firebase.firestore(); 13 | console.log("Cloud Firestore Loaded"); 14 | 15 | var db = firebase.firestore(); 16 | 17 | const timestamps = firebase.firestore(); 18 | const settings = { 19 | timestampsInSnapshots: true 20 | }; 21 | firestore.settings(settings); 22 | 23 | // Enable offline capabilities 24 | firebase.firestore().enablePersistence() 25 | .then(function() { 26 | // Initialize Cloud Firestore through firebase 27 | var db = firebase.firestore(); 28 | }) 29 | .catch(function(err) { 30 | if (err.code == 'failed-precondition') { 31 | // Multiple tabs open, persistence can only be enabled in one tab at a a time. 32 | 33 | } else if (err.code == 'unimplemented') { 34 | // The current browser does not support all of the 35 | // features required to enable persistence 36 | // ... 37 | } 38 | }); 39 | 40 | /* 41 | var docRef = db.collection('rate').doc('feedback'); 42 | // Update the timestamp field with the value from the server 43 | var updateTimestamp = docRef.update({ 44 | timestamp: firebase.firestore.FieldValue.serverTimestamp() 45 | }); 46 | console.log(updateTimestamp); 47 | 48 | */ 49 | -------------------------------------------------------------------------------- /app/src/assets/js/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | Your Javascript 3 | */ 4 | 5 | console.log("I am a Beta version of PWA Fire App 🐹"); 6 | 7 | const divResult = document.getElementById('result'); 8 | const divInstall = document.getElementById('installContainer'); 9 | const buttonInstall = document.getElementById('buttonInstall'); 10 | const buttonShare = document.getElementById('buttonShare'); 11 | 12 | window.addEventListener('beforeinstallprompt', (event) => { 13 | console.log('👍', 'beforeinstallprompt', event); 14 | // Stash the event so it can be triggered later. 15 | window.deferredPrompt = event; 16 | // Remove the 'hidden' class from the install button container 17 | buttonInstall.removeAttribute('disabled'); 18 | }); 19 | 20 | buttonInstall.addEventListener('click', () => { 21 | console.log('👍', 'buttonInstall-clicked'); 22 | const promptEvent = window.deferredPrompt; 23 | if (!promptEvent) { 24 | // The deferred prompt isn't available. 25 | return; 26 | } 27 | // Show the install prompt. 28 | promptEvent.prompt(); 29 | // Log the result 30 | promptEvent.userChoice.then((result) => { 31 | console.log('👍', 'userChoice', result); 32 | // Reset the deferred prompt variable, since 33 | // prompt() can only be called once. 34 | window.deferredPrompt = null; 35 | // Hide the install button. 36 | buttonInstall.setAttribute('disabled', true); 37 | }); 38 | }); 39 | 40 | window.addEventListener('appinstalled', (event) => { 41 | console.log('👍', 'app successfully installed', event); 42 | }); 43 | 44 | if ('share' in navigator) { 45 | console.log('👍', 'navigator.share is supported'); 46 | buttonShare.removeAttribute('disabled'); 47 | buttonShare.addEventListener('click', (e) => { 48 | console.log('👍', 'buttonShare-clicked', e); 49 | e.preventDefault(); 50 | const shareOpts = { 51 | title: 'PWA Fire App', 52 | text: 'Starter Web App Designed With Progressive Web App Best Practices And Packaged Ready For Your PWA Project #pwafireapp #pwafiredev #pwafire #pwa', 53 | url: 'https://pwafire.org/developer/app/', 54 | }; 55 | navigator.share(shareOpts) 56 | .then((e) => { 57 | const msg = 'navigator.share succeeded.'; 58 | divResult.textContent = msg; 59 | console.log('👍', msg, e); 60 | }) 61 | .catch((err) => { 62 | const msg = 'navigator.share failed'; 63 | divResult.textContent = `${msg}\n${JSON.stringify(err)}`; 64 | console.error('👎', msg, err); 65 | }); 66 | }); 67 | } 68 | else { 69 | console.warn('👎', 'navigator.share is not supported'); 70 | // const divNotSup = document.getElementById('shareNotSupported'); 71 | // divNotSup.classList.toggle('hidden', false); 72 | 73 | // Add the snackbar to show no support [2] 74 | document.getElementById("snackbar").innerHTML = "navigator.share is not supported in this browser"; 75 | var snackbar = document.getElementById("snackbar"); 76 | // Add the "show" class to div 77 | snackbar.className = "show"; 78 | // After 5 seconds, remove the show class from div 79 | setTimeout(function(){ snackbar.className = snackbar.className.replace("show", ""); }, 3000); 80 | // divResult.classList.toggle('hidden', true); 81 | } 82 | 83 | /* 84 | * Warn the page must be served over HTTPS 85 | * The `beforeinstallprompt` event won't fire if the page is served over HTTP. 86 | * Installability requires a service worker with a fetch event handler, and 87 | * if the page isn't served over HTTPS, the service worker won't load. 88 | 89 | if (window.location.protocol === 'http:') { 90 | const requireHTTPS = document.getElementById('requireHTTPS'); 91 | const link = requireHTTPS.querySelector('a'); 92 | link.href = window.location.href.replace('http://', 'https://'); 93 | requireHTTPS.classList.remove('hidden'); 94 | } 95 | 96 | 97 | // add a new version update prompt for users 98 | self.addEventListener('message', (event) => { 99 | if (!event.data){ 100 | return; 101 | } 102 | 103 | switch (event.data) { 104 | case 'skipWaiting': 105 | self.skipWaiting(); 106 | break; 107 | default: 108 | // NOOP 109 | break; 110 | } 111 | }); 112 | 113 | */ -------------------------------------------------------------------------------- /app/src/assets/js/article.js: -------------------------------------------------------------------------------- 1 | 2 | console.log("Loading Article"); 3 | function postArticle() 4 | { 5 | var article_url = document.getElementById('article_url').value; 6 | var article_title = document.getElementById('article_title').value; 7 | var article_caption = document.getElementById('article_caption').value; 8 | console.log(article_url); 9 | console.log(article_title); 10 | console.log(article_caption); 11 | 12 | // Add a new document with a generated id. 13 | db.collection("posts").add({ 14 | article_url: article_url, 15 | article_title: article_title, 16 | article_caption: article_caption 17 | }) 18 | 19 | .then(function(docRef) { 20 | console.log("Document written with ID: ", docRef.id); 21 | }) 22 | .catch(function(error) { 23 | console.error("Error adding document: ", error); 24 | }); 25 | 26 | } 27 | 28 | // redirect to homepage on sending article_title 29 | document 30 | .getElementById("formRedirect") 31 | .addEventListener("submit", function(e) { 32 | e.preventDefault(); 33 | window.location.href = "../latest"; 34 | }); 35 | -------------------------------------------------------------------------------- /app/src/assets/js/auth.js: -------------------------------------------------------------------------------- 1 | 2 | // Initialize the FirebaseUI Widget using Firebase. 3 | var ui = new firebaseui.auth.AuthUI(firebase.auth()); 4 | 5 | ui.start('#firebaseui-auth-container', { 6 | signInOptions: [ 7 | // List of OAuth providers supported. 8 | firebase.auth.GoogleAuthProvider.PROVIDER_ID, 9 | firebase.auth.GithubAuthProvider.PROVIDER_ID, 10 | ], 11 | // Other config options... 12 | }); 13 | 14 | 15 | firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION) 16 | .then(function() { 17 | // Existing and future Auth states are now persisted in the current 18 | // session only. Closing the window would clear any existing state even 19 | // if a user forgets to sign out. 20 | // ... 21 | // New sign-in will be persisted with session persistence. 22 | return firebase.auth.GoogleAuthProvider(); 23 | }) 24 | .catch(function(error) { 25 | // Handle Errors here. 26 | var errorCode = error.code; 27 | var errorMessage = error.message; 28 | }); 29 | 30 | var uiConfig = { 31 | callbacks: { 32 | signInSuccessWithAuthResult: function(authResult, redirectUrl) { 33 | // User successfully signed in. 34 | // Return type determines whether we continue the redirect automatically 35 | // or whether we leave that to developer to handle. 36 | return true; 37 | 38 | }, 39 | uiShown: function() { 40 | // The widget is rendered. 41 | // Hide the loader. 42 | document.getElementById('loader').style.display = 'none'; 43 | } 44 | }, 45 | // Will use popup for IDP Providers sign-in flow instead of the default, redirect. 46 | signInFlow: 'popup', 47 | signInSuccessUrl: '../add-article', 48 | signInOptions: [ 49 | // Leave the lines as is for the providers you want to offer your users. 50 | firebase.auth.GoogleAuthProvider.PROVIDER_ID, 51 | firebase.auth.GithubAuthProvider.PROVIDER_ID, 52 | ], 53 | // Terms of service url. 54 | tosUrl: '../', 55 | // Privacy policy url. 56 | privacyPolicyUrl: '../' 57 | }; 58 | 59 | // The start method will wait until the DOM is loaded. 60 | ui.start('#firebaseui-auth-container', uiConfig); 61 | 62 | firebase.auth().signOut().then(function() { 63 | // Sign-out successful. 64 | }).catch(function(error) { 65 | // An error happened. 66 | }); 67 | -------------------------------------------------------------------------------- /app/src/assets/js/display.js: -------------------------------------------------------------------------------- 1 | /* 2 | var postsRef; 3 | var postArticle; 4 | // Read firestore data from database in the posts collection 5 | db.collection("posts").get().then(function(querySnapshot) { 6 | querySnapshot.forEach(function(doc) { 7 | // doc.data() is never undefined for query doc snapshots 8 | console.log(doc.id, " => ", doc.data()); 9 | const posts = doc.data(); 10 | article_url.href = posts.article_url; 11 | article_title.innerText = posts.article_title; 12 | article_caption.innerText = posts.article_caption; 13 | }); 14 | }); 15 | */ 16 | 17 | const cafeList = document.querySelector('#cafe-list'); 18 | 19 | // create element and render cafe to the dom 20 | function renderCafe(doc) { 21 | let li = document.createElement('li'); 22 | let article_title = document.createElement('span'); 23 | let article_caption = document.createElement('span'); 24 | 25 | li.setAttribute('data-id', doc.id); 26 | article_title.textContent = doc.data().article_title; 27 | article_caption.textContent = doc.data().article_caption; 28 | 29 | li.appendChild(article_title); 30 | li.appendChild(article_caption); 31 | 32 | cafeList.appendChild(li); 33 | 34 | } 35 | 36 | db.collection('posts').get().then((snapshot) => { 37 | snapshot.docs.forEach(doc => { 38 | console.log(doc.data()); 39 | // call the render function 40 | renderCafe(doc); 41 | } ) ; 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /app/src/assets/js/login.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | if logged in 4 | */ 5 | 6 | firebase.auth().onAuthStateChanged(function(user) { 7 | if (user) { 8 | 9 | // Add the snackbar to show guide 10 | document.getElementById("snackbar").innerHTML = "Share an amazing trendy article : )"; 11 | var snackbar = document.getElementById("snackbar"); 12 | 13 | // Add the "show" class to div 14 | snackbar.className = "show"; 15 | 16 | // After 5 seconds, remove the show class from div 17 | setTimeout(function(){ snackbar.className = snackbar.className.replace("show", ""); }, 5000); 18 | 19 | // Add user is logged in 20 | document.getElementById("auth_in").innerHTML = "Logged in : )"; 21 | var auth_in = document.getElementById("auth_in"); 22 | 23 | // Add the "show" class to div 24 | auth_in.className = "auth-in"; 25 | // After 5 seconds, remove the auth-in class from div 26 | setTimeout(function(){ auth_in.className = auth_in.className.replace("auth_in", ""); }); 27 | 28 | } else { 29 | // User is signed out. 30 | // ... 31 | console.log("You are signed out!"); 32 | } 33 | // ... 34 | }); 35 | -------------------------------------------------------------------------------- /app/src/assets/js/render.js: -------------------------------------------------------------------------------- 1 | 2 | var render = function (template, node) { 3 | 4 | if (!node) return; 5 | node.innerHTML = template; 6 | 7 | }; 8 | 9 | var template = '

Hello world!

'; 10 | render(template, document.querySelector('#main')); 11 | 12 | /* 13 | 14 | var data = { 15 | animals:[ 16 | {name: 'mouse'}, 17 | {name: 'cat'}, 18 | {name: 'bird'}, 19 | {name: 'dog'} 20 | ] 21 | }; 22 | var directive = { 23 | 'li':{ 24 | 'animal<-animals':{ //for each entry in animales name the element 'animal' 25 | 'span': 'animal.name' //the dot selector, means the current node (here a LI) 26 | } 27 | } 28 | }; 29 | $p( 'ul' ).render( data, directive ); 30 | */ -------------------------------------------------------------------------------- /app/src/assets/js/sync.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | Copyright 2018 PWAFire.Org Authored. All Rights Reserved. 4 | **/ 5 | (function (window) { 6 | 'use strict'; 7 | 8 | // Add background sync on page load 9 | 10 | document.addEventListener("DOMContentLoaded", function() { 11 | window.registerBGSync(); 12 | }); 13 | //To register `BG Sync` and check 'push notification' support 14 | //Exposing `registerSync()` globally for only development purpose 15 | window.registerBGSync = function() { 16 | //If `serviceWorker` is registered and ready 17 | navigator.serviceWorker.ready 18 | .then(function (registration) { 19 | //Registering `background sync` event 20 | return registration.sync.register('Comments') //`Comments` is sync tag name 21 | .then(function (rs) { 22 | console.info('Background sync registered!'); 23 | // bgSyncElement.classList.add('hide'); 24 | // bgSyncTextElement.removeAttribute('hidden'); //Show registered text to user 25 | }, function () { 26 | console.error('Background sync registered failed.'); 27 | }); 28 | 29 | }); 30 | } 31 | })(window); 32 | -------------------------------------------------------------------------------- /app/src/assets/js/test.js: -------------------------------------------------------------------------------- 1 | const cafeList = document.querySelector('#cafe-list'); 2 | 3 | // create element and render cafe to the dom 4 | function renderCafe(doc) { 5 | let li = document.createElement('li'); 6 | let article_title = document.createElement('span'); 7 | let article_caption = document.createElement('span'); 8 | 9 | li.setAttribute('data-id', doc.id); 10 | article_title.textContent = doc.data().article_title; 11 | article_caption.textContent = doc.data().article_caption; 12 | 13 | li.appendChild(article_title); 14 | li.appendChild(article_caption); 15 | 16 | cafeList.appendChild(li); 17 | 18 | } 19 | 20 | db.collection('posts').get().then((snapshot) => { 21 | snapshot.docs.forEach(doc => { 22 | console.log(doc.data()); 23 | // call the render function 24 | renderCafe(doc); 25 | } ) ; 26 | }); 27 | -------------------------------------------------------------------------------- /app/src/assets/js/update.js: -------------------------------------------------------------------------------- 1 | function showRefreshUI(registration) { 2 | // TODO: Display a toast or refresh UI. 3 | 4 | // This demo creates and injects a button. 5 | 6 | var button = document.createElement('button'); 7 | button.style.position = 'absolute'; 8 | button.style.bottom = '24px'; 9 | button.style.left = '24px'; 10 | button.textContent = 'This site has updated. Please click to see changes.'; 11 | 12 | button.addEventListener('click', function() { 13 | if (!registration.waiting) { 14 | // Just to ensure registration.waiting is available before 15 | // calling postMessage() 16 | return; 17 | } 18 | 19 | button.disabled = true; 20 | 21 | registration.waiting.postMessage('skipWaiting'); 22 | }); 23 | 24 | document.body.appendChild(button); 25 | } 26 | 27 | function onNewServiceWorker(registration, callback) { 28 | if (registration.waiting) { 29 | // service-worker is waiting to activate. Can occur if multiple clients open and 30 | // one of the clients is refreshed. 31 | return callback(); 32 | } 33 | 34 | function listenInstalledStateChange() { 35 | registration.installing.addEventListener('statechange', function(event) { 36 | if (event.target.state === 'installed') { 37 | // A new service worker is available, inform the user 38 | callback(); 39 | } 40 | }); 41 | } 42 | 43 | if (registration.installing) { 44 | return listenInstalledStateChange(); 45 | } 46 | 47 | // We are currently controlled so a new service-worker may be found... 48 | // Add a listener in case a new service-worker is found, 49 | registration.addEventListener('updatefound', listenInstalledStateChange); 50 | } 51 | 52 | window.addEventListener('load', function() { 53 | navigator.serviceWorker.register('../../service-worker.js') 54 | .then(function (registration) { 55 | // Track updates to the Service Worker. 56 | if (!navigator.serviceWorker.controller) { 57 | // The window client isn't currently controlled so it's a new service 58 | // worker that will activate immediately 59 | return; 60 | } 61 | 62 | // When the user asks to refresh the UI, we'll need to reload the window 63 | var preventDevToolsReloadLoop; 64 | navigator.serviceWorker.addEventListener('controllerchange', function(event) { 65 | // Ensure refresh is only called once. 66 | // This works around a bug in "force update on reload". 67 | if (preventDevToolsReloadLoop) return; 68 | preventDevToolsReloadLoop = true; 69 | console.log('Controller loaded'); 70 | window.location.reload(); 71 | }); 72 | 73 | onNewServiceWorker(registration, function() { 74 | showRefreshUI(registration); 75 | }); 76 | }); 77 | }); -------------------------------------------------------------------------------- /app/src/assets/js/view.js: -------------------------------------------------------------------------------- 1 | // Display data into our web app 2 | const article_title=document.querySelector("#article_title"); 3 | const article_caption=document.querySelector("#article_caption"); 4 | -------------------------------------------------------------------------------- /app/src/auth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Latest PWA News : Authentication 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/images/banners/pwafireappbanner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/banners/pwafireappbanner.gif -------------------------------------------------------------------------------- /app/src/images/home/pwafireapp-frog-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/images/home/pwafireapp-frog-icon1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/home/pwafireapp-frog-icon1.gif -------------------------------------------------------------------------------- /app/src/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-384x384.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-48x48.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-512x512.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-72x72.png -------------------------------------------------------------------------------- /app/src/images/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/icons/icon-96x96.png -------------------------------------------------------------------------------- /app/src/images/others/latest.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/images/others/pwafireapp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/images/others/pwafireapp1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/images/others/round-hot_tub-24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/images/others/round-trending_up-24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/images/pwafireapp-frog-icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/pwafireapp-frog-icon.gif -------------------------------------------------------------------------------- /app/src/images/pwafireapp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/images/sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/sample.gif -------------------------------------------------------------------------------- /app/src/images/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/sample.jpg -------------------------------------------------------------------------------- /app/src/images/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/sample.png -------------------------------------------------------------------------------- /app/src/images/sample.svg: -------------------------------------------------------------------------------- 1 | arrow -------------------------------------------------------------------------------- /app/src/images/vectors/pwafireappbanner.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwafire/pwafireapp/a49ae8313b6c041514e211d1e0bbe4b12a8c683a/app/src/images/vectors/pwafireappbanner.psd -------------------------------------------------------------------------------- /app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | PWA Fire App - Workbox Progressive Web App Demo And Starter App 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 51 | 52 | 53 | 54 | 57 |
58 | 63 | 64 |
65 | 66 |
67 |

68 | I am a Beta version 69 |

70 | 71 |

72 | Starter Web App Designed With Progressive Web App Best Practices
And 73 | Packaged Ready For Your PWA Project 74 |

75 |

76 | Starter Web App Designed With Progressive Web App Best Practices And 77 | Packaged Ready For Your PWA Project 78 |

79 |
80 |

81 | Getting set up? 82 |

83 |

84 | You need basic HTML, CSS and JavaScript skills
Have fun with me! 85 | Man's got to code better and relaxed 😋 😎 86 |

87 | 88 | 96 | 97 | 100 |
101 | 104 | 107 |
108 | 109 | 110 | 116 | 117 | 120 | 121 |
122 | 123 | 126 | 127 | 129 | 130 | 141 |
142 | 143 | 144 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /app/src/latest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PWA Fire App - Latest Progressive Web Apps News 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 46 | 51 | 52 | 53 |
54 | 55 | 56 |
57 |

58 | What's coming up here? 59 |

60 |

61 | We are adding cool and amazing latest progressive web news app directory 62 |

63 |
64 | 65 | 73 | 74 |
75 | 76 | 84 | 85 | 86 |
87 |
88 |
89 | 90 | 91 |
92 | 93 | 100 |
101 |
102 | 103 |
104 | 105 |
106 | 107 | 108 | 111 |
112 | 115 | 118 |
119 | 120 | 121 | 127 | 128 | 131 | 132 | 133 | 134 | 137 | 138 | 140 | 141 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/latest/sport1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | [ PWA Fire App - Page 2 ] 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 45 |
46 | pwafireapp-logo 47 |

Yey! I am beta version of PWA Fire App

48 |

This page could be anything of other pages 😜

49 |

Get started here

50 |

Home Page

51 | 52 | 55 |
56 | 59 | 62 |
63 | 64 | 68 | 69 | 70 | 74 | 75 |
76 | 77 | 78 | 79 | 80 | 83 | 84 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/profile/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | [ PWA Fire App - Page 2 ] 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 45 |
46 | pwafireapp-logo 47 |

Yey! I am beta version of PWA Fire App

48 |

This page could be anything of other pages 😜

49 |

Get started here

50 |

Home Page

51 | 52 | 55 |
56 | 59 | 62 |
63 | 64 | 68 | 69 | 70 | 74 | 75 |
76 | 77 | 78 | 79 | 80 | 83 | 84 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/profile/share.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PWA Fire App 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 |

44 | Web Share Target Handler 45 |

46 |

47 | href: 48 |

49 |

50 | Query String Parameters:
51 | title:
52 | text:
53 | url: 54 |

55 | 56 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /app/src/service-worker.js: -------------------------------------------------------------------------------- 1 | 2 | // Authored by Maye Edwin : https://twitter.com/MayeEdwin1 3 | // Add custom cache strategies and routing methods 4 | // pwafire v4.0.1 5 | 6 | importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.1.1/workbox-sw.js'); 7 | 8 | if (workbox) { 9 | console.log('Yay! Workbox is loaded ! Cheers to PWA Fire 🐹'); 10 | workbox.precaching.precacheAndRoute([]); 11 | 12 | /* cache images in the e.g others folder; edit to other folders you got 13 | and config in the sw-config.js file 14 | 15 | -- notes -- 16 | if you are using *networkFirst* cache strategies, 17 | they should come first as in this app 18 | */ 19 | workbox.routing.registerRoute( 20 | /(.*)others(.*)\.(?:|svg|png|gif|jpg)/, 21 | new workbox.strategies.NetworkFirst({ 22 | cacheName: 'images-cache', 23 | plugins: [ 24 | new workbox.expiration.Plugin({ 25 | maxEntries: 50, // max number of images to cache 26 | maxAgeSeconds: 7 * 24 * 60 * 60, // 7 Days 27 | }) 28 | ] 29 | }) 30 | ); 31 | 32 | // cache google fonts 33 | workbox.routing.registerRoute( 34 | new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'), 35 | new workbox.strategies.NetworkFirst({ 36 | cacheName: 'google-fonts', 37 | plugins: [ 38 | new workbox.cacheableResponse.Plugin({ 39 | statuses: [0, 200], 40 | }), 41 | ], 42 | }) 43 | ); 44 | 45 | /* cache resources from a specific subdirectory 46 | -- notes -- 47 | add url for other sub-directories that a user visits depending 48 | on his or her needs "/subdirectory/" as shown below ; *subdirectory* 49 | could be any name eg *latest* if a user shows interest in *latest* categ of a news app; 50 | this means all .html articles in the *latest* route that the user reads, get cached 51 | */ 52 | workbox.routing.registerRoute( 53 | new RegExp('/latest/'), 54 | new workbox.strategies.StaleWhileRevalidate({ 55 | // use a custom cache name 56 | cacheName: 'latest-cache', 57 | plugins: [ 58 | new workbox.expiration.Plugin({ 59 | // max number of items to be cached 60 | maxEntries: 20, 61 | maxAgeSeconds: 7 * 24 * 60 * 60, // 7 Days 62 | }), 63 | ] 64 | }) 65 | ); 66 | 67 | /* Make your JS and CSS ⚡ fast by returning the assets from the cache, 68 | while making sure they are updated in the background for the next use. 69 | */ 70 | workbox.routing.registerRoute( 71 | // cache js, css, scc files 72 | /.*\.(?:css|js|scss|)/, 73 | // use cache but update in the background ASAP 74 | new workbox.strategies.StaleWhileRevalidate({ 75 | // use a custom cache name 76 | cacheName: 'assets-cache', 77 | plugins: [ 78 | new workbox.expiration.Plugin({ 79 | // max number of items to be cached 80 | maxEntries: 20, 81 | }), 82 | ] 83 | }) 84 | ); 85 | 86 | // add offline analytics 87 | workbox.googleAnalytics.initialize(); 88 | 89 | /* Install a new service worker and have it update 90 | and control a web page as soon as possible 91 | */ 92 | 93 | workbox.core.skipWaiting(); 94 | workbox.core.clientsClaim(); 95 | 96 | } else { 97 | console.log("Oops! Workbox didn't load 👺"); 98 | } 99 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ### PWA Fire App 2 | 3 | Before we get started; let us make sure everything is ready and we have all tools needed. 4 | 5 | #### [What you should already know]() 6 | 7 | - Basic HTML, CSS, and JavaScript (ofcourse you are) 8 | - How to run commands from the command line 9 | - Some familiarity with service workers is recommended 10 | - Familiarity with Node.js is recommended 11 | 12 | **NOTE** All the code is commented BTW; Read through to understand each line. It is good to do so! 13 | 14 | #### [What you will need]() 15 | 16 | [PWA Fire App](https://pwafire.org/developer/app) requires Node.js. [Install](https://nodejs.org/en/) the latest long term support (LTS) version if you have not already. 17 | 18 | Make sure you have all this checked before we start; 19 | 20 | - Connection to the internet 21 | - A browser that [supports service worker](https://pwafire.org/developer/tools/browser-test/) 22 | - A text editor; like [VS Code](https://code.visualstudio.com/) 23 | - [Node.js](https://nodejs.org/en/) installed 24 | - [Firebase](https://pwafire.org/developer/codelabs/firebase-hosting-web/) if you need to deploy it 25 | 26 | #### [Get the app](https://github.com/mayeedwin/pwafireapp/archive/master.zip) 27 | 28 | The important directory for our web app is going to be the **src** folder in which you will place all of your project files or start your new progressive web app project from. 29 | 30 | Download [PWA Fire App here](https://github.com/mayeedwin/pwafireapp/archive/master.zip) 31 | 32 | #### [Install project dependencies and start the server]() 33 | 34 | 1. Navigate to the app directory via the command line: 35 | 36 | cd pwafireapp-master 37 | cd app 38 | 39 | 2. Run the following commands to install the project dependencies: 40 | 41 | npm install 42 | 43 | 3. Then build and serve the app with these commands: 44 | 45 | npm run build 46 | npm run start 47 | 48 | The *npm install* command installs the project dependencies based on the configuration in **package.json.** Open **../app/package.json** and examine its contents. Get the [explanation here](https://pwafire.org/developer/pwa/started/#sw-config-for-node) 49 | 50 |

NOTE : Checkout for the latest workbox release here and update the importScripts below say 3.6.1 to 3.6.2 if it's available and update the service-worker.js file in the src folder.

51 | 52 | Once you have started the server, open the browser and navigate to http://localhost:8081/ to view the app. The app is a simple one page progressive web app which just showcases a working PWA Fire App. 53 | 54 | #### [Add your project source files or start a new project]() 55 | 56 | Open the **src** folder in your text editor. The **src** folder is where you will be building your progressive web app or copy all your projects source files as in the app structure. 57 | 58 | Find demo [PWA Fire App here](https://pwafireapp.firebaseapp.com) 59 | 60 | 1. Add or create your source files as in the **app structure** shown below. For example, you will place all **.css** files in the **css** folder. 61 | 62 | ```bash 63 | 64 | │   ├── src 65 | │   ├── assets 66 | | ├── css 67 | | ├── js 68 | | ├── scss 69 | │   ├── images 70 | | ├── icons 71 | | ├── others 72 | | ├── pages 73 | │   ├── index.html 74 | │   ├── app.webmanifest 75 | │   ├── service-worker.js 76 | 77 | ``` 78 | 79 | 2. For the **images** folder, make sure you have all your app icons in your project as shown below and copy all images to it; The **icons** should be of sizes as in the **app.webmanifest** file. Atleast have an icon size of 144x144 is required for our progressive web app to install. 80 | 81 | 3. **Generate** your progressive [web app icons here](https://app-manifest.firebaseapp.com/). Unzip the icons and copy them to the **icons** folder of the [PWA Fire App](https://pwafire.org/developer/app). Open **app.webmanifest** and configure your web app name, short name and theme color **(must be the same as specified in the *index.html*).** 82 | 83 | 4. For the **others** folder; You could rename it to say **team** and add images to it but make sure to update that in the **service-worker.js** as shown below; Just an example of extra folders you may want to add to your project or your project has. 84 | 85 | ```javascript 86 | 87 | /* cache images in the e.g others folder; edit to other folders you got 88 | and config in the sw-config.js file 89 | */ 90 | workbox.routing.registerRoute( 91 | /(.*)others(.*)\.(?:png|gif|jpg)/, 92 | workbox.strategies.cacheFirst({ 93 | cacheName: 'images', 94 | plugins: [ 95 | new workbox.expiration.Plugin({ 96 | maxEntries: 50, 97 | maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days 98 | }) 99 | ] 100 | }) 101 | ); 102 | 103 | ``` 104 | 105 | 5. To make your progressive web app more discoverable and your pages built with best web practices, configure your pages as in the **head** tag of the default **index.html** in the **src** folder. 106 | 107 | ### [Build your progressive web app]() 108 | 109 | After configuring your project to the app structure above; let us build and even deploy it! 110 | Then build and serve the app with these commands: 111 | 112 | npm run build 113 | npm run start 114 | 115 | The web app files are copied over to the **build** folder when the **npm run build** command is run, and the server (is started with **npm run start**) serves these files from the **build** directory. 116 | 117 | **Alternatively**, you can test your web app by running `firebase serve` in the app root folder and host with [firebase](https://firebase.google.com/docs/web/setup) as in [this codelab](https://pwafire.org/developer/codelabs/firebase-hosting-web/). 118 | 119 | You are done! Welcome to PWAs World! 120 | 121 | ### [Engage us](https://twitter.com/pwafire) 122 | Donate a star, like, follow and contribute in any way. You got any **bug?** Report it [here for support.](https://github.com/mayeedwin/pwafireapp/issues/new) You want to contribute? Create your [feature here.](https://github.com/mayeedwin/pwafireapp/issues/new) 123 | --------------------------------------------------------------------------------