├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .firebaserc ├── .gitignore ├── __mocks__ └── fileMock.js ├── database.rules.json ├── favicon.ico ├── firebase.json ├── functions ├── index.js └── package.json ├── index.html ├── manifest.json ├── package.json ├── public ├── app.js ├── favicon.ico ├── images │ ├── icon-128x128.png │ ├── icon-144x144.png │ ├── icon-152x152.png │ ├── icon-192x192.png │ ├── icon-384x384.png │ ├── icon-512x512.png │ ├── icon-72x72.png │ └── icon-96x96.png ├── index.html ├── manifest.js ├── manifest.json ├── styles.css ├── sw.js └── vendor.js ├── src ├── components │ ├── App.js │ ├── App.spec.js │ ├── __snapshots__ │ │ └── App.spec.js.snap │ ├── card-item │ │ ├── CardItem.js │ │ ├── CardItem.spec.js │ │ ├── __snapshots__ │ │ │ └── CardItem.spec.js.snap │ │ └── styles.scss │ ├── cards │ │ ├── Cards.js │ │ ├── Cards.spec.js │ │ ├── __snapshots__ │ │ │ └── Cards.spec.js.snap │ │ └── styles.scss │ ├── header │ │ ├── Header.js │ │ ├── Header.spec.js │ │ ├── __snapshots__ │ │ │ └── Header.spec.js.snap │ │ └── styles.scss │ └── styles.scss ├── images │ ├── icons │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ ├── icon-384x384.png │ │ ├── icon-512x512.png │ │ ├── icon-72x72.png │ │ └── icon-96x96.png │ └── svgs │ │ ├── back.svg │ │ ├── logo-white.svg │ │ └── logo.svg └── index.js ├── sw.js ├── webpack.config.babel.js ├── webpack.dev.config.babel.js └── webpack.prod.config.babel.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"] 3 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,py}] 8 | charset = utf-8 9 | 10 | indent_style = space 11 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.spec.js 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb", 4 | "rules": { 5 | "react/jsx-filename-extension": 0, 6 | "linebreak-style": "off", 7 | "class-methods-use-this": "off" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "planning-poker-5ea54" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | ".read": "auth != null", 4 | ".write": "auth != null" 5 | } 6 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/favicon.ico -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "rules": "database.rules.json" 4 | }, 5 | "hosting": { 6 | "public": "public", 7 | "rewrites": [ 8 | { 9 | "source": "**", 10 | "destination": "/index.html" 11 | } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | var functions = require('firebase-functions'); 2 | 3 | // // Start writing Firebase Functions 4 | // // https://firebase.google.com/functions/write-firebase-functions 5 | // 6 | // exports.helloWorld = functions.https.onRequest((request, response) => { 7 | // response.send("Hello from Firebase!"); 8 | // }) 9 | -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "dependencies": { 5 | "firebase-admin": "^4.1.2", 6 | "firebase-functions": "^0.5" 7 | }, 8 | "private": true 9 | } 10 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Agile Poker 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AgilePoker", 3 | "short_name": "AgilePoker", 4 | "theme_color": "#2a0044", 5 | "background_color": "#2a0044", 6 | "display": "standalone", 7 | "start_url": "/", 8 | "icons": [ 9 | { 10 | "src": "images/icon-72x72.png", 11 | "sizes": "72x72", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "images/icon-96x96.png", 16 | "sizes": "96x96", 17 | "type": "image/png" 18 | }, 19 | { 20 | "src": "images/icon-128x128.png", 21 | "sizes": "128x128", 22 | "type": "image/png" 23 | }, 24 | { 25 | "src": "images/icon-144x144.png", 26 | "sizes": "144x144", 27 | "type": "image/png" 28 | }, 29 | { 30 | "src": "images/icon-152x152.png", 31 | "sizes": "152x152", 32 | "type": "image/png" 33 | }, 34 | { 35 | "src": "images/icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image/png" 38 | }, 39 | { 40 | "src": "images/icon-384x384.png", 41 | "sizes": "384x384", 42 | "type": "image/png" 43 | }, 44 | { 45 | "src": "images/icon-512x512.png", 46 | "sizes": "512x512", 47 | "type": "image/png" 48 | } 49 | ], 50 | "splash_pages": null 51 | } 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwa-planning-poker", 3 | "version": "1.0.0", 4 | "description": "A Planning Poker Progressive Web Apps for Agile meetings", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --hot", 8 | "build": "webpack", 9 | "test": "jest", 10 | "lint": "eslint src", 11 | "check": "npm run test && npm run lint" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "classnames": "^2.2.5", 17 | "preact": "^7.2.0", 18 | "preact-compat": "^3.14.0", 19 | "react": "^15.4.2", 20 | "react-dom": "^15.4.2" 21 | }, 22 | "devDependencies": { 23 | "babel-core": "^6.21.0", 24 | "babel-eslint": "^7.1.1", 25 | "babel-jest": "^19.0.0", 26 | "babel-loader": "^6.2.10", 27 | "babel-preset-es2015": "^6.18.0", 28 | "babel-preset-react": "^6.16.0", 29 | "clean-webpack-plugin": "^0.1.15", 30 | "copy-webpack-plugin": "^4.0.1", 31 | "css-loader": "^0.26.1", 32 | "eslint": "^3.13.1", 33 | "eslint-config-airbnb": "^14.0.0", 34 | "eslint-loader": "^1.6.1", 35 | "eslint-plugin-import": "^2.2.0", 36 | "eslint-plugin-jsx-a11y": "^3.0.2", 37 | "eslint-plugin-react": "^6.9.0", 38 | "extract-text-webpack-plugin": "^1.0.1", 39 | "html-webpack-plugin": "^2.26.0", 40 | "identity-obj-proxy": "^3.0.0", 41 | "jest": "^19.0.2", 42 | "react-test-renderer": "^15.4.2", 43 | "sass-loader": "^4.1.1", 44 | "style-loader": "^0.13.1", 45 | "url-loader": "^0.5.7", 46 | "webpack": "^1.14.0", 47 | "webpack-dev-server": "^1.16.2" 48 | }, 49 | "jest": { 50 | "moduleNameMapper": { 51 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/fileMock.js", 52 | "\\.(css|scss)$": "identity-obj-proxy" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /public/app.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([0,2],[function(M,e,u){"use strict";function t(M){return M&&M.__esModule?M:{default:M}}var j=u(1),A=t(j),I=u(1),g=t(I),a=u(5),D=t(a);g.default.render(A.default.createElement(D.default,null),document.getElementById("app"))},,,,,function(M,e,u){"use strict";function t(M){return M&&M.__esModule?M:{default:M}}Object.defineProperty(e,"__esModule",{value:!0});var j=u(1),A=t(j),I=u(6),g=t(I),a=u(12),D=t(a);u(21);var n=function(){return A.default.createElement("div",null,A.default.createElement(g.default,null),A.default.createElement(D.default,null))};e.default=n},function(M,e,u){"use strict";function t(M){return M&&M.__esModule?M:{default:M}}Object.defineProperty(e,"__esModule",{value:!0});var j=u(1),A=t(j),I=u(7),g=t(I),a=u(8),D=t(a),n=function(){return A.default.createElement("header",{className:D.default.header},A.default.createElement("div",{className:D.default.content},A.default.createElement("figure",null,A.default.createElement("img",{src:g.default,alt:"Logo brand"})),A.default.createElement("h1",null,"AgilePoker")))};e.default=n},function(M,e){M.exports="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjYiIGhlaWdodD0iMjYiIHZpZXdCb3g9IjAgMCAyNiAyNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+QzQ5NTVGNUMtMEE2NS00RTNCLUE4ODctMDE0QzY0NTEzRDdFPC90aXRsZT48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik05LjczNSAxNC4zNzlhMS43OTcgMS43OTcgMCAwIDEtMS41MDQuODI2Yy0uNjEyIDAtMS4xOC0uMzIxLTEuNTA0LS44MjZoMy4wMDh6bS0xLjUwNCAxLjI4N2MuODY3IDAgMS42NjMtLjUxIDIuMDMtMS4yODdoLjE5OWEuMjMuMjMgMCAxIDAgMC0uNDYySDYuMDAzYS4yMy4yMyAwIDAgMCAwIC40NjJoLjE5OGEyLjI2MSAyLjI2MSAwIDAgMCAyLjAzIDEuMjg3ek0xNy43NzUgMTUuMjA1Yy0uNjEyIDAtMS4xOC0uMzIxLTEuNTA0LS44MjZoMy4wMDhhMS43OTcgMS43OTcgMCAwIDEtMS41MDQuODI2bTIuMjI4LTEuMjg4aC00LjQ1N2EuMjMuMjMgMCAwIDAgMCAuNDYyaC4xOThhMi4yNjEgMi4yNjEgMCAwIDAgMi4wMyAxLjI4OGMuODY3IDAgMS42NjQtLjUxMiAyLjAzMS0xLjI4OGguMTk4YS4yMy4yMyAwIDAgMCAwLS40NjIiLz48cGF0aCBkPSJNOS44MDYgMTguNDIzaC4zNTlhLjIzLjIzIDAgMCAwIC4yMy0uMjMuOTUuOTUgMCAwIDEgLjk1LS45NWgzLjMxN2EuOTUuOTUgMCAwIDEgLjk0OS45NWMwIC4xMjcuMTAzLjIzLjIzLjIzaC4zNmEuOTUuOTUgMCAwIDEgLjkyLjcxOEg4Ljg4NWEuOTUuOTUgMCAwIDEgLjkyLS43MThtOC45NjcuNzE4aC0xLjE4YTEuNDEyIDEuNDEyIDAgMCAwLTEuMzkyLTEuMThoLS4xNDdhMS40MTMgMS40MTMgMCAwIDAtMS4zOTEtMS4xNzloLTEuNDI4VjguMDUxaDQuMjJsLTEuNzc3IDUuMzE2YS4yMzEuMjMxIDAgMCAwIC40MzguMTQ2bDEuNjYtNC45NjUgMS42NiA0Ljk2NWEuMjMuMjMgMCAwIDAgLjQzOC0uMTQ2bC0xLjc3OC01LjMxNmguMzc4YS4yMy4yMyAwIDEgMCAwLS40NjJoLTUuMjRWNi4yMzhoMS4xMjljLjM2MiAwIC42NTcuMjk1LjY1Ny42NTcgMCAuMTI4LjEwMy4yMy4yMy4yM2guN2EuMjMuMjMgMCAwIDAgMC0uNDZoLS40OTNhMS4xMiAxLjEyIDAgMCAwLTEuMDk0LS44ODhoLTEuMTI4di0uMjQyYS4yMy4yMyAwIDAgMC0uNDYyIDB2LjI0MmgtMS4xMjdhMS4xMiAxLjEyIDAgMCAwLTEuMDk1Ljg4N2gtLjQ5MmEuMjMuMjMgMCAwIDAgMCAuNDYyaC42OTlhLjIzLjIzIDAgMCAwIC4yMy0uMjNjMC0uMzYzLjI5NS0uNjU4LjY1OC0uNjU4aDEuMTI3djEuMzUxaC01LjI0YS4yMy4yMyAwIDAgMCAwIC40NjJoLjM3OWwtMS43NzcgNS4zMTZhLjIzLjIzIDAgMCAwIC40MzcuMTQ2bDEuNjYtNC45NjUgMS42NiA0Ljk2NWEuMjMuMjMgMCAwIDAgLjQzOC0uMTQ2TDguNTUyIDguMDUxaDQuMjJ2OC43MzFoLTEuNDI4Yy0uNjk5IDAtMS4yOC41MTEtMS4zOTEgMS4xOGgtLjE0N2MtLjcgMC0xLjI4MS41MTEtMS4zOTEgMS4xOEg3LjIzNGEuMjMuMjMgMCAwIDAgMCAuNDZoMTEuNTM4YS4yMy4yMyAwIDAgMCAwLS40NiIvPjxwYXRoIGQ9Ik0xMyAuNjE1YTEyLjMwMyAxMi4zMDMgMCAwIDAtOC43NTcgMy42MjhBMTIuMzAzIDEyLjMwMyAwIDAgMCAuNjE1IDEzYzAgMy4zMDggMS4yODkgNi40MTggMy42MjggOC43NTdBMTIuMzAzIDEyLjMwMyAwIDAgMCAxMyAyNS4zODVjMy4zMDggMCA2LjQxOC0xLjI4OSA4Ljc1Ny0zLjYyOEExMi4zMDMgMTIuMzAzIDAgMCAwIDI1LjM4NSAxM2MwLTMuMzA4LTEuMjg5LTYuNDE4LTMuNjI4LTguNzU3QTEyLjMwMyAxMi4zMDMgMCAwIDAgMTMgLjYxNU0xMyAyNmExMi45MTUgMTIuOTE1IDAgMCAxLTkuMTkyLTMuODA4QTEyLjkxNSAxMi45MTUgMCAwIDEgMCAxM2MwLTMuNDcyIDEuMzUyLTYuNzM3IDMuODA4LTkuMTkyQTEyLjkxNSAxMi45MTUgMCAwIDEgMTMgMGMzLjQ3MiAwIDYuNzM3IDEuMzUyIDkuMTkyIDMuODA4QTEyLjkxNSAxMi45MTUgMCAwIDEgMjYgMTNjMCAzLjQ3Mi0xLjM1MiA2LjczNy0zLjgwOCA5LjE5MkExMi45MTUgMTIuOTE1IDAgMCAxIDEzIDI2Ii8+PC9nPjwvc3ZnPg=="},function(M,e){M.exports={header:"fyeEC",content:"_35nvu"}},,,,function(M,e,u){"use strict";function t(M){return M&&M.__esModule?M:{default:M}}function j(M,e){if(!(M instanceof e))throw new TypeError("Cannot call a class as a function")}function A(M,e){if(!M)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?M:e}function I(M,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);M.prototype=Object.create(e&&e.prototype,{constructor:{value:M,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(M,e):M.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var g=function(){function M(M,e){for(var u=0;u 2 | 3 | 4 | 5 | 6 | 7 | Agile Poker 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /public/manifest.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(n){if(r[n])return r[n].exports;var a=r[n]={exports:{},id:n,loaded:!1};return e[n].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n=window.webpackJsonp;window.webpackJsonp=function(o,p){for(var l,c,s=0,i=[];sfigure{margin:0 1em;float:left}._35nvu img{width:2em}._35nvu>h1{color:#fff;font-weight:300;font-size:1.4em}._1Oqix{position:relative;display:inline-block;background-color:#fafafa;border-radius:4px;width:5em;height:8em;cursor:pointer;margin:.4em;border:1px solid rgba(41,0,68,.7);box-shadow:0 0 2px 0 rgba(0,0,0,.5)}._1Oqix:hover{opacity:.75}._3Q6ZY{width:9.5em;height:15em;box-shadow:0 0 10px 0 rgba(0,0,0,.2)}._1Oqix>span{position:absolute;font-size:2.5em;color:#2a0044;top:50%;left:50%;transform:translate(-50%,-60%)}._17if_{bottom:0;left:-1.5em}._2vUUx,._17if_{position:absolute}._2vUUx{margin:0 auto;left:0;right:0;top:11em}._3Q6ZY>span{font-size:5.5em;font-weight:300}._1E2AT{display:flex;flex-flow:row wrap;max-width:28em;padding:3em 0;margin:0 auto;animation:_3IFwn .7s ease-in-out}.hLcrF{margin:2em;text-align:center}._1Udwa{animation:_3IFwn 1s ease-in-out}._2kd9t{animation:hvgzA 1s ease-in-out}.SEHnH{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.Rykdh{position:relative;line-height:.5;width:165px;margin:0 auto}.Rykdh>p{font-size:14px;font-weight:700;font-style:oblique;color:#2a0044}.h4aMm{padding-top:2em}.h4aMm:before{position:absolute;left:-.5em;top:.2em;content:"\201C";font-size:60px;font-weight:700;color:#999}.h4aMm:after{content:""}@keyframes _3IFwn{0%{opacity:0}to{opacity:1}}@keyframes hvgzA{0%{opacity:0}to{opacity:1}}body{background-color:#f0f0f0;font-family:Helvetica Neue,Helvetica,Arial,sans-serif} -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var CACHE_NAME = 'static-v1'; 4 | 5 | self.addEventListener('install', function (event) { 6 | event.waitUntil( 7 | caches.open(CACHE_NAME).then(function (cache) { 8 | return cache.addAll([ 9 | '/', 10 | '/index.html', 11 | '/styles.css', 12 | '/app.js', 13 | '/manifest.js', 14 | '/vendor.js', 15 | ]); 16 | }) 17 | ) 18 | }); 19 | 20 | self.addEventListener('activate', function activator(event) { 21 | event.waitUntil( 22 | caches.keys().then(function (keys) { 23 | return Promise.all(keys 24 | .filter(function (key) { 25 | return key.indexOf(CACHE_NAME) !== 0; 26 | }) 27 | .map(function (key) { 28 | return caches.delete(key); 29 | }) 30 | ); 31 | }) 32 | ); 33 | }); 34 | 35 | self.addEventListener('fetch', function (event) { 36 | event.respondWith( 37 | caches.match(event.request).then(function (cachedResponse) { 38 | return cachedResponse || fetch(event.request); 39 | }) 40 | ); 41 | }); 42 | -------------------------------------------------------------------------------- /public/vendor.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([1,2],[function(e,t,n){n(1),e.exports=n(1)},function(e,t,n){(function(t){!function(t,r){e.exports=r(n(3),n(4))}(this,function(e,n){function r(){return null}function o(e){var t=e.nodeName,n=e.attributes;e.attributes={},t.defaultProps&&_(e.attributes,t.defaultProps),n&&_(e.attributes,n)}function i(e,t){var n,r,o;if(t){for(o in t)if(n=B.test(o))break;if(n){r=e.attributes={};for(o in t)t.hasOwnProperty(o)&&(r[B.test(o)?o.replace(/([A-Z0-9])/,"-$1").toLowerCase():o]=t[o])}}}function a(e,t,r){var o=t&&t._preactCompatRendered&&t._preactCompatRendered.base;o&&o.parentNode!==t&&(o=null),o||(o=t.children[0]);for(var i=t.childNodes.length;i--;)t.childNodes[i]!==o&&t.removeChild(t.childNodes[i]);var a=n.render(e,t,o);return t&&(t._preactCompatRendered=a&&(a._component||{base:a})),"function"==typeof r&&r(),a&&a._component||a}function u(e,t,r,o){var i=n.h(J,{context:e.context},t),u=a(i,r);return o&&o(u),u._component||u.base}function l(e){var t=e._preactCompatRendered&&e._preactCompatRendered.base;return!(!t||t.parentNode!==e)&&(n.render(n.h(r),e,t),!0)}function c(e){return h.bind(null,e)}function s(e,t){for(var n=t||0;n0;)r[o]=arguments[o+2];if(!y(e))return e;var i=e.attributes||e.props,a=n.h(e.nodeName||e.type,i,e.children||i&&i.children),u=[a,t];return r&&r.length?u.push(r):t&&t.children&&u.push(t.children),m(n.cloneElement.apply(void 0,u))}function y(e){return e&&(e instanceof H||e.$$typeof===L)}function b(e,t){return t._refProxies[e]||(t._refProxies[e]=function(n){t&&t.refs&&(t.refs[e]=n,null===n&&(delete t._refProxies[e],t=null))})}function g(e){var t=e.nodeName,n=e.attributes;if(n&&"string"==typeof t){var r={};for(var o in n)r[o.toLowerCase()]=o;if(r.ondoubleclick&&(n.ondblclick=n[r.ondoubleclick],delete n[r.ondoubleclick]),r.onchange&&("textarea"===t||"input"===t.toLowerCase()&&!/^fil|che|rad/i.test(n.type))){var i=r.oninput||"oninput";n[i]||(n[i]=A([n[i],n[r.onchange]]),delete n[r.onchange])}}}function x(e){var t=e.attributes;if(t){var n=t.className||t.class;n&&(t.className=n)}}function _(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function C(e,t){for(var n in e)if(!(n in t))return!0;for(var r in t)if(e[r]!==t[r])return!0;return!1}function N(e){return e&&e.base||e}function w(){}function k(e){function t(e,t){E(this),I.call(this,e,t,$),O.call(this,e,t)}return e=_({constructor:t},e),e.mixins&&S(e,P(e.mixins)),e.statics&&_(t,e.statics),e.propTypes&&(t.propTypes=e.propTypes),e.defaultProps&&(t.defaultProps=e.defaultProps),e.getDefaultProps&&(t.defaultProps=e.getDefaultProps()),w.prototype=I.prototype,t.prototype=_(new w,e),t.displayName=e.displayName||"Component",t}function P(e){for(var t={},n=0;n1)for(var n=1;n2;)L.push(arguments[u]);for(n&&n.children&&(L.length||L.push(n.children),delete n.children);L.length;)if((i=L.pop())instanceof Array)for(u=i.length;u--;)L.push(i[u]);else null!=i&&i!==!0&&i!==!1&&("number"==typeof i&&(i=String(i)),a="string"==typeof i,a&&o?r[r.length-1]+=i:((r||(r=[])).push(i),o=a));var l=new t(e,n||void 0,r||V);return M.vnode&&M.vnode(l),l}function r(e,t){if(t)for(var n in t)e[n]=t[n];return e}function o(e){return r({},e)}function i(e,t){for(var n=t.split("."),r=0;r2?[].slice.call(arguments,2):e.children)}function s(e,t,n){var r=t.split(".");return function(t){for(var o=t&&t.target||this,a={},l=a,c=u(n)?i(t,n):o.nodeName?o.type.match(/^che|rad/)?o.checked:o.value:t,s=0;s=h?e.appendChild(l):l!==c[g]&&(l===c[g+1]&&b(c[g]),e.insertBefore(l,c[g]||null)))}if(p)for(var g in f)f[g]&&E(f[g]);for(;d<=v;)l=s[v--],l&&E(l)}function E(e,t){var n=e._component;if(n)I(n,!t);else{e[q]&&e[q].ref&&e[q].ref(null),t||C(e);for(var r;r=e.lastChild;)E(r,t)}}function T(e,t,n){var r;for(r in n)t&&r in t||null==n[r]||g(e,r,n[r],n[r]=void 0,Y);if(t)for(r in t)"children"===r||"innerHTML"===r||r in n&&t[r]===("value"===r||"checked"===r?e[r]:n[r])||g(e,r,n[r],n[r]=t[r],Y)}function A(e){var t=e.constructor.name,n=te[t];n?n.push(e):te[t]=[e]}function O(e,t,n){var r=new e(t,n),o=te[e.name];if(W.call(r,t,n),o)for(var i=o.length;i--;)if(o[i].constructor===e){r.nextBase=o[i].nextBase,o.splice(i,1);break}return r}function U(e,t,n,r,o){e._disable||(e._disable=!0,(e.__ref=t.ref)&&delete t.ref,(e.__key=t.key)&&delete t.key,!e.base||o?e.componentWillMount&&e.componentWillMount():e.componentWillReceiveProps&&e.componentWillReceiveProps(t,r),r&&r!==e.context&&(e.prevContext||(e.prevContext=e.context),e.context=r),e.prevProps||(e.prevProps=e.props),e.props=t,e._disable=!1,0!==n&&(1!==n&&M.syncComponentUpdates===!1&&e.base?f(e):j(e,1,o)),e.__ref&&e.__ref(e))}function j(e,t,n,i){if(!e._disable){var u,l,c,s,f=e.props,p=e.state,m=e.context,v=e.prevProps||f,b=e.prevState||p,g=e.prevContext||m,x=e.base,_=e.nextBase,C=x||_,N=e._component;if(x&&(e.props=v,e.state=b,e.context=g,2!==t&&e.shouldComponentUpdate&&e.shouldComponentUpdate(f,p,m)===!1?u=!0:e.componentWillUpdate&&e.componentWillUpdate(f,p,m),e.props=f,e.state=p,e.context=m),e.prevProps=e.prevState=e.prevContext=e.nextBase=null,e._dirty=!1,!u){for(e.render&&(l=e.render(f,p,m)),e.getChildContext&&(m=r(o(m),e.getChildContext()));d(l);)l=h(l,m);var P,S,T=l&&l.nodeName;if(a(T)){var A=y(l);c=N,c&&c.constructor===T&&A.key==c.__key?U(c,A,1,m):(P=c,c=O(T,A,m),c.nextBase=c.nextBase||_,c._parentComponent=e,e._component=c,U(c,A,0,m),j(c,1,n,!0)),S=c.base}else s=C,P=N,P&&(s=e._component=null),(C||1===t)&&(s&&(s._component=null),S=k(s,l,m,n||!x,C&&C.parentNode,!0));if(C&&S!==C&&c!==N){var R=C.parentNode;R&&S!==R&&(R.replaceChild(S,C),P||(C._component=null,E(C)))}if(P&&I(P,S!==C),e.base=S,S&&!i){for(var W=e,D=e;D=D._parentComponent;)(W=D).base=S;S._component=W,S._componentConstructor=W.constructor}}!x||n?Q.unshift(e):u||(e.componentDidUpdate&&e.componentDidUpdate(v,b,g),M.afterUpdate&&M.afterUpdate(e));var L,V=e._renderCallbacks;if(V)for(;L=V.pop();)L.call(e);X||i||w()}}function R(e,t,n,r){for(var o=e&&e._component,i=o,a=e,u=o&&e._componentConstructor===t.nodeName,l=u,c=y(t);o&&!l&&(o=o._parentComponent);)l=o.constructor===t.nodeName;return o&&l&&(!r||o._component)?(U(o,c,3,n,r),e=o.base):(i&&!u&&(I(i,!0),e=a=null),o=O(t.nodeName,c,n),e&&!o.nextBase&&(o.nextBase=e,a=null),U(o,c,1,n,r),e=o.base,a&&e!==a&&(a._component=null,E(a))),e}function I(e,t){M.beforeUnmount&&M.beforeUnmount(e);var n=e.base;e._disable=!0,e.componentWillUnmount&&e.componentWillUnmount(),e.base=null;var r=e._component;if(r)I(r,t);else if(n){n[q]&&n[q].ref&&n[q].ref(null),e.nextBase=n,t&&(b(n),A(e));for(var o;o=n.lastChild;)E(o,!t)}e.__ref&&e.__ref(null),e.componentDidUnmount&&e.componentDidUnmount()}function W(e,t){this._dirty=!0,this.context=t,this.props=e,this.state||(this.state={})}function D(e,t,n){return k(n,e,{},!1,t)}var M={},L=[],V=[],z={},B=function(e){return z[e]||(z[e]=e.toLowerCase())},$="undefined"!=typeof Promise&&Promise.resolve(),G=$?function(e){$.then(e)}:setTimeout,H={},q="undefined"!=typeof Symbol?Symbol.for("preactattr"):"__preactattr_",F={boxFlex:1,boxFlexGroup:1,columnCount:1,fillOpacity:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,fontWeight:1,lineClamp:1,lineHeight:1,opacity:1,order:1,orphans:1,strokeOpacity:1,widows:1,zIndex:1,zoom:1},J={blur:1,error:1,focus:1,load:1,resize:1,scroll:1},Z=[],K={},Q=[],X=0,Y=!1,ee=!1,te={};r(W.prototype,{linkState:function(e,t){var n=this._linkedStates||(this._linkedStates={});return n[e+t]||(n[e+t]=s(this,e,t))},setState:function(e,t){var n=this.state;this.prevState||(this.prevState=o(n)),r(n,a(e)?e(n,this.props):e),t&&(this._renderCallbacks=this._renderCallbacks||[]).push(t),f(this)},forceUpdate:function(){j(this,2)},render:function(){}}),e.h=n,e.cloneElement=c,e.Component=W,e.render=D,e.rerender=p,e.options=M})}]); -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Header from './header/Header'; 4 | import Cards from './cards/Cards'; 5 | import './styles.scss'; 6 | 7 | const App = () => ( 8 |
9 |
10 | 11 |
12 | ); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /src/components/App.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import App from './App'; 5 | 6 | test('Snapshot', () => { 7 | const component = renderer.create( 8 | 9 | ); 10 | let tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/__snapshots__/App.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Snapshot 1`] = ` 4 |
5 |
8 |
11 |
12 | Logo brand 16 |
17 |

18 | AgilePoker 19 |

20 |
21 |
22 |
25 |
28 |
31 |
35 | 36 | ? 37 | 38 |
39 |
43 | 44 | 0 45 | 46 |
47 |
51 | 52 | 1/2 53 | 54 |
55 |
59 | 60 | 1 61 | 62 |
63 |
67 | 68 | 2 69 | 70 |
71 |
75 | 76 | 3 77 | 78 |
79 |
83 | 84 | 5 85 | 86 |
87 |
91 | 92 | 8 93 | 94 |
95 |
99 | 100 | 13 101 | 102 |
103 |
107 | 108 | 21 109 | 110 |
111 |
115 | 116 | 34 117 | 118 |
119 |
123 | 124 | 55 125 | 126 |
127 |
128 |
129 |
130 |
131 | `; 132 | -------------------------------------------------------------------------------- /src/components/card-item/CardItem.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/no-static-element-interactions */ 2 | 3 | import React from 'react'; 4 | import cx from 'classnames'; 5 | 6 | import logo from '../../images/svgs/logo.svg'; 7 | import back from '../../images/svgs/back.svg'; 8 | import styles from './styles.scss'; 9 | 10 | const onClick = (value, openedCard, toggleCard) => { 11 | if (openedCard) { 12 | return toggleCard(null); 13 | } 14 | return toggleCard(value); 15 | }; 16 | 17 | const getContainerStyles = (openedCard) => { 18 | if (openedCard) { 19 | return cx(styles.container, styles.opened); 20 | } 21 | return styles.container; 22 | }; 23 | 24 | const renderBrandLogo = (openedCard) => { 25 | if (openedCard) { 26 | return ( 27 |
28 | Brand logo 29 |
30 | ); 31 | } 32 | return null; 33 | }; 34 | 35 | const renderBackButton = (openedCard) => { 36 | if (openedCard) { 37 | return ( 38 |
39 | Back button 40 |
41 | ); 42 | } 43 | return null; 44 | }; 45 | 46 | const CardItem = ({ value, openedCard, toggleCard }) => ( 47 |
onClick(value, openedCard, toggleCard)} 49 | className={getContainerStyles(openedCard)} 50 | > 51 | {value} 52 | {renderBrandLogo(openedCard)} 53 | {renderBackButton(openedCard)} 54 |
55 | ); 56 | 57 | CardItem.defaultProps = { 58 | openedCard: null, 59 | }; 60 | 61 | CardItem.propTypes = { 62 | value: React.PropTypes.string.isRequired, 63 | openedCard: React.PropTypes.string, 64 | toggleCard: React.PropTypes.func.isRequired, 65 | }; 66 | 67 | export default CardItem; 68 | -------------------------------------------------------------------------------- /src/components/card-item/CardItem.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import CardItem from './CardItem'; 5 | 6 | const props = { 7 | value: 'value', 8 | toggleCard: () => {}, 9 | }; 10 | 11 | test('Snapshot', () => { 12 | const component = renderer.create( 13 | 14 | ); 15 | let tree = component.toJSON(); 16 | expect(tree).toMatchSnapshot(); 17 | }); 18 | -------------------------------------------------------------------------------- /src/components/card-item/__snapshots__/CardItem.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Snapshot 1`] = ` 4 |
8 | 9 | value 10 | 11 |
12 | `; 13 | -------------------------------------------------------------------------------- /src/components/card-item/styles.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | display: inline-block; 4 | background-color: #fafafa; 5 | border-radius: 4px; 6 | width: 5em; 7 | height: 8em; 8 | cursor: pointer; 9 | margin: .4em; 10 | border: solid 1px rgba(41, 0, 68, 0.7); 11 | box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.5); 12 | } 13 | 14 | .container:hover { 15 | opacity: 0.75; 16 | } 17 | 18 | .opened { 19 | width: 9.5em; 20 | height: 15em; 21 | box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.2); 22 | } 23 | 24 | .container > span { 25 | position: absolute; 26 | font-size: 2.5em; 27 | color: #2a0044; 28 | top: 50%; 29 | left: 50%; 30 | transform: translate(-50%, -60%); 31 | } 32 | 33 | .backButton { 34 | position: absolute; 35 | bottom: 0; 36 | left: -1.5em; 37 | } 38 | 39 | .brandLogo { 40 | position: absolute; 41 | margin: 0 auto; 42 | left: 0; 43 | right: 0; 44 | top: 11em; 45 | } 46 | 47 | .opened > span { 48 | font-size: 5.5em; 49 | font-weight: 300; 50 | } 51 | -------------------------------------------------------------------------------- /src/components/cards/Cards.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import CardItem from '../card-item/CardItem'; 4 | import styles from './styles.scss'; 5 | 6 | class Cards extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | this.state = { 11 | openedCard: null, 12 | }; 13 | 14 | this.cards = [ 15 | '?', '0', '1/2', '1', '2', '3', '5', '8', '13', '21', '34', '55', 16 | ]; 17 | 18 | this.toggleCard = this.toggleCard.bind(this); 19 | this.renderCards = this.renderCards.bind(this); 20 | } 21 | 22 | toggleCard(card) { 23 | this.setState({ openedCard: card }); 24 | } 25 | 26 | renderCards() { 27 | if (this.state.openedCard) { 28 | return ( 29 |
30 | 35 |
36 |

If there is no struggle,

37 |

there is no progress.

38 |
39 |
40 | ); 41 | } 42 | return this.cards.map(card => 43 | , 49 | ); 50 | } 51 | 52 | render() { 53 | const animationStyles = this.state.openedCard ? 54 | styles.opened : styles.closed; 55 | 56 | return ( 57 |
58 |
59 |
60 | {this.renderCards()} 61 |
62 |
63 |
64 | ); 65 | } 66 | } 67 | 68 | export default Cards; 69 | -------------------------------------------------------------------------------- /src/components/cards/Cards.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import Cards from './Cards'; 5 | 6 | test('Snapshot', () => { 7 | const component = renderer.create( 8 | 9 | ); 10 | let tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/cards/__snapshots__/Cards.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Snapshot 1`] = ` 4 |
7 |
10 |
13 |
17 | 18 | ? 19 | 20 |
21 |
25 | 26 | 0 27 | 28 |
29 |
33 | 34 | 1/2 35 | 36 |
37 |
41 | 42 | 1 43 | 44 |
45 |
49 | 50 | 2 51 | 52 |
53 |
57 | 58 | 3 59 | 60 |
61 |
65 | 66 | 5 67 | 68 |
69 |
73 | 74 | 8 75 | 76 |
77 |
81 | 82 | 13 83 | 84 |
85 |
89 | 90 | 21 91 | 92 |
93 |
97 | 98 | 34 99 | 100 |
101 |
105 | 106 | 55 107 | 108 |
109 |
110 |
111 |
112 | `; 113 | -------------------------------------------------------------------------------- /src/components/cards/styles.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-flow: row wrap; 4 | max-width: 28em; 5 | padding: 3em 0; 6 | margin: 0 auto; 7 | animation: show .7s ease-in-out; 8 | } 9 | 10 | .content { 11 | margin: 2em; 12 | text-align: center; 13 | } 14 | 15 | .opened { 16 | animation: show 1s ease-in-out; 17 | } 18 | 19 | .closed { 20 | animation: hide 1s ease-in-out; 21 | } 22 | 23 | .card { 24 | position: absolute; 25 | top: 50%; 26 | left: 50%; 27 | transform: translate(-50%, -50%); 28 | } 29 | 30 | .quotes { 31 | position: relative; 32 | line-height: 0.5; 33 | width: 165px; 34 | margin: 0 auto; 35 | } 36 | 37 | .quotes > p { 38 | font-size: 14px; 39 | font-weight: bold; 40 | font-style: oblique; 41 | color: #2a0044; 42 | } 43 | 44 | .firstText { 45 | padding-top: 2em; 46 | } 47 | 48 | .firstText::before{ 49 | position: absolute; 50 | left: -0.5em; 51 | top: 0.2em; 52 | content: "\201C"; 53 | font-size: 60px; 54 | font-weight: bold; 55 | color: #999; 56 | } 57 | 58 | .firstText::after{ 59 | content: ""; 60 | } 61 | 62 | @keyframes show { 63 | 0% { opacity: 0; } 64 | 100% { opacity: 1; } 65 | } 66 | 67 | @keyframes hide { 68 | 0% { opacity: 0; } 69 | 100% { opacity: 1; } 70 | } 71 | -------------------------------------------------------------------------------- /src/components/header/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from '../../images/svgs/logo-white.svg'; 4 | import styles from './styles.scss'; 5 | 6 | const Header = () => ( 7 |
8 |
9 |
10 | Logo brand 11 |
12 |

AgilePoker

13 |
14 |
15 | ); 16 | 17 | export default Header; 18 | -------------------------------------------------------------------------------- /src/components/header/Header.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import Header from './Header'; 5 | 6 | test('Snapshot', () => { 7 | const component = renderer.create( 8 |
9 | ); 10 | let tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /src/components/header/__snapshots__/Header.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Snapshot 1`] = ` 4 |
7 |
10 |
11 | Logo brand 15 |
16 |

17 | AgilePoker 18 |

19 |
20 |
21 | `; 22 | -------------------------------------------------------------------------------- /src/components/header/styles.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | position: fixed; 3 | background-color: #2a0044; 4 | height: 3.4em; 5 | box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.5); 6 | top: 0; 7 | left: 0; 8 | right: 0; 9 | width: 100%; 10 | z-index: 1; 11 | } 12 | 13 | .content { 14 | display: flex; 15 | justify-content: flex-start; 16 | align-items: center; 17 | } 18 | 19 | .content > figure { 20 | margin: 0 1em; 21 | float: left; 22 | } 23 | 24 | .content img { 25 | width: 2em; 26 | } 27 | 28 | .content > h1 { 29 | color: #fff; 30 | font-weight: 300; 31 | font-size: 1.4em; 32 | } 33 | -------------------------------------------------------------------------------- /src/components/styles.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f0f0f0; 3 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | } 5 | -------------------------------------------------------------------------------- /src/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /src/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /src/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /src/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /src/images/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-384x384.png -------------------------------------------------------------------------------- /src/images/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-512x512.png -------------------------------------------------------------------------------- /src/images/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-72x72.png -------------------------------------------------------------------------------- /src/images/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusml/pwa-planning-poker/77ecd2c63c62fd53d041d4d8b15a48d6d2bba7bb/src/images/icons/icon-96x96.png -------------------------------------------------------------------------------- /src/images/svgs/back.svg: -------------------------------------------------------------------------------- 1 | 06A51D00-6520-4E53-91C0-7F187B8D8600 -------------------------------------------------------------------------------- /src/images/svgs/logo-white.svg: -------------------------------------------------------------------------------- 1 | C4955F5C-0A65-4E3B-A887-014C64513D7E -------------------------------------------------------------------------------- /src/images/svgs/logo.svg: -------------------------------------------------------------------------------- 1 | C7E40C69-A7B1-4851-9557-22515611433E -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import App from './components/App'; 5 | 6 | ReactDOM.render(, document.getElementById('app')); // eslint-disable-line no-undef 7 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var CACHE_NAME = 'static-v1'; 4 | 5 | self.addEventListener('install', function (event) { 6 | event.waitUntil( 7 | caches.open(CACHE_NAME).then(function (cache) { 8 | return cache.addAll([ 9 | '/', 10 | '/index.html', 11 | '/styles.css', 12 | '/app.js', 13 | '/manifest.js', 14 | '/vendor.js', 15 | ]); 16 | }) 17 | ) 18 | }); 19 | 20 | self.addEventListener('activate', function activator(event) { 21 | event.waitUntil( 22 | caches.keys().then(function (keys) { 23 | return Promise.all(keys 24 | .filter(function (key) { 25 | return key.indexOf(CACHE_NAME) !== 0; 26 | }) 27 | .map(function (key) { 28 | return caches.delete(key); 29 | }) 30 | ); 31 | }) 32 | ); 33 | }); 34 | 35 | self.addEventListener('fetch', function (event) { 36 | event.respondWith( 37 | caches.match(event.request).then(function (cachedResponse) { 38 | return cachedResponse || fetch(event.request); 39 | }) 40 | ); 41 | }); 42 | -------------------------------------------------------------------------------- /webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | import devConfig from './webpack.dev.config.babel.js'; 4 | import prodConfig from './webpack.prod.config.babel.js'; 5 | 6 | let config; 7 | 8 | switch (process.env.npm_lifecycle_event) { 9 | case 'build': 10 | config = prodConfig; 11 | break; 12 | default: 13 | config = devConfig; 14 | break; 15 | } 16 | 17 | module.exports = config; 18 | -------------------------------------------------------------------------------- /webpack.dev.config.babel.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({ template: 'index.html' }); 5 | 6 | module.exports = { 7 | entry: './src/index.js', 8 | output: { 9 | filename: 'bundle.js', 10 | }, 11 | eslint: { 12 | configFile: './.eslintrc', 13 | }, 14 | module: { 15 | preLoaders: [ 16 | { 17 | test: /\.js$/, 18 | exclude: /node_modules/, 19 | loader: 'eslint-loader', 20 | }, 21 | ], 22 | loaders: [ 23 | { 24 | test: /\.js$/, 25 | exclude: /node_modules/, 26 | loader: 'babel', 27 | }, 28 | { 29 | test: /\.scss$/, 30 | loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', 31 | }, 32 | { 33 | test: /.(png|woff(2)?|eot|otf|ttf|svg)(\?[a-z0-9=\.]+)?$/, 34 | loader: 'url-loader?limit=100000', 35 | }, 36 | ], 37 | }, 38 | plugins: [HTMLWebpackPluginConfig], 39 | }; 40 | -------------------------------------------------------------------------------- /webpack.prod.config.babel.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | 3 | const webpack = require('webpack'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 6 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 7 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 8 | 9 | const DefinePlugin = new webpack.DefinePlugin({ 10 | 'process.env': { 11 | NODE_ENV: JSON.stringify('production'), 12 | }, 13 | }); 14 | const CleanPlugin = new CleanWebpackPlugin(['public']); 15 | const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({ template: 'index.html' }); 16 | const UglifyPlugin = new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }); 17 | const DedupePlugin = new webpack.optimize.DedupePlugin(); 18 | const CommonChunksPlugin = new webpack.optimize.CommonsChunkPlugin({ names: ['vendor', 'manifest'] }); 19 | 20 | const CopyWebpackPluginConfig = new CopyWebpackPlugin([ 21 | { from: 'manifest.json' }, 22 | { from: 'favicon.ico' }, 23 | { from: 'sw.js' }, 24 | { from: 'src/images/icons', to: 'images' }, 25 | ]); 26 | 27 | const ExtractText = new ExtractTextPlugin('styles.css'); 28 | 29 | module.exports = { 30 | entry: { 31 | vendor: ['react', 'react-dom'], 32 | app: './src/index.js', 33 | }, 34 | output: { 35 | path: 'public', 36 | filename: '[name].js', 37 | }, 38 | resolve: { 39 | alias: { 40 | react: 'preact-compat', 41 | 'react-dom': 'preact-compat', 42 | }, 43 | }, 44 | module: { 45 | loaders: [ 46 | { 47 | test: /\.js$/, 48 | exclude: /node_modules/, 49 | loader: 'babel', 50 | }, 51 | { 52 | test: /\.scss$/, 53 | loader: ExtractText.extract( 54 | 'style', 55 | 'css?modules&importLoaders=1&localIdentName=[hash:base64:5]', 56 | ), 57 | }, 58 | { 59 | test: /.(png|woff(2)?|eot|otf|ttf|svg)(\?[a-z0-9=\.]+)?$/, 60 | loader: 'url-loader?limit=100000', 61 | }, 62 | ], 63 | }, 64 | plugins: [CleanPlugin, DefinePlugin, HTMLWebpackPluginConfig, UglifyPlugin, DedupePlugin, 65 | ExtractText, CopyWebpackPluginConfig, CommonChunksPlugin], 66 | }; 67 | --------------------------------------------------------------------------------