diff --git a/config/webpack/base/webpack.base.client.config.js b/config/webpack/base/webpack.base.client.config.js
index 7f90dff..d72032c 100644
--- a/config/webpack/base/webpack.base.client.config.js
+++ b/config/webpack/base/webpack.base.client.config.js
@@ -36,13 +36,14 @@ module.exports = {
plugins: [
new WebpackPwaManifest({
filename: 'manifest.json',
- name: 'MMT Management Manager',
+ name: 'MMTPWA',
short_name: 'MMTPWA',
description: 'Manage your MMT Management in this PWA!',
background_color: '#ffffff',
+ theme_color: '#ec2821',
orientation: 'portrait',
display: 'standalone',
- publicPath: '/',
+ start_url: '/',
icons: [
{
src: resolve('src/assets/mmt.png'),
diff --git a/package-lock.json b/package-lock.json
index f35c1ad..ff8e0c8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -72,9 +72,10 @@
"dev": true
},
"@types/node": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.0.2.tgz",
- "integrity": "sha512-DPbG0qQ5kdvXBK0jGdv1yd8vGN7hwH8sB2Q1z1kGaxtCnXkSxYJ009VccGlcgknYoLeMTYu4TTzOditDJMdP2Q==",
+
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.0.3.tgz",
+ "integrity": "sha512-J7nx6JzxmtT4zyvfLipYL7jNaxvlCWpyG7JhhCQ4fQyG+AGfovAHoYR55TFx+X8akfkUJYpt5JG6GPeFMjZaCQ==",
"dev": true
},
"abab": {
@@ -4698,7 +4699,7 @@
"is-string": "1.0.4",
"is-subset": "0.1.1",
"lodash": "4.17.10",
- "object-inspect": "1.5.0",
+ "object-inspect": "1.6.0",
"object-is": "1.0.1",
"object.assign": "4.1.0",
"object.entries": "1.0.4",
@@ -11091,7 +11092,7 @@
"dev": true,
"requires": {
"unist-util-modify-children": "1.1.2",
- "unist-util-visit": "1.3.0"
+ "unist-util-visit": "1.3.1"
}
},
"mdn-data": {
@@ -12532,9 +12533,9 @@
}
},
"object-inspect": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.5.0.tgz",
- "integrity": "sha512-UmOFbHbwvv+XHj7BerrhVq+knjceBdkvU5AriwLMvhv2qi+e7DJzxfBeFpILEjVzCp+xA+W/pIf06RGPWlZNfw==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
+ "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==",
"dev": true
},
"object-is": {
@@ -13065,7 +13066,7 @@
"integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
"dev": true,
"requires": {
- "@types/node": "10.0.2"
+ "@types/node": "10.0.3"
}
},
"parseurl": {
@@ -14038,7 +14039,7 @@
"requires": {
"htmlparser2": "3.9.2",
"remark": "8.0.0",
- "unist-util-find-all-after": "1.0.1"
+ "unist-util-find-all-after": "1.0.2"
}
},
"postcss-less": {
@@ -15919,7 +15920,7 @@
"requires": {
"remark-parse": "4.0.0",
"remark-stringify": "4.0.0",
- "unified": "6.1.6"
+ "unified": "6.2.0"
}
},
"remark-parse": {
@@ -19160,9 +19161,9 @@
}
},
"unified": {
- "version": "6.1.6",
- "resolved": "https://registry.npmjs.org/unified/-/unified-6.1.6.tgz",
- "integrity": "sha512-pW2f82bCIo2ifuIGYcV12fL96kMMYgw7JKVEgh7ODlrM9rj6vXSY3BV+H6lCcv1ksxynFf582hwWLnA1qRFy4w==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz",
+ "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==",
"dev": true,
"requires": {
"bail": "1.0.3",
@@ -19268,9 +19269,9 @@
}
},
"unist-util-find-all-after": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.1.tgz",
- "integrity": "sha1-TlUSq/734GFnga7Pex7XUcAK+Qg=",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.2.tgz",
+ "integrity": "sha512-nDl79mKpffXojLpCimVXnxhlH/jjaTnDuScznU9J4jjsaUtBdDbxmlc109XtcqxY4SDO0SwzngsxxW8DIISt1w==",
"dev": true,
"requires": {
"unist-util-is": "2.1.2"
@@ -19297,7 +19298,7 @@
"integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==",
"dev": true,
"requires": {
- "unist-util-visit": "1.3.0"
+ "unist-util-visit": "1.3.1"
}
},
"unist-util-stringify-position": {
@@ -19307,9 +19308,9 @@
"dev": true
},
"unist-util-visit": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.0.tgz",
- "integrity": "sha512-9ntYcxPFtl44gnwXrQKZ5bMqXMY0ZHzUpqMFiU4zcc8mmf/jzYm8GhYgezuUlX4cJIM1zIDYaO6fG/fI+L6iiQ==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.1.tgz",
+ "integrity": "sha512-0fdB9EQJU0tho5tK0VzOJzAQpPv2LyLZ030b10GxuzAWEfvd54mpY7BMjQ1L69k2YNvL+SvxRzH0yUIehOO8aA==",
"dev": true,
"requires": {
"unist-util-is": "2.1.2"
diff --git a/src/assets/mmt1024.png b/src/assets/mmt1024.png
new file mode 100644
index 0000000..d8376a5
Binary files /dev/null and b/src/assets/mmt1024.png differ
diff --git a/src/components/Button/Button.js b/src/components/Button/Button.js
new file mode 100644
index 0000000..1a6f09b
--- /dev/null
+++ b/src/components/Button/Button.js
@@ -0,0 +1,12 @@
+import React from 'react'
+import './Button.scss'
+
+const Button = ({ isActive = false, buttonText = 'Submit', buttonAction = null}) => {
+ return (
+
+ )
+}
+
+export default Button
\ No newline at end of file
diff --git a/src/components/Button/Button.scss b/src/components/Button/Button.scss
new file mode 100644
index 0000000..f5e1d36
--- /dev/null
+++ b/src/components/Button/Button.scss
@@ -0,0 +1,51 @@
+$activated-colour: #ec2821;
+$deactive-colour: grey;
+
+.button {
+ width: 100%;
+ display: block;
+ border: 2px solid $deactive-colour;
+ background-color: $deactive-colour;
+ padding: 12px;
+ text-transform: uppercase;
+ margin: 12px 0;
+ color: white;
+ transition: .3s;
+}
+
+.is-active {
+ width: 100%;
+ display: block;
+ border: 2px solid $activated-colour;
+ background-color: transparent;
+ padding: 12px;
+ text-transform: uppercase;
+ margin: 12px 0;
+ position: relative;
+ color: $activated-colour;
+ transition: .3s;
+ transform: translateZ(0);
+ overflow: hidden;
+ cursor: pointer;
+
+ &:after {
+ position: absolute;
+ content: '';
+ height: 100%;
+ width: 0;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ transition: .3s;
+ background-color: $activated-colour;
+ z-index: -1;
+ }
+
+ &:hover {
+ color: white;
+
+ &:after {
+ width: 100%;
+ }
+ }
+}
diff --git a/src/components/PushSubscribeExample/PushSubscribeExample.js b/src/components/PushSubscribeExample/PushSubscribeExample.js
new file mode 100644
index 0000000..068a74a
--- /dev/null
+++ b/src/components/PushSubscribeExample/PushSubscribeExample.js
@@ -0,0 +1,41 @@
+import React, { Component } from 'react'
+import { subscribeUser, swRegistration } from '../../services/serviceWorker/serviceWorker'
+import Button from '../Button/Button';
+
+class PushSubscribeExample extends Component {
+ state = {
+ userSubscribed : false
+ }
+
+ componentDidMount () {
+ navigator.serviceWorker.ready.then((swRegistration) => {
+ swRegistration.pushManager.getSubscription()
+ .then((subscription) => {
+ if(subscription) {
+ this.setState({
+ userSubscribed : true
+ })
+ }
+ })
+ })
+ }
+
+ buttonClicked = () => {
+ if(!this.state.userSubscribed) {
+ subscribeUser()
+ this.setState({
+ userSubscribed : true
+ })
+ }
+ }
+
+ render () {
+ return (
+
+
+
+ )
+ }
+}
+
+export default PushSubscribeExample
\ No newline at end of file
diff --git a/src/components/pushNotificationExample/PushNotificationExample.js b/src/components/pushNotificationExample/PushNotificationExample.js
new file mode 100644
index 0000000..be24bfe
--- /dev/null
+++ b/src/components/pushNotificationExample/PushNotificationExample.js
@@ -0,0 +1,60 @@
+import React, { Component } from 'react'
+import { swRegistration } from '../../services/serviceWorker/serviceWorker'
+import Button from '../Button/Button'
+import './PushNotificationExample.scss'
+
+class PushNotificationExample extends Component {
+ state = {
+ title: '',
+ message: ''
+ }
+
+ handleChange = ({ target }) => {
+ const { value, name } = target
+ this.setState({ [name] : value })
+ }
+
+ resetState () {
+ this.setState({
+ title: '',
+ message: ''
+ })
+ }
+
+ handleSubmit = (event) => {
+ event.preventDefault()
+ const title = this.state.title
+ const options = {
+ body: this.state.message,
+ icon: '../../assets/mmt.png',
+ badge: '../../assets/mmt.png'
+ }
+ swRegistration.showNotification(title, options)
+ this.resetState()
+ }
+
+ render () {
+ return (
+
+ )
+ }
+}
+
+export default PushNotificationExample
\ No newline at end of file
diff --git a/src/components/pushNotificationExample/PushNotificationExample.scss b/src/components/pushNotificationExample/PushNotificationExample.scss
new file mode 100644
index 0000000..7c73dfa
--- /dev/null
+++ b/src/components/pushNotificationExample/PushNotificationExample.scss
@@ -0,0 +1,70 @@
+$primary-colour: #ec2821;
+
+.form {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.default {
+ padding: 5px;
+ box-sizing: border-box;
+ width: 100%;
+ border: 1px solid lightgrey;
+ margin-bottom: 12px;
+}
+
+.title {
+ composes: default;
+ padding: 8px 5px;
+ box-sizing: border-box;
+ width: 100%;
+ border: 1px solid lightgrey;
+ margin-bottom: 12px;
+}
+
+.message {
+ composes: default;
+ padding: 8px 5px;
+ box-sizing: border-box;
+ width: 100%;
+ border: 1px solid lightgrey;
+ margin-bottom: 12px;
+ height: 100px;
+}
+
+.button {
+ width: 100%;
+ display: block;
+ border: 2px solid $primary-colour;
+ background-color: transparent;
+ padding: 8px;
+ text-transform: uppercase;
+ position: relative;
+ color: $primary-colour;
+ transition: .3s;
+ transform: translateZ(0);
+ overflow: hidden;
+ cursor: pointer;
+
+ &:after {
+ position: absolute;
+ content: '';
+ height: 100%;
+ width: 0;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ transition: .3s;
+ background-color: $primary-colour;
+ z-index: -1;
+ }
+
+ &:hover {
+ color: white;
+
+ &:after {
+ width: 100%;
+ }
+ }
+}
diff --git a/src/index.js b/src/index.js
index e50e4aa..0c810c7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -5,19 +5,11 @@ import BrowserRouter from 'react-router-dom/BrowserRouter'
import { renderRoutes } from 'react-router-config'
import routes from './routes'
import './styles/styles.scss'
+import { registerWorker, swRegistration } from './services/serviceWorker/serviceWorker'
const rootEl = document.getElementById('root')
-if ('serviceWorker' in navigator) {
- // const registration = runtime.register().then((data) => {
- // console.info('It registered!! ', data)
- // })
- navigator.serviceWorker.register('/sw.js').then(function (registration) {
- console.log('Service worker registration succeeded:', registration)
- }).catch(function (error) {
- console.log('Service worker registration failed:', error)
- })
-}
+registerWorker();
ReactDOM.hydrate((
diff --git a/src/pages/HomePage/HomePage.js b/src/pages/HomePage/HomePage.js
index 25c03e1..528b8af 100644
--- a/src/pages/HomePage/HomePage.js
+++ b/src/pages/HomePage/HomePage.js
@@ -5,7 +5,9 @@ class HomePage extends Component {
render () {
return (
+
+
)
}
diff --git a/src/pages/PushNotificationPage/PushNotificationPage.js b/src/pages/PushNotificationPage/PushNotificationPage.js
new file mode 100644
index 0000000..9fc73cc
--- /dev/null
+++ b/src/pages/PushNotificationPage/PushNotificationPage.js
@@ -0,0 +1,18 @@
+import React, { Component } from 'react'
+import PushNotificationExample from '../../components/pushNotificationExample/PushNotificationExample'
+import PushSubscribeExample from '../../components/PushSubscribeExample/PushSubscribeExample';
+import './PushNotificationPage.scss'
+
+class PushNotificationPage extends Component {
+ render () {
+ return (
+
+
Push Notification Page
+
+
+
+ )
+ }
+}
+
+export default PushNotificationPage
diff --git a/src/pages/PushNotificationPage/PushNotificationPage.scss b/src/pages/PushNotificationPage/PushNotificationPage.scss
new file mode 100644
index 0000000..5b981b4
--- /dev/null
+++ b/src/pages/PushNotificationPage/PushNotificationPage.scss
@@ -0,0 +1,5 @@
+.title {
+ text-align: center;
+ font-size: 1.2rem;
+ font-family: 'Arial', sans-serif;
+}
diff --git a/src/pages/PushNotificationPage/index.js b/src/pages/PushNotificationPage/index.js
new file mode 100644
index 0000000..7eac783
--- /dev/null
+++ b/src/pages/PushNotificationPage/index.js
@@ -0,0 +1 @@
+export { default } from './PushNotificationPage'
diff --git a/src/routes.js b/src/routes.js
index 5ae2e0c..61b5974 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -1,5 +1,6 @@
import HomePage from './pages/HomePage'
import NotFoundPage from './pages/NotFoundPage'
+import PushNotificationPage from './pages/PushNotificationPage/index';
export default [
{
@@ -7,8 +8,11 @@ export default [
exact: true,
component: HomePage
},
+ { path: '/push',
+ component: PushNotificationPage
+ },
{
path: '**',
- component: NotFoundPage
+ component: HomePage
}
]
diff --git a/src/services/authService/authService.js b/src/services/authService/authService.js
deleted file mode 100644
index 4df4027..0000000
--- a/src/services/authService/authService.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const login = () => {
-
-}
-
-export default {
- login
-}
diff --git a/src/services/authService/index.js b/src/services/authService/index.js
deleted file mode 100644
index 34d7796..0000000
--- a/src/services/authService/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './authService'
diff --git a/src/services/basketService/basketService.js b/src/services/basketService/basketService.js
deleted file mode 100644
index ea4799d..0000000
--- a/src/services/basketService/basketService.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import Request from '@vfuk/dalmatian/request'
-
-const get = basketId => {
- return new Promise((resolve, reject) => {
- new Request(`/api/digital/v1/basket/basket/${basketId}`)
- .get()
- .then(resolve)
- .catch(reject)
- })
-}
-
-const create = () => {
-
-}
-
-const update = () => {
-
-}
-
-const empty = basketId => {
- return new Promise((resolve, reject) => {
- new Request(`/api/digital/v1/basket/basket/${basketId}/empty`)
- .post()
- .then(() => get(basketId))
- .then(resolve)
- .catch(reject)
- })
-}
-
-export default {
- get,
- create,
- update,
- empty
-}
diff --git a/src/services/basketService/index.js b/src/services/basketService/index.js
deleted file mode 100644
index 551f503..0000000
--- a/src/services/basketService/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './basketService'
diff --git a/src/services/serviceWorker/serviceWorker.js b/src/services/serviceWorker/serviceWorker.js
new file mode 100644
index 0000000..70be87a
--- /dev/null
+++ b/src/services/serviceWorker/serviceWorker.js
@@ -0,0 +1,58 @@
+const applicationServerPublicKey = 'BBQbFed_Nv1Jv1ZsWNbeqqQT2ttt_HFgpBZ5wi4hCfJZMVx2fLa0el-LI_NP_UdugMUDLOfzTOqJU_nxXj0-U1g'
+let isSubscribed = false
+let swRegistration = null
+
+const registerWorker = () => {
+ if ('serviceWorker' in navigator && 'PushManager' in window) {
+ console.log('Service Worker and Push is supported')
+
+ navigator.serviceWorker.register('sw.js')
+ .then((swReg) => {
+ console.log('Service Worker is registered', swReg)
+ swRegistration = swReg
+ // Commented out and moved to Component for time being as sporadic on mobile and required multiple refreshes of page
+ // subscribeUser()
+ })
+ .catch((error) => {
+ console.error('Service Worker Error', error)
+ })
+ } else {
+ console.warn('Push messaging is not supported')
+ }
+}
+
+const urlB64ToUint8Array = (base64String) => {
+ const padding = '='.repeat((4 - base64String.length % 4) % 4)
+ const base64 = (base64String + padding)
+ .replace(/-/g, '+')
+ .replace(/_/g, '/')
+
+ const rawData = window.atob(base64)
+ const outputArray = new Uint8Array(rawData.length)
+
+ for (let i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i)
+ }
+ return outputArray
+}
+
+const subscribeUser = () => {
+ console.log('running subscribe user')
+ const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey)
+ swRegistration.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: applicationServerKey
+ })
+ .then((subscription) => {
+ console.log('User is subscribed.')
+ updateSubscriptionOnServer(subscription)
+ })
+ .catch((err) => {
+ console.log('Failed to subscribe the user: ', err)
+ })
+}
+
+const updateSubscriptionOnServer = (subscription) => {
+ console.log(JSON.stringify(subscription))
+}
+export { registerWorker, swRegistration, subscribeUser, isSubscribed }
diff --git a/src/sw.js b/src/sw.js
index ffb6928..6af2bc2 100644
--- a/src/sw.js
+++ b/src/sw.js
@@ -1,56 +1,74 @@
const template = `
-const CACHE_NAME = 'v1'
+const enableCache = false;
+const CACHE_NAME = 'MMT-v1'
console.info('In offline')
self.addEventListener('install', event => {
console.info('Installed')
-
- event.waitUntil(
- self.caches.open(CACHE_NAME)
- .then(cache => (
- fetch('/assets/manifest.json')
- .then(response => response.json())
- .then(assets =>
- cache.addAll([
- '/',
- '/assets/bundle.js',
- '/assets/styles.css'
- ])
+ if (enableCache) {
+ event.waitUntil(
+ self.caches.open(CACHE_NAME)
+ .then(cache => (
+ fetch('/assets/manifest.json')
+ .then(response => response.json())
+ .then(assets =>
+ cache.addAll([
+ '/',
+ '/assets/bundle.js',
+ '/assets/styles.css'
+ ])
+ )
)
)
- )
- .then(() => self.skipWaiting())
- .catch(console.warn)
- )
+ .then(() => self.skipWaiting())
+ .catch(console.warn)
+ )
+ }
})
self.addEventListener('fetch', (event) => {
console.log('Fetch')
+ if (enableCache) {
+ event.respondWith(
+ self.caches
+ .match(event.request)
+ .then((response) => (response || fetch(event.request)))
+ .catch(e => {
+ console.error('Error on the cache', e)
+ })
+ )
+ }
- event.respondWith(
- self.caches
- .match(event.request)
- .then((response) => (response || fetch(event.request)))
- .catch(e => {
- console.error('Error on the cache', e)
- })
- )
})
-self.addEventListener('activate', event => {
- console.log('Actrivate')
- const cacheWhitelist = [CACHE_NAME]
+self.addEventListener("activate", event => {
+ const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
- self.caches.keys()
+ caches.keys()
.then(keyList =>
Promise.all(keyList.map(key => {
- if (!cacheWhitelist.includes(key)) {
- return self.caches.delete(key)
- }
+ if (!cacheWhitelist.includes(key)) {
+ console.log('Deleting cache: ' + key)
+ return caches.delete(key);
+ }
}))
)
- .then(() => self.clients.claim())
- )
+ );
+});
+
+self.addEventListener('push', function(event) {
+ console.log('[Service Worker] Push Received.')
+ console.log('[Service Worker] Push had this data: ')
+
+ const title = 'Push MMT PWA'
+ const options = {
+ body: 'Message: Hello World',
+ icon: 'images/icon.png',
+ badge: 'images/badge.png'
+ };
+
+ const notificationPromise = self.registration.showNotification(title, options)
+ event.waitUntil(notificationPromise)
})
`