├── public ├── images │ ├── icon.jpg │ ├── like.png │ ├── dislike.png │ └── icons │ │ ├── icon-72x72.png │ │ ├── icon-96x96.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ └── icon-512x512.png ├── manifest.json ├── app.js ├── firebase-messaging-sw.js └── index.html ├── .gitattributes ├── README.md ├── package.json ├── .gitignore ├── api.js └── bin └── www /public/images/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icon.jpg -------------------------------------------------------------------------------- /public/images/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/like.png -------------------------------------------------------------------------------- /public/images/dislike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/dislike.png -------------------------------------------------------------------------------- /public/images/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-72x72.png -------------------------------------------------------------------------------- /public/images/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-96x96.png -------------------------------------------------------------------------------- /public/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /public/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /public/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /public/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/images/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-384x384.png -------------------------------------------------------------------------------- /public/images/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houssem-yahiaoui/progressive-web-app-starter-kit/HEAD/public/images/icons/icon-512x512.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Progressive Web Application Starter Pack 2 | 3 | [![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=102)](https://github.com/ellerbrock/open-source-badge/) 4 | [![Open Source Love](https://badges.frapsoft.com/os/mit/mit.svg?v=102)](https://github.com/ellerbrock/open-source-badge/) 5 | 6 | *Coming soon ...* 7 | 8 | [![forthebadge](http://forthebadge.com/badges/built-with-love.svg)](https://github.com/houssem-yahiaoui/) 9 | 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwa-starter-kit", 3 | "version": "0.0.6", 4 | "description": "Your typical Progressive Web Application Starter Pack", 5 | "author": "Houssem Yahiaoui ", 6 | "scripts": { 7 | "start": "node ./bin/www" 8 | }, 9 | "main": "api.js", 10 | "repository" : { 11 | "type" : "git", 12 | "url" : "https://github.com/houssem-yahiaoui/progressive-web-app-starter-kit.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/houssem-yahiaoui/progressive-web-app-starter-kit/issues" 16 | }, 17 | "keywords": [ 18 | "notifcation", 19 | "nodejs", 20 | "js", 21 | "es6", 22 | "serviceworker", 23 | "pwa" 24 | ], 25 | "dependencies": { 26 | "express": "^4.13.4", 27 | "fcm-push": "^1.1.2", 28 | "socket.io": "^1.4.6" 29 | }, 30 | "license": "MIT" 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | node_modules 49 | server.js 50 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Progressive Web Application ", 3 | "gcm_sender_id": "103953800507", 4 | "short_name": "PWA", 5 | "theme_color": "#175f99", 6 | "background_color": "#544851", 7 | "display": "browser", 8 | "Scope": "/", 9 | "start_url": "/", 10 | "icons": [ 11 | { 12 | "src": "images/icons/icon-72x72.png", 13 | "sizes": "72x72", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "images/icons/icon-96x96.png", 18 | "sizes": "96x96", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "images/icons/icon-128x128.png", 23 | "sizes": "128x128", 24 | "type": "image/png" 25 | }, 26 | { 27 | "src": "images/icons/icon-144x144.png", 28 | "sizes": "144x144", 29 | "type": "image/png" 30 | }, 31 | { 32 | "src": "images/icons/icon-152x152.png", 33 | "sizes": "152x152", 34 | "type": "image/png" 35 | }, 36 | { 37 | "src": "images/icons/icon-192x192.png", 38 | "sizes": "192x192", 39 | "type": "image/png" 40 | }, 41 | { 42 | "src": "images/icons/icon-384x384.png", 43 | "sizes": "384x384", 44 | "type": "image/png" 45 | }, 46 | { 47 | "src": "images/icons/icon-512x512.png", 48 | "sizes": "512x512", 49 | "type": "image/png" 50 | } 51 | ], 52 | "splash_pages": null 53 | } 54 | -------------------------------------------------------------------------------- /api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const app = express(); 5 | const FCM = require('fcm-push'); 6 | 7 | app.io = require('socket.io')(); 8 | 9 | // [*] Configuring our static files. 10 | app.use(express.static('public/')); 11 | 12 | // [*] Configuring Routes. 13 | app.get('/', (req, res) => { 14 | res.sendFile(__dirname + '/public/index.html'); 15 | }); 16 | 17 | // [*] Configuring our Socket Connection. 18 | app.io.on('connection', socket => { 19 | console.log('Huston ! we have a new connection ...'); 20 | socket.on('new_user', (endpoint) => { 21 | // [*] TODO: Adding our user notification registration token to our list typically hide it in a secret place. like a DB 22 | // or some secure server because this information is critical to you users. 23 | }); 24 | 25 | socket.on('pushme', (data) => { 26 | var serverKey = ''; 27 | var fcm = new FCM(''); 28 | var message = { 29 | to: data.endpoint, // required fill with device token or topics 30 | notification: { 31 | title: data.payload.title, 32 | body: data.payload.body 33 | } 34 | }; 35 | 36 | fcm.send(message) 37 | .then(function(response) { 38 | //TODO : Implement success mechanism 39 | }) 40 | .catch(function(err) { 41 | //TODO : implement error handling mechanism 42 | }) 43 | }); 44 | 45 | }); 46 | 47 | 48 | module.exports = app 49 | -------------------------------------------------------------------------------- /public/app.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | apiKey: "", 3 | authDomain: "", 4 | databaseURL: "", 5 | storageBucket: "", 6 | messagingSenderId: "" 7 | }; 8 | firebase.initializeApp(config); 9 | 10 | const messaging = firebase.messaging(); 11 | messaging.requestPermission() 12 | .then(() => { 13 | return messaging.getToken(); 14 | }) 15 | .then((token) => { 16 | console.log(token); 17 | document.getElementById('endpoint').innerHTML = token 18 | socket.emit("new_user", token); 19 | document.getElementById('push').onclick = function() { 20 | title = document.getElementById('push_title'); 21 | payload = document.getElementById('push_message'); 22 | console.log("About to send :" + token, " + ", "payload:", payload.value); 23 | console.log('sending push'); 24 | socket.emit('pushme', { 25 | endpoint: token, 26 | payload: { 27 | title : title.value, 28 | body : payload.value 29 | } 30 | }); 31 | } 32 | }) 33 | .catch(function(err) { 34 | //TODO : Implement propoer error handling 35 | }); 36 | 37 | //[*] Showasing a model insteam of a notification when user is physicaly on the page. 38 | messaging.onMessage((notif) => { 39 | console.log(notif); 40 | var dialog = document.querySelector('dialog'); 41 | if (!dialog.showModal) { 42 | dialogPolyfill.registerDialog(dialog); 43 | } 44 | document.getElementById('message').innerHTML = notif.notification.body; 45 | document.getElementById('title').innerHTML = notif.notification.title; 46 | dialog.showModal(); 47 | dialog.querySelector('.close').addEventListener('click', () => { 48 | dialog.close(); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../api'); 8 | var debug = require('debug')('push-notification:server'); 9 | var http = require('http'); 10 | var io = app.io; 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | io.attach( server ); 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /public/firebase-messaging-sw.js: -------------------------------------------------------------------------------- 1 | //[*] Importing Firebase Needed Dependecies 2 | importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-app.js'); 3 | importScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-messaging.js'); 4 | 5 | // [*] Firebase Configurations 6 | var config = { 7 | apiKey: "", 8 | authDomain: "", 9 | databaseURL: "", 10 | storageBucket: "", 11 | messagingSenderId: "" 12 | }; 13 | 14 | //[*] Initializing our Firebase Application. 15 | firebase.initializeApp(config); 16 | 17 | // [*] Initislaizing the Firebase Messaging Object. 18 | const messaging = firebase.messaging(); 19 | 20 | // [*] SW Install State Event. 21 | self.addEventListener('install', (event) => { 22 | //TODO: implement a caching start toastr 23 | //[*] Let's cache a bit ! 24 | event.waitUntil( 25 | caches.open('pwa').then((cache) => { 26 | return cache.addAll([ 27 | '/', 28 | '/index.html', 29 | '/app.js' 30 | ]).then(() => { 31 | self.skipWaiting(); 32 | //TODO: implement success caching process toastr 33 | }); 34 | }) 35 | ); 36 | }); 37 | 38 | // [*] SW Activate State Event. 39 | self.addEventListener('activate',(event) => { 40 | //TODO : implmenet proper handling 41 | }); 42 | 43 | // [*] SW Fetch Event. 44 | self.addEventListener('fetch', (event) => { 45 | event.respondWith( 46 | caches.match(event.request).then(response => { 47 | return response || fetch(event.request); 48 | }) 49 | ); 50 | }); 51 | 52 | // [*] Special object let us handle our Background Push Notifications 53 | messaging.setBackgroundMessageHandler((payload) => { 54 | const notificationOptions = { 55 | body: payload.data.msg, 56 | icon: "images/icon.jpg" 57 | } 58 | self.addEventListener('notificationclick', (event) => { 59 | var messageId = event.notification.data; 60 | 61 | event.notification.close(); 62 | 63 | if (event.action === 'like') { 64 | //TODO : implmenet proper handling 65 | } else if (event.action === 'dislike') { 66 | //TODO : implmenet proper handling 67 | } else { 68 | //TODO : implmenet proper handling 69 | } 70 | }, false); 71 | return self.registration.showNotification(payload.data.title, 72 | notificationOptions); 73 | }); 74 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PWA 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 | PWA | Progressive Web Applications ! 19 | 20 |
21 | 22 | 25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 | 35 |
36 |
37 | 38 | 39 |
40 | 43 |
44 |
    45 |
  • 46 | Application Reg_Id : 47 |
  • 48 |
49 |
50 |
51 | 52 |

53 |
54 |

55 |
56 |
57 | 58 | 59 |
60 |
61 |
62 |
63 | 64 | 72 |
73 | 74 | 80 | 81 | 82 | 83 | --------------------------------------------------------------------------------