From f8c80328b5f8cbb261e47c854f70806e4836e120 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Fri, 19 Oct 2018 10:17:07 -0700 Subject: [PATCH 01/20] Update readme - Add troubleshooting section for `connection not secure` --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 25729cbc56..0f3224489d 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,10 @@ Make sure you have created a `.env` file in `packages/venia-concept` which speci Venia and its GraphQL queries may be out of sync with the schema of your connected Magento instance. Make sure the Magento instance is up to date with the 2.3 development branch, and your copy of this repository (or your dependency on it) is up to date. +### My browser complains that the connection is not secure + +Generating certificates is handled by [devcert](https://github.com/davewasmer/devcert). If you're on a Linux machine make sure that `libnss3-tools` (or whatever the equivalent is) is installed on your system. Further information provided in [this section of the devcert readme](https://github.com/davewasmer/devcert#skipcertutil). + **To test whether your queries are up to date, run `npm run validate:venia:gql` at project root.** ## Things not to do From 1475cd1e4fe8697daebb899245b3780c5c874cd2 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Tue, 23 Oct 2018 09:01:30 -0700 Subject: [PATCH 02/20] Add web app manifest, offline mode - Add Apollo simple persistence for persistent cache of graphql queries - Add html files to SW runtime cache - Fix venia favicon (fixes serving images via upward in general) - Add new venia icons --- package-lock.json | 70 ++++++++++-------- .../plugins/ServiceWorkerPlugin.js | 5 ++ packages/upward-js/lib/middleware.js | 3 +- packages/venia-concept/manifest.webmanifest | 13 ++++ packages/venia-concept/media/favicon.ico | Bin 15086 -> 3361 bytes .../media/icons/venia_circle_144.png | Bin 0 -> 11873 bytes .../media/icons/venia_square_144.png | Bin 0 -> 7052 bytes packages/venia-concept/src/index.js | 10 ++- .../templates/doctype-and-head-start.mst | 1 + packages/venia-concept/venia-upward.yml | 30 ++++++++ packages/venia-concept/webpack.config.js | 3 +- 11 files changed, 101 insertions(+), 34 deletions(-) create mode 100644 packages/venia-concept/manifest.webmanifest create mode 100644 packages/venia-concept/media/icons/venia_circle_144.png create mode 100644 packages/venia-concept/media/icons/venia_square_144.png diff --git a/package-lock.json b/package-lock.json index 84614bca20..6841199ceb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1739,7 +1739,7 @@ }, "style-loader": { "version": "0.20.3", - "resolved": "http://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==", "dev": true, "requires": { @@ -1911,7 +1911,7 @@ }, "@types/accepts": { "version": "1.3.5", - "resolved": "http://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", "dev": true, "requires": { @@ -1920,7 +1920,7 @@ }, "@types/async": { "version": "2.0.49", - "resolved": "http://registry.npmjs.org/@types/async/-/async-2.0.49.tgz", + "resolved": "https://registry.npmjs.org/@types/async/-/async-2.0.49.tgz", "integrity": "sha512-Benr3i5odUkvpFkOpzGqrltGdbSs+EVCkEBGXbuR7uT0VzhXKIkhem6PDzHdx5EonA+rfbB3QvP6aDOw5+zp5Q==", "dev": true, "optional": true @@ -1967,7 +1967,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", "dev": true }, @@ -2383,6 +2383,12 @@ "optimism": "^0.6.6" } }, + "apollo-cache-persist": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/apollo-cache-persist/-/apollo-cache-persist-0.1.1.tgz", + "integrity": "sha512-/7GAyblPR169ryW3ugbtHqiU0UGkhIt10NeaO2gn2ClxjLHF/nIkJD5mx/0OCF2vLNbbnzLZVDeIO1pf72TrEA==", + "dev": true + }, "apollo-client": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.4.2.tgz", @@ -7795,7 +7801,7 @@ }, "css-color-names": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", "dev": true }, @@ -7811,7 +7817,7 @@ }, "css-loader": { "version": "0.28.11", - "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", "dev": true, "requires": { @@ -10337,7 +10343,7 @@ }, "file-loader": { "version": "1.1.11", - "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { @@ -10779,12 +10785,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10799,17 +10807,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -10926,7 +10937,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -10938,6 +10950,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -10952,6 +10965,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10959,12 +10973,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -10983,6 +10999,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -11063,7 +11080,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -11075,6 +11093,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -11196,6 +11215,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -12189,7 +12209,7 @@ }, "gulp": { "version": "3.9.1", - "resolved": "http://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "dev": true, "requires": { @@ -12235,7 +12255,7 @@ }, "semver": { "version": "4.3.6", - "resolved": "http://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", "dev": true }, @@ -17483,7 +17503,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -18297,12 +18317,6 @@ "wrappy": "1" } }, - "one-time": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", - "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", - "dev": true - }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", @@ -19131,12 +19145,6 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, - "post-compile-webpack-plugin": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/post-compile-webpack-plugin/-/post-compile-webpack-plugin-0.1.2.tgz", - "integrity": "sha1-D254Ck3/Ll9Z8gf5A6jV8EdshC4=", - "dev": true - }, "postcss": { "version": "6.0.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", @@ -21551,7 +21559,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -27141,7 +27149,7 @@ }, "xmlbuilder": { "version": "4.2.1", - "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "dev": true, "requires": { diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js index bd3f483a92..f4f0cc9b66 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js @@ -30,6 +30,11 @@ class ServiceWorkerPlugin { if (this.config.runtimeCacheAssetPath) { config.runtimeCaching = [ + // TODO: Make this an argument like this.config.runtimeCacheAssetPath? + { + urlPattern: '/', + handler: 'staleWhileRevalidate' + }, { urlPattern: new RegExp(this.config.runtimeCacheAssetPath), handler: 'staleWhileRevalidate' diff --git a/packages/upward-js/lib/middleware.js b/packages/upward-js/lib/middleware.js index 9593c66534..19c46e573d 100644 --- a/packages/upward-js/lib/middleware.js +++ b/packages/upward-js/lib/middleware.js @@ -78,7 +78,8 @@ class UpwardMiddleware { debug('status, headers, and body valid. responding'); res.status(response.status) .set(response.headers) - .send(response.body); + // TODO: Issue #408 + .send(new Buffer(response.body, 'binary')); } }; } diff --git a/packages/venia-concept/manifest.webmanifest b/packages/venia-concept/manifest.webmanifest new file mode 100644 index 0000000000..34b2c5c2ab --- /dev/null +++ b/packages/venia-concept/manifest.webmanifest @@ -0,0 +1,13 @@ +{ + "name": "Venia", + "short_name": "Venia", + "start_url": ".", + "display": "standalone", + "background_color": "#fff", + "description": "Shopping", + "icons": [{ + "src": "/venia_circle_144.png", + "sizes": "144x144", + "type": "image/png" + }] +} diff --git a/packages/venia-concept/media/favicon.ico b/packages/venia-concept/media/favicon.ico index fd57e1d1a9e519dd9688ea82c31fe0d4bde78b91..5b4f3f947f9639b1c50c12a6af5b84652bd034b6 100644 GIT binary patch literal 3361 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 A0RR91 diff --git a/packages/venia-concept/media/icons/venia_circle_144.png b/packages/venia-concept/media/icons/venia_circle_144.png new file mode 100644 index 0000000000000000000000000000000000000000..6db6f53ba8f3779271b8399f69b6d9efa3f997cb GIT binary patch literal 11873 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 { }; }); +const cache = new InMemoryCache(); + +persistCache({ + cache, + storage: window.localStorage +}); + const apolloClient = new ApolloClient({ link: authLink.concat(httpLink), - cache: new InMemoryCache() + cache: cache }); ReactDOM.render( diff --git a/packages/venia-concept/templates/doctype-and-head-start.mst b/packages/venia-concept/templates/doctype-and-head-start.mst index efa899076b..01d44b5bb9 100644 --- a/packages/venia-concept/templates/doctype-and-head-start.mst +++ b/packages/venia-concept/templates/doctype-and-head-start.mst @@ -4,3 +4,4 @@ + diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml index 124305130d..3519b752ac 100644 --- a/packages/venia-concept/venia-upward.yml +++ b/packages/venia-concept/venia-upward.yml @@ -7,9 +7,15 @@ response: - matches: request.url.pathname pattern: '^/(graphql|rest|media/)' use: proxy + - matches: request.url.pathname + pattern: '^/manifest.webmanifest' + use: manifest - matches: request.url.pathname pattern: '^/favicon.ico' use: favicon + - matches: request.url.pathname + pattern: '^/homescreen144.png' + use: homescreen - matches: urlKey pattern: '.' use: appShell @@ -119,6 +125,30 @@ favicon: inline: ./media/favicon.ico encoding: binary +homescreen: + inline: + status: 200 + headers: + inline: + content-type: + inline: 'image/png' + body: + file: + inline: ./media/icons/homescreen144.png + encoding: binary + + +manifest: + inline: + status: 200 + headers: + inline: + content-type: + inline: application/json + body: + file: + inline: ./manifest.webmanifest + static: directory: inline: './dist' diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 93f81e464b..79b556fbc1 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -125,7 +125,8 @@ module.exports = async function(env) { env, enableServiceWorkerDebugging, serviceWorkerFileName, - paths: themePaths + paths: themePaths, + runtimeCacheAssetPath: '\\.html$' }) ] }; From c6380a9b5438ee722d1cc2f8cb032c44ccdfc53f Mon Sep 17 00:00:00 2001 From: "pzimmerman@bargreen.com" Date: Thu, 25 Oct 2018 08:54:58 -0700 Subject: [PATCH 03/20] Fix venia config --- package-lock.json | 28 +++++++------------------ package.json | 7 ++++--- packages/venia-concept/venia-upward.yml | 4 ++-- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6841199ceb..0e0a8d8189 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10785,14 +10785,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10807,20 +10805,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -10937,8 +10932,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -10950,7 +10944,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -10965,7 +10958,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10973,14 +10965,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -10999,7 +10989,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -11080,8 +11069,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -11093,7 +11081,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -11215,7 +11202,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/package.json b/package.json index 14eed0daf3..f966393ec2 100644 --- a/package.json +++ b/package.json @@ -33,19 +33,20 @@ "watch:venia": "cd packages/venia-concept && npm run -s watch; cd - >/dev/null" }, "devDependencies": { - "@magento/eslint-config": "^1.2.3", "@magento/directive-parser": "^0.1.1", + "@magento/eslint-config": "^1.2.3", "@storybook/addon-actions": "^3.4.2", "@storybook/addons": "^3.4.6", "@storybook/react": "^3.4.2", "apollo-boost": "^0.1.15", + "apollo-cache-persist": "^0.1.1", "apollo-link-context": "^1.0.9", "apollo-server": "^2.0.5", "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-eslint": "^8.2.3", - "babel-loader": "^7.1.2", "babel-helper-module-imports": "^7.0.0-beta.3", + "babel-loader": "^7.1.2", "babel-plugin-graphql-tag": "^1.6.0", "babel-plugin-import-graphql": "^2.6.2", "babel-plugin-syntax-dynamic-import": "^6.18.0", @@ -98,8 +99,8 @@ "jest-fetch-mock": "^1.4.1", "jest-junit": "^5.1.0", "jest-junit-reporter": "^1.1.0", - "js-yaml": "^3.12.0", "jquery": "^3.3.1", + "js-yaml": "^3.12.0", "keypress": "^0.2.1", "lerna": "^3.4.0", "lodash.debounce": "^4.0.8", diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml index 3519b752ac..ce531d9553 100644 --- a/packages/venia-concept/venia-upward.yml +++ b/packages/venia-concept/venia-upward.yml @@ -14,7 +14,7 @@ response: pattern: '^/favicon.ico' use: favicon - matches: request.url.pathname - pattern: '^/homescreen144.png' + pattern: '^/venia_circle_144.png' use: homescreen - matches: urlKey pattern: '.' @@ -134,7 +134,7 @@ homescreen: inline: 'image/png' body: file: - inline: ./media/icons/homescreen144.png + inline: ./media/icons/venia_circle_144.png encoding: binary From 6d89232e38642f12cf7233afbe6ff3d01e6fad83 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Tue, 30 Oct 2018 14:45:17 -0700 Subject: [PATCH 04/20] Offline mode integrated into resolveUnknownRoute - If offline loads component from localStorage - If offline and component not in cache, load NotFound root component - Add fallback page to venia-upward yml - Switch to inject-manifest in webpack --- .../src/Router/resolveUnknownRoute.js | 41 ++++++++- .../src/RootComponents/NotFound/index.js | 6 ++ .../src/RootComponents/NotFound/notFound.css | 10 +++ .../src/RootComponents/NotFound/notFound.js | 28 ++++++ packages/venia-concept/src/sw.js | 88 +++++++++++++++++++ packages/venia-concept/templates/fallback.mst | 9 ++ packages/venia-concept/venia-upward.yml | 17 ++++ packages/venia-concept/webpack.config.js | 20 +++-- 8 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 packages/venia-concept/src/RootComponents/NotFound/index.js create mode 100644 packages/venia-concept/src/RootComponents/NotFound/notFound.css create mode 100644 packages/venia-concept/src/RootComponents/NotFound/notFound.js create mode 100644 packages/venia-concept/src/sw.js create mode 100644 packages/venia-concept/templates/fallback.mst diff --git a/packages/peregrine/src/Router/resolveUnknownRoute.js b/packages/peregrine/src/Router/resolveUnknownRoute.js index 65c0230d30..5d959bfe7e 100644 --- a/packages/peregrine/src/Router/resolveUnknownRoute.js +++ b/packages/peregrine/src/Router/resolveUnknownRoute.js @@ -52,11 +52,40 @@ export default function resolveUnknownRoute(opts) { } /** - * @description Calls the GraphQL API for results from the urlResolver query + * @description Checks if route is stored in localStorage, if not call `fetchRoute` * @param {{ route: string, apiBase: string}} opts * @returns {Promise<{type: "PRODUCT" | "CATEGORY" | "CMS_PAGE"}>} */ function remotelyResolveRoute(opts) { + let urlResolve = localStorage.getItem('urlResolve') + urlResolve = JSON.parse(urlResolve); + + // If it exists in localStorage, use that value + // TODO: Use simplePersistence or figure out how to manually add to workbox + if (( urlResolve && urlResolve[opts.route] ) || !navigator.onLine) { + if (urlResolve && urlResolve[opts.route]) { + return new Promise(function(resolve) { + resolve(urlResolve[opts.route].data.urlResolver); + }); + } else { + return new Promise(function(resolve) { + resolve({ + type: "NOTFOUND", + id: -1 + }) + }); + } + } else { + return fetchRoute(opts); + } +} + +/** + * @description Calls the GraphQL API for results from the urlResolver query + * @param {{ route: string, apiBase: string}} opts + * @returns {Promise<{type: "PRODUCT" | "CATEGORY" | "CMS_PAGE"}>} + */ +function fetchRoute(opts) { const url = new URL('/graphql', opts.apiBase); return fetch(url, { method: 'POST', @@ -76,7 +105,15 @@ function remotelyResolveRoute(opts) { }) }) .then(res => res.json()) - .then(res => res.data.urlResolver); + .then(res => { + let urlResolve = localStorage.getItem('urlResolve') + urlResolve = JSON.parse(urlResolve); + urlResolve = urlResolve ? urlResolve : {}; + urlResolve[opts.route] = res; + localStorage.setItem('urlResolve', JSON.stringify(urlResolve)); + return res.data.urlResolver + }); + } /** diff --git a/packages/venia-concept/src/RootComponents/NotFound/index.js b/packages/venia-concept/src/RootComponents/NotFound/index.js new file mode 100644 index 0000000000..c098bd07f4 --- /dev/null +++ b/packages/venia-concept/src/RootComponents/NotFound/index.js @@ -0,0 +1,6 @@ +/** + * @RootComponent + * description = 'Page to display when offline' + * pageTypes = NOTFOUND + */ +export { default } from './notFound'; diff --git a/packages/venia-concept/src/RootComponents/NotFound/notFound.css b/packages/venia-concept/src/RootComponents/NotFound/notFound.css new file mode 100644 index 0000000000..e98375699b --- /dev/null +++ b/packages/venia-concept/src/RootComponents/NotFound/notFound.css @@ -0,0 +1,10 @@ +.root { + padding: 1rem; +} + +.title { + font-size: 1.5rem; + font-weight: 400; + margin: 0 0 1rem; + padding: 0.5rem; +} diff --git a/packages/venia-concept/src/RootComponents/NotFound/notFound.js b/packages/venia-concept/src/RootComponents/NotFound/notFound.js new file mode 100644 index 0000000000..654fc1fa0f --- /dev/null +++ b/packages/venia-concept/src/RootComponents/NotFound/notFound.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; +import classify from 'src/classify'; +import defaultClasses from './notFound.css'; + +class NotFound extends Component { + // TODO: Should not be a default here, we just don't have + // the wiring in place to map route info down the tree (yet) + static defaultProps = { + id: 3 + }; + + goBack() { + history.back(); + } + + render() { + const { classes } = this.props; + + return ( +
+

Offline!

+ +
+ ); + } +} + +export default classify(defaultClasses)(NotFound); diff --git a/packages/venia-concept/src/sw.js b/packages/venia-concept/src/sw.js new file mode 100644 index 0000000000..44abbe7a7a --- /dev/null +++ b/packages/venia-concept/src/sw.js @@ -0,0 +1,88 @@ +workbox.skipWaiting(); +workbox.clientsClaim(); + +function precacheManifest() { + fetch('/roots-manifest.json') + .then(function(response) { + return response.json(); + }) + .then(function(json) { + let toCache = []; + Object.keys(json).forEach((key) => { + toCache.push(json[key].chunkName); + }); + toCache = toCache.concat([ + 'fallback.html', + 'roots-manifest.json', + 'favicon.ico', + '/' + ]); + workbox.precaching.precache(toCache) + + }); +} + + +precacheManifest(); + +workbox.routing.registerRoute( + '/', + workbox.strategies.staleWhileRevalidate() +); + +workbox.routing.registerRoute( + new RegExp('\\.html$'), + workbox.strategies.networkFirst() +); + +workbox.routing.registerRoute( + new RegExp('/\.\\.js$'), + workbox.strategies.staleWhileRevalidate() +); + +workbox.routing.registerRoute( + new RegExp('roots-manifest.json'), + workbox.strategies.staleWhileRevalidate() +); + +workbox.routing.registerRoute( + /\.(?:png|gif|jpg|jpeg|svg)$/, + workbox.strategies.cacheFirst({ + cacheName: 'images', + plugins: [ + new workbox.expiration.Plugin({ + maxEntries: 60, + maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days + }), + ], + }) +); + +workbox.precaching.precacheAndRoute(self.__precacheManifest || []); + +// This "catch" handler is triggered when any of the other routes fail to +// generate a response. + +// TODO: Decide how this fits in with the NotFound root component. +workbox.routing.setCatchHandler(({event, request, url}) => { + // Use event, request, and url to figure out how to respond. + // One approach would be to use request.destination, see + // https://medium.com/dev-channel/service-worker-caching-strategies-based-on-request-types-57411dd7652c + switch (event.request.destination) { + case 'document': + return caches.match('/fallback.html'); + break; + + case 'image': + return caches.match(FALLBACK_IMAGE_URL); + break; + + case 'font': + return caches.match(FALLBACK_FONT_URL); + break; + + default: + // If we don't have a fallback, just return an error response. + return Response.error(); + } +}); diff --git a/packages/venia-concept/templates/fallback.mst b/packages/venia-concept/templates/fallback.mst new file mode 100644 index 0000000000..9767d74aad --- /dev/null +++ b/packages/venia-concept/templates/fallback.mst @@ -0,0 +1,9 @@ +{{> templates/doctype-and-head-start}} + Venia - Not Found + + +
+

Not found!

+
+ + diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml index ce531d9553..ac97573199 100644 --- a/packages/venia-concept/venia-upward.yml +++ b/packages/venia-concept/venia-upward.yml @@ -7,6 +7,9 @@ response: - matches: request.url.pathname pattern: '^/(graphql|rest|media/)' use: proxy + - matches: request.url.pathname + pattern: '^/fallback.html' + use: fallback - matches: request.url.pathname pattern: '^/manifest.webmanifest' use: manifest @@ -149,6 +152,20 @@ manifest: file: inline: ./manifest.webmanifest +fallback: + inline: + status: 200 + headers: + inline: + content-type: + inline: 'text/html' + body: + engine: mustache + template: ./templates/fallback.mst + provide: + model: resource.model + + static: directory: inline: './dist' diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 79b556fbc1..808f2d56b4 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -1,10 +1,10 @@ require('dotenv').config(); +const workboxPlugin = require('workbox-webpack-plugin'); const webpack = require('webpack'); const { WebpackTools: { MagentoRootComponentsPlugin, - ServiceWorkerPlugin, MagentoResolver, UpwardPlugin, PWADevServer @@ -17,6 +17,7 @@ const UglifyPlugin = require('uglifyjs-webpack-plugin'); const configureBabel = require('./babel.config.js'); const themePaths = { + templates: path.resolve(__dirname, 'templates'), src: path.resolve(__dirname, 'src'), output: path.resolve(__dirname, 'dist') }; @@ -121,12 +122,10 @@ module.exports = async function(env) { process.env.MAGENTO_BACKEND_PRODUCT_MEDIA_PATH ) }), - new ServiceWorkerPlugin({ - env, - enableServiceWorkerDebugging, - serviceWorkerFileName, - paths: themePaths, - runtimeCacheAssetPath: '\\.html$' + // TODO: Move to ServiceWorkerPlugin in PWA Buildpack + new workboxPlugin.InjectManifest({ + swSrc: `./src/sw.js`, + swDest: `sw.js` }) ] }; @@ -163,7 +162,12 @@ module.exports = async function(env) { new UpwardPlugin( config.devServer, path.resolve(__dirname, 'venia-upward.yml') - ) + ), + // TODO: Move to ServiceWorkerPlugin in PWA Buildpack + new workboxPlugin.InjectManifest({ + swSrc: `./src/sw.js`, + swDest: `sw.js` + }) ); } else if (phase === 'production') { config.performance = { From 97499928f6db4aca0f8b4bf3c1a97636da0ae7bc Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Thu, 1 Nov 2018 13:04:37 -0700 Subject: [PATCH 05/20] Add inject-mainfest option to PWA-Buildpack --- .../plugins/ServiceWorkerPlugin.js | 51 +++++++++++++------ packages/venia-concept/webpack.config.js | 26 +++++----- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js index f4f0cc9b66..8abdb1f1b0 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js @@ -13,7 +13,7 @@ class ServiceWorkerPlugin { ServiceWorkerPlugin.validateOptions('ServiceWorkerPlugin', config); this.config = config; } - applyWorkbox(compiler) { + applyGenerateSW(compiler) { const config = { // `globDirectory` and `globPatterns` must match at least 1 file // otherwise workbox throws an error @@ -28,30 +28,42 @@ class ServiceWorkerPlugin { swDest: this.config.serviceWorkerFileName }; - if (this.config.runtimeCacheAssetPath) { - config.runtimeCaching = [ - // TODO: Make this an argument like this.config.runtimeCacheAssetPath? - { - urlPattern: '/', - handler: 'staleWhileRevalidate' - }, - { - urlPattern: new RegExp(this.config.runtimeCacheAssetPath), - handler: 'staleWhileRevalidate' - } - ]; + if (this.config.runtimeCacheConfig) { + config.runtimeCaching = this.config.runtimeCacheConfig; } new WorkboxPlugin.GenerateSW(config).apply(compiler); } + + configureInjectManifest() { + let injectManifest; + if (this.config.swPath) { + injectManifest = new WorkboxPlugin.InjectManifest(this.config.swPath); + } else { + injectManifest = new WorkboxPlugin.InjectManifest({ + swSrc: this.config.paths.src + '/sw.js', + swDest: this.config.paths.dest + '/sw.js' + }) + } + return injectManifest; + } + + applyInjectManifest(compiler) { + this.configureInjectManifest().apply(compiler); + } + + apply(compiler) { if (this.config.env.phase === 'development') { // add a WriteFilePlugin to write out the service worker to the filesystem so it can be served by M2, even though it's under dev - if (this.config.enableServiceWorkerDebugging) { + if (this.config.enableServiceWorkerDebugging && !this.config.injectManifest) { + console.log('hey'); new WriteFileWebpackPlugin({ test: new RegExp(this.config.serviceWorkerFileName + '$'), log: true }).apply(compiler); - this.applyWorkbox(compiler); + this.applyGenerateSW(compiler); + } else if (this.config.enableServiceWorkerDebugging && this.config.injectManifest) { + this.applyInjectManifest(compiler); } else { // TODO: (feature) emit a structured { code, severity, resolution } object // on Environment that might throw and might not @@ -63,5 +75,14 @@ class ServiceWorkerPlugin { this.applyWorkbox(compiler); } } + + applyWorkbox(compiler) { + if (this.config.injectManifest) { + this.applyInjectManifest(compiler); + } else { + this.applyGenerateSW(compiler); + } + + } } module.exports = ServiceWorkerPlugin; diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 808f2d56b4..7293ae8638 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -5,6 +5,7 @@ const webpack = require('webpack'); const { WebpackTools: { MagentoRootComponentsPlugin, + ServiceWorkerPlugin, MagentoResolver, UpwardPlugin, PWADevServer @@ -120,17 +121,23 @@ module.exports = async function(env) { */ 'process.env.MAGENTO_BACKEND_PRODUCT_MEDIA_PATH': JSON.stringify( process.env.MAGENTO_BACKEND_PRODUCT_MEDIA_PATH - ) + ), }), - // TODO: Move to ServiceWorkerPlugin in PWA Buildpack - new workboxPlugin.InjectManifest({ - swSrc: `./src/sw.js`, - swDest: `sw.js` + new ServiceWorkerPlugin({ + env, + enableServiceWorkerDebugging, + serviceWorkerFileName, + paths: themePaths, + injectManifest: true, + swPath: { + swSrc: './src/sw.js', + swDest: 'sw.js' + } }) ] }; - if (phase === 'development') { config.devtool = 'eval-source-map'; + if (phase === 'development') { const devServerConfig = { publicPath: config.output.publicPath, @@ -162,12 +169,7 @@ module.exports = async function(env) { new UpwardPlugin( config.devServer, path.resolve(__dirname, 'venia-upward.yml') - ), - // TODO: Move to ServiceWorkerPlugin in PWA Buildpack - new workboxPlugin.InjectManifest({ - swSrc: `./src/sw.js`, - swDest: `sw.js` - }) + ) ); } else if (phase === 'production') { config.performance = { From 9e8fa41438358ecfa765cacba64a5e9cb1d42928 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Fri, 2 Nov 2018 14:59:23 -0700 Subject: [PATCH 06/20] Add tests - Add test for offline functionality of resolveUnkownRoute - Add test for injectManifest of ServiceWorkerPlugin --- .../__tests__/resolveUnknownRoute.test.js | 113 ++++++++++++++++-- .../plugins/ServiceWorkerPlugin.js | 5 +- .../__tests__/ServiceWorkerPlugin.spec.js | 33 +++++ 3 files changed, 139 insertions(+), 12 deletions(-) diff --git a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js index c2f6720d01..52a5a7d173 100644 --- a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js +++ b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js @@ -7,10 +7,15 @@ const urlResolverRes = type => } }); -test('Happy path: resolves w/ rootChunkID and rootModuleID for first matching component found', async () => { - fetch.mockResponseOnce(urlResolverRes('PRODUCT')); - fetch.mockResponseOnce( - JSON.stringify({ +const NotFoundManifest = { + NotFound: { + rootChunkID: 3, + rootModuleID: 100, + pageTypes: ['NOTFOUND'] + } + } + +const mockManifest = { Category: { rootChunkID: 2, rootModuleID: 100, @@ -19,9 +24,38 @@ test('Happy path: resolves w/ rootChunkID and rootModuleID for first matching co Product: { rootChunkID: 1, rootModuleID: 99, - pageTypes: ['PRODUCT'] // match - } - }) + pageTypes: ['PRODUCT'] + }, + ...NotFoundManifest + } + +const cachedResponse = JSON.stringify({ + 'foo-bar.html': { + ...JSON.parse(urlResolverRes('PRODUCT')) + } +}); + +const isOnline = _value => ({ + get: () => _value, + set: (v) => _value = v +}); + +Object.defineProperty(navigator, 'onLine', isOnline(true)); + +beforeEach(() => { + navigator.onLine = true; + fetch.resetMocks(); + clearLocalStorage('urlResolve'); +}); + +function clearLocalStorage(item) { + localStorage.setItem(item, null); +} + +test('Happy path: resolves w/ rootChunkID and rootModuleID for first matching component found', async () => { + fetch.mockResponseOnce(urlResolverRes('PRODUCT')); + fetch.mockResponseOnce( + JSON.stringify(mockManifest) ); const res = await resolveUnknownRoute({ @@ -32,5 +66,68 @@ test('Happy path: resolves w/ rootChunkID and rootModuleID for first matching co expect(res.rootChunkID).toBe(1); expect(res.rootModuleID).toBe(99); - fetch.resetMocks(); }); + +test('returns NOTFOUND when offline and requested content is not in cache ', async () => { + navigator.onLine = false; + + fetch.mockResponseOnce( + JSON.stringify(mockManifest) + ); + const res = await resolveUnknownRoute({ + route: 'foo-bar.html', + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + expect(res).toHaveProperty('rootChunkID', NotFoundManifest.NotFound.rootChunkID); +}); + +test('stores response of urlResolver in cache', async () => { + fetch.mockResponseOnce(urlResolverRes('PRODUCT')); + fetch.mockResponseOnce( + JSON.stringify(mockManifest) + ); + + const url = 'foo-bar.html'; + + await resolveUnknownRoute({ + route: url, + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + expect(localStorage.getItem('urlResolve')).not.toBeNull() +}); + +test('does not call fetchRoute when response is cached', async () => { + + localStorage.setItem('urlResolve', cachedResponse); + + fetch.mockResponseOnce( + JSON.stringify(mockManifest) + ); + await resolveUnknownRoute({ + route: 'foo-bar.html', + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + + expect(fetch).toHaveBeenCalledTimes(1); +}); + +test('calls fetchRoute when response is not cached', async () => { + fetch.mockResponseOnce(urlResolverRes('PRODUCT')); + fetch.mockResponseOnce( + JSON.stringify(mockManifest) + ); + await resolveUnknownRoute({ + route: 'foo-bar.html', + apiBase: 'https://store.com', + __tmp_webpack_public_path__: 'https://dev-server.com/pub' + }); + + expect(fetch).toHaveBeenCalledTimes(2); +}); + diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js index 8abdb1f1b0..878ea1e1de 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js @@ -48,15 +48,13 @@ class ServiceWorkerPlugin { } applyInjectManifest(compiler) { - this.configureInjectManifest().apply(compiler); + debugger; } - apply(compiler) { if (this.config.env.phase === 'development') { // add a WriteFilePlugin to write out the service worker to the filesystem so it can be served by M2, even though it's under dev if (this.config.enableServiceWorkerDebugging && !this.config.injectManifest) { - console.log('hey'); new WriteFileWebpackPlugin({ test: new RegExp(this.config.serviceWorkerFileName + '$'), log: true @@ -82,7 +80,6 @@ class ServiceWorkerPlugin { } else { this.applyGenerateSW(compiler); } - } } module.exports = ServiceWorkerPlugin; diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js index b751a9bb0e..bed77a83b7 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js @@ -137,3 +137,36 @@ test('.apply generates and writes out a serviceworker when enableServiceWorkerDe }) ); }); + +test('.apply uses `InjectManifest` when `injectManifest` is `true`', () => { + const plugin = new ServiceWorkerPlugin({ + env: { + phase: 'development' + }, + enableServiceWorkerDebugging: true, + serviceWorkerFileName: 'sw.js', + injectManifest: true, + paths: { + output: 'path/to/assets' + }, + swPath: { + swSrc: 'path/to/sw', + swDest: 'path/to/dest' + } + }); + + const fakeCompiler = {}; + const workboxApply = jest.fn(); + WorkboxPlugin.InjectManifest.mockImplementationOnce(() => ({ + apply: workboxApply + })); + + plugin.apply(fakeCompiler); + + expect(WorkboxPlugin.InjectManifest).toHaveBeenCalledWith( + expect.objectContaining({ + swDest: "path/to/dest", + swSrc: "path/to/sw" + }) + ); +}); From 0422136989d6446dd670dbdccc874f272b1c65b8 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Tue, 6 Nov 2018 10:39:22 -0800 Subject: [PATCH 07/20] Add venia icons to manifest, fix injectManifest - SW.js is now served with `Service-Worker-Allowed` header - manifest icons are moved to the dist directory with `copy-webpack-plugin` - manifest.webmanifest -> manifest.json --- package.json | 1 + .../plugins/ServiceWorkerPlugin.js | 2 +- packages/venia-concept/manifest.json | 23 ++++++++++++ packages/venia-concept/manifest.webmanifest | 13 ------- .../media/icons/venia_circle_192.png | Bin 0 -> 18550 bytes .../media/icons/venia_circle_512.png | Bin 0 -> 31757 bytes .../media/icons/venia_square_192.png | Bin 0 -> 11053 bytes .../media/icons/venia_square_512.png | Bin 0 -> 19541 bytes .../templates/doctype-and-head-start.mst | 2 +- packages/venia-concept/venia-upward.yml | 35 +++++++++--------- packages/venia-concept/webpack.config.js | 10 ++++- 11 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 packages/venia-concept/manifest.json delete mode 100644 packages/venia-concept/manifest.webmanifest create mode 100644 packages/venia-concept/media/icons/venia_circle_192.png create mode 100644 packages/venia-concept/media/icons/venia_circle_512.png create mode 100644 packages/venia-concept/media/icons/venia_square_192.png create mode 100644 packages/venia-concept/media/icons/venia_square_512.png diff --git a/package.json b/package.json index f966393ec2..5355b81a21 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "chokidar": "^2.0.4", "concurrently": "^4.0.1", "contains-path": "^1.0.0", + "copy-webpack-plugin": "^4.6.0", "coveralls": "^3.0.1", "css-loader": "~0", "danger": "^3.9.0", diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js index 878ea1e1de..f969667056 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js @@ -48,7 +48,7 @@ class ServiceWorkerPlugin { } applyInjectManifest(compiler) { - debugger; + this.configureInjectManifest().apply(compiler); } apply(compiler) { diff --git a/packages/venia-concept/manifest.json b/packages/venia-concept/manifest.json new file mode 100644 index 0000000000..e0c9793806 --- /dev/null +++ b/packages/venia-concept/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "Venia", + "short_name": "Venia", + "start_url": "/", + "theme_color": "#ff6334", + "display": "standalone", + "background_color": "#fff", + "description": "Shop the look", + "icons": [{ + "src": "/media/icons/venia_circle_144.png", + "sizes": "144x144", + "type": "image/png" + },{ + "src": "/media/icons/venia_circle_192.png", + "sizes": "192x192", + "type": "image/png" + },{ + "src": "/media/icons/venia_circle_512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/packages/venia-concept/manifest.webmanifest b/packages/venia-concept/manifest.webmanifest deleted file mode 100644 index 34b2c5c2ab..0000000000 --- a/packages/venia-concept/manifest.webmanifest +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "Venia", - "short_name": "Venia", - "start_url": ".", - "display": "standalone", - "background_color": "#fff", - "description": "Shopping", - "icons": [{ - "src": "/venia_circle_144.png", - "sizes": "144x144", - "type": "image/png" - }] -} diff --git a/packages/venia-concept/media/icons/venia_circle_192.png b/packages/venia-concept/media/icons/venia_circle_192.png new file mode 100644 index 0000000000000000000000000000000000000000..4a0c3c3d20cec83476db46cc97af6d981f2bf80a GIT binary patch literal 18550 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 H;1mBB+X43S literal 0 HcmV?d00001 diff --git a/packages/venia-concept/media/icons/venia_circle_512.png b/packages/venia-concept/media/icons/venia_circle_512.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2c8e483c377d576870f08e4d6a8a8341fbc5bb GIT binary patch literal 31757 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 Gq5lW?`^*af literal 0 HcmV?d00001 diff --git a/packages/venia-concept/media/icons/venia_square_192.png b/packages/venia-concept/media/icons/venia_square_192.png new file mode 100644 index 0000000000000000000000000000000000000000..6cfe4069bc31605a096ef9ad753a957fb032756d GIT binary patch literal 11053 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 literal 0 HcmV?d00001 diff --git a/packages/venia-concept/media/icons/venia_square_512.png b/packages/venia-concept/media/icons/venia_square_512.png new file mode 100644 index 0000000000000000000000000000000000000000..9c0ea7d0066a62ce3c17893267d394b8905aafec GIT binary patch literal 19541 zcmb?@XH-*P5a&y%f)r6uI*5RDr3*qp1f=&OUFjVJ>5yOnl_p4$8W8DK=`|<0@#0@@ThRyb$|avikNp6E`s&3G1jxv`0bZo=*U(X=_)ATG zMfh&@>YNM!Z~+<*m5qaDw&sKVGmpcur$ZzN;1OV@YWVq~0fWe621fakTRmUgEj3v5 z?45meXHJ^^GG!$iG-iz(G)8;V>(lG+ZE0X?b^PQtGv!9pg%#S~O3283Vee@gG@Pzv zpDs+A;;zVMQhj^P%m?y_o^)q`lS?4!udeXWUPN}jy*u|M{0@K`!k$@jTmpc`_%91$ zvJvu(*QcnU*cugfKe&injm=9jk zuvGx>m_1_v;yGaiH4P*Zy(=RlQWk;@Qu{HfVvK1Ww}D&(Y56_y%-@JUa{knCPJVdO2CPS5ErA*!L*5{sZoy_EG0Y zv*R-}nq^SH?kYPwyIk3q;!mpp282(}n0ep;z-D$SXcYfCGDnqov~L(Nh#Ul240ZSPH1YG>f1yZ- z0`Ald5sSf9Y&D>D%OJoa05W&B8E~F25q?g?h@jIL0oV60zLT(N&rFo0&)GDag2#R-`zKIGR2DzMy@hx>_y& zY%iwqrR^c6=hMi*(7vdF&PN)`bR1LqAHj5!xM>7H4

fZ~rs4aw0mq3X0aCgl4~q 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 literal 0 HcmV?d00001 diff --git a/packages/venia-concept/templates/doctype-and-head-start.mst b/packages/venia-concept/templates/doctype-and-head-start.mst index 01d44b5bb9..7958815ab2 100644 --- a/packages/venia-concept/templates/doctype-and-head-start.mst +++ b/packages/venia-concept/templates/doctype-and-head-start.mst @@ -4,4 +4,4 @@ - + diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml index ac97573199..901f2058bc 100644 --- a/packages/venia-concept/venia-upward.yml +++ b/packages/venia-concept/venia-upward.yml @@ -11,14 +11,14 @@ response: pattern: '^/fallback.html' use: fallback - matches: request.url.pathname - pattern: '^/manifest.webmanifest' + pattern: '^/sw.js' + use: serviceworker + - matches: request.url.pathname + pattern: '^/manifest.json' use: manifest - matches: request.url.pathname pattern: '^/favicon.ico' use: favicon - - matches: request.url.pathname - pattern: '^/venia_circle_144.png' - use: homescreen - matches: urlKey pattern: '.' use: appShell @@ -128,19 +128,6 @@ favicon: inline: ./media/favicon.ico encoding: binary -homescreen: - inline: - status: 200 - headers: - inline: - content-type: - inline: 'image/png' - body: - file: - inline: ./media/icons/venia_circle_144.png - encoding: binary - - manifest: inline: status: 200 @@ -150,7 +137,7 @@ manifest: inline: application/json body: file: - inline: ./manifest.webmanifest + inline: ./manifest.json fallback: inline: @@ -165,6 +152,18 @@ fallback: provide: model: resource.model +serviceworker: + inline: + status: 200 + headers: + inline: + service-worker-allowed: + inline: '/' + content-type: + inline: 'application/javascript' + body: + file: + inline: ./dist/sw.js static: directory: diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 7293ae8638..b5a9075df9 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -1,6 +1,6 @@ require('dotenv').config(); -const workboxPlugin = require('workbox-webpack-plugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); const webpack = require('webpack'); const { WebpackTools: { @@ -18,6 +18,7 @@ const UglifyPlugin = require('uglifyjs-webpack-plugin'); const configureBabel = require('./babel.config.js'); const themePaths = { + media: path.resolve(__dirname, 'media'), templates: path.resolve(__dirname, 'templates'), src: path.resolve(__dirname, 'src'), output: path.resolve(__dirname, 'dist') @@ -133,7 +134,12 @@ module.exports = async function(env) { swSrc: './src/sw.js', swDest: 'sw.js' } - }) + }), + new CopyWebpackPlugin([{ + from: `${themePaths.media}/**/*`, + to: themePaths.output, + toType: 'dir' + }]) ] }; config.devtool = 'eval-source-map'; From ab042dffbdf22a377dd1f170b7ed9f12e0398a33 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Tue, 6 Nov 2018 10:52:28 -0800 Subject: [PATCH 08/20] Add copy-webpack-plugin --- package-lock.json | 66 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0e0a8d8189..ba2bfc7cba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1739,7 +1739,7 @@ }, "style-loader": { "version": "0.20.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", + "resolved": "http://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==", "dev": true, "requires": { @@ -1911,7 +1911,7 @@ }, "@types/accepts": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "resolved": "http://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", "dev": true, "requires": { @@ -1920,7 +1920,7 @@ }, "@types/async": { "version": "2.0.49", - "resolved": "https://registry.npmjs.org/@types/async/-/async-2.0.49.tgz", + "resolved": "http://registry.npmjs.org/@types/async/-/async-2.0.49.tgz", "integrity": "sha512-Benr3i5odUkvpFkOpzGqrltGdbSs+EVCkEBGXbuR7uT0VzhXKIkhem6PDzHdx5EonA+rfbB3QvP6aDOw5+zp5Q==", "dev": true, "optional": true @@ -1967,7 +1967,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", "dev": true }, @@ -7635,6 +7635,50 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "copy-webpack-plugin": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", + "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + }, + "dependencies": { + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "core-js": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", @@ -7801,7 +7845,7 @@ }, "css-color-names": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", "dev": true }, @@ -7817,7 +7861,7 @@ }, "css-loader": { "version": "0.28.11", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", "dev": true, "requires": { @@ -10343,7 +10387,7 @@ }, "file-loader": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { @@ -14648,7 +14692,7 @@ }, "jest-get-type": { "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "resolved": "http://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", "dev": true }, @@ -17489,7 +17533,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -24147,7 +24191,7 @@ }, "storybook-readme": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/storybook-readme/-/storybook-readme-3.3.0.tgz", + "resolved": "http://registry.npmjs.org/storybook-readme/-/storybook-readme-3.3.0.tgz", "integrity": "sha512-SxaprEbFxEcRwXLPWKvL8uD3/NL41qTabKtLpI7O4v6UMbgRMg3IqWkOBJazxNtKOlEw7i4CTY76OZ7jOqU0WA==", "dev": true, "requires": { @@ -27135,7 +27179,7 @@ }, "xmlbuilder": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", "dev": true, "requires": { From 2658b5e6618131be8aa040fdc39e68a54c359c9a Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Tue, 6 Nov 2018 13:37:18 -0800 Subject: [PATCH 09/20] Add offline indicator --- package-lock.json | 14 +++++---- .../venia-concept/src/actions/app/actions.js | 2 +- .../src/components/AppShell/appShell.js | 18 +++++++++-- .../src/components/OnlineIndicator/index.js | 1 + .../OnlineIndicator/onlineIndicator.css | 31 +++++++++++++++++++ .../OnlineIndicator/onlineIndicator.js | 28 +++++++++++++++++ packages/venia-concept/src/index.css | 2 ++ packages/venia-concept/src/index.js | 5 +++ packages/venia-concept/src/reducers/app.js | 15 ++++++++- 9 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 packages/venia-concept/src/components/OnlineIndicator/index.js create mode 100644 packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css create mode 100644 packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js diff --git a/package-lock.json b/package-lock.json index a5102a22f1..b23ab8b68d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2100,6 +2100,12 @@ "optimism": "^0.6.6" } }, + "apollo-cache-persist": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/apollo-cache-persist/-/apollo-cache-persist-0.1.1.tgz", + "integrity": "sha512-/7GAyblPR169ryW3ugbtHqiU0UGkhIt10NeaO2gn2ClxjLHF/nIkJD5mx/0OCF2vLNbbnzLZVDeIO1pf72TrEA==", + "dev": true + }, "apollo-client": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.4.2.tgz", @@ -8545,14 +8551,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8572,8 +8576,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -8721,7 +8724,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } diff --git a/packages/venia-concept/src/actions/app/actions.js b/packages/venia-concept/src/actions/app/actions.js index 972465ba4d..3074eb31b6 100644 --- a/packages/venia-concept/src/actions/app/actions.js +++ b/packages/venia-concept/src/actions/app/actions.js @@ -1,6 +1,6 @@ import { createActions } from 'redux-actions'; const prefix = 'APP'; -const actionTypes = ['TOGGLE_DRAWER']; +const actionTypes = ['TOGGLE_DRAWER', 'SET_ONLINE', 'SET_OFFLINE']; export default createActions(...actionTypes, { prefix }); diff --git a/packages/venia-concept/src/components/AppShell/appShell.js b/packages/venia-concept/src/components/AppShell/appShell.js index 76635564b0..7be5b5183c 100644 --- a/packages/venia-concept/src/components/AppShell/appShell.js +++ b/packages/venia-concept/src/components/AppShell/appShell.js @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import { bool, func, shape, string } from 'prop-types'; import { Page } from '@magento/peregrine'; +import { connect } from 'react-redux'; import classify from 'src/classify'; import ErrorView from 'src/components/ErrorView'; @@ -9,6 +10,7 @@ import Mask from 'src/components/Mask'; import MiniCart from 'src/components/MiniCart'; import Navigation from 'src/components/Navigation'; import defaultClasses from './appShell.css'; +import OnlineIndicator from 'src/components/OnlineIndicator'; const renderRoutingError = props => ; @@ -26,7 +28,7 @@ class AppShell extends Component { }; render() { - const { app, classes, closeDrawer } = this.props; + const { app, classes, closeDrawer, isOnline } = this.props; const { drawer, overlay } = app; const navIsOpen = drawer === 'nav'; const cartIsOpen = drawer === 'cart'; @@ -35,6 +37,7 @@ class AppShell extends Component { return (

+ {renderRoutingError}
@@ -45,4 +48,15 @@ class AppShell extends Component { } } -export default classify(defaultClasses)(AppShell); +const mapStateToProps = ({ app }) => { + const { isOnline } = app; + return { + isOnline + }; +}; + +export default connect( + mapStateToProps, + null +) +(classify(defaultClasses)(AppShell)); diff --git a/packages/venia-concept/src/components/OnlineIndicator/index.js b/packages/venia-concept/src/components/OnlineIndicator/index.js new file mode 100644 index 0000000000..5a0a7d60d3 --- /dev/null +++ b/packages/venia-concept/src/components/OnlineIndicator/index.js @@ -0,0 +1 @@ +export { default } from './onlineIndicator'; diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css new file mode 100644 index 0000000000..dd95f9fdfc --- /dev/null +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css @@ -0,0 +1,31 @@ +.root { + align-items: center; + display: inline-flex; + justify-content: center; + width: 100%; + height: 0px; + background-color: rgb(var(--venia-warning-light)); + margin-bottom: .3em; + animation-name: slidein; + animation-duration: 244ms; + animation-fill-mode: forwards; + overflow: hidden; +} + +.root span { + color: rgb(var(--venia-warning-dark)); +} + +.root p { + margin-left: 1em; +} + +@keyframes slidein { + from { + height: 0; + } + to { + height: 3rem; + } +} + diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js new file mode 100644 index 0000000000..51dc92da0b --- /dev/null +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; +import Icon from 'src/components/Icon'; +import PropTypes from 'prop-types'; + +import classify from 'src/classify'; +import defaultClasses from './onlineIndicator.css'; + +class OnlineIndicator extends Component { + static propTypes = { + classes: PropTypes.shape({ + root: PropTypes.string + }), + isOnline: PropTypes.bool + }; + + render() { + const { isOnline, classes } = this.props; + + return (!isOnline + ?
+ +

You are offline. Some features may be unavailable.

+
+ : null) + } +} + +export default classify(defaultClasses)(OnlineIndicator); diff --git a/packages/venia-concept/src/index.css b/packages/venia-concept/src/index.css index 88bae4772a..9630b0a60b 100644 --- a/packages/venia-concept/src/index.css +++ b/packages/venia-concept/src/index.css @@ -12,6 +12,8 @@ html { --venia-text-alt: 117, 117, 117; --venia-text-hint: 158, 158, 158; --venia-text-spot: 255, 99, 51; + --venia-warning-dark: 249, 93, 94; + --venia-warning-light: 254, 229, 232; } html { diff --git a/packages/venia-concept/src/index.js b/packages/venia-concept/src/index.js index 8e4b4928be..5dbf3ee66e 100644 --- a/packages/venia-concept/src/index.js +++ b/packages/venia-concept/src/index.js @@ -11,10 +11,12 @@ import { Router, Util } from '@magento/peregrine'; import store from 'src/store'; // import { getUserDetails } from 'src/actions/user'; +import app from 'src/actions/app'; import AppShell from 'src/components/AppShell'; import ensureDirURI from 'src/util/ensureDirUri'; import './index.css'; + // store.dispatch(getUserDetails()); const { BrowserPersistence } = Util; @@ -77,3 +79,6 @@ if (process.env.SERVICE_WORKER && 'serviceWorker' in navigator) { }); }); } + +window.addEventListener('online', () => {store.dispatch(app.setOnline())}); +window.addEventListener('offline', () => {store.dispatch(app.setOffline())}); diff --git a/packages/venia-concept/src/reducers/app.js b/packages/venia-concept/src/reducers/app.js index e657da621d..9fdb599c4e 100644 --- a/packages/venia-concept/src/reducers/app.js +++ b/packages/venia-concept/src/reducers/app.js @@ -7,7 +7,8 @@ export const name = 'app'; const initialState = { drawer: null, overlay: false, - pending: {} + pending: {}, + isOnline: navigator.onLine }; const reducerMap = { @@ -17,6 +18,18 @@ const reducerMap = { drawer: payload, overlay: !!payload }; + }, + [actions.setOnline]: (state) => { + return { + ...state, + isOnline: true + } + }, + [actions.setOffline]: (state) => { + return { + ...state, + isOnline: false + } } }; From cf3d2b069e5ae3b31f325abb10cb4e79fd1c1172 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Tue, 6 Nov 2018 15:05:11 -0800 Subject: [PATCH 10/20] Style offline indicator, fix offline to online - `getRouteComponent` now checks if the current route has the `NotFoundComponent` stored in state. If so, reload the route when online Progress towards #374 --- packages/peregrine/src/Router/MagentoRouteHandler.js | 9 ++++++++- .../src/components/OnlineIndicator/onlineIndicator.css | 3 +++ .../src/components/OnlineIndicator/onlineIndicator.js | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/peregrine/src/Router/MagentoRouteHandler.js b/packages/peregrine/src/Router/MagentoRouteHandler.js index 2bb9c87890..6148116da5 100644 --- a/packages/peregrine/src/Router/MagentoRouteHandler.js +++ b/packages/peregrine/src/Router/MagentoRouteHandler.js @@ -37,7 +37,14 @@ export default class MagentoRouteHandler extends Component { const { pathname } = props.location; const isKnown = state.componentMap.has(pathname); - if (!isKnown) { + // id of -1 is currently what defines a `NOTFOUND` component + const isNotFoundComponent = isKnown + ? state.componentMap.get(pathname).id === -1 + : false; + + const shouldReloadRoute = (isNotFoundComponent && navigator.onLine); + + if (!isKnown || shouldReloadRoute) { this.getRouteComponent(); } } diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css index dd95f9fdfc..efbf02246a 100644 --- a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css @@ -6,10 +6,12 @@ height: 0px; background-color: rgb(var(--venia-warning-light)); margin-bottom: .3em; + padding: 0 2.4rem; animation-name: slidein; animation-duration: 244ms; animation-fill-mode: forwards; overflow: hidden; + font-size: .9em; } .root span { @@ -18,6 +20,7 @@ .root p { margin-left: 1em; + line-height: 1.3em; } @keyframes slidein { diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js index 51dc92da0b..d899fdaa8e 100644 --- a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js @@ -19,7 +19,7 @@ class OnlineIndicator extends Component { return (!isOnline ?
-

You are offline. Some features may be unavailable.

+

You are offline. Some features may be unavailable.

: null) } From b372fab1f271137188206b35ff851ebfb29f3f82 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Thu, 8 Nov 2018 13:23:26 -0800 Subject: [PATCH 11/20] Add online indicator - Shows online status when switching from offline -> online - Animation for both online and offline --- .../src/components/AppShell/appShell.js | 28 +++++++++-- .../src/components/Main/main.css | 10 ---- .../OnlineIndicator/onlineIndicator.css | 47 ++++++++++++++++--- .../OnlineIndicator/onlineIndicator.js | 12 +++-- 4 files changed, 75 insertions(+), 22 deletions(-) diff --git a/packages/venia-concept/src/components/AppShell/appShell.js b/packages/venia-concept/src/components/AppShell/appShell.js index 7be5b5183c..7940927ea8 100644 --- a/packages/venia-concept/src/components/AppShell/appShell.js +++ b/packages/venia-concept/src/components/AppShell/appShell.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import { findDOMNode } from 'react-dom'; import { bool, func, shape, string } from 'prop-types'; import { Page } from '@magento/peregrine'; import { connect } from 'react-redux'; @@ -27,9 +28,30 @@ class AppShell extends Component { closeDrawer: func.isRequired }; + state = { + hasBeenOffline: false + }; + + get onlineIndicator() { + const { isOnline } = this.props; + const { hasBeenOffline } = this.state; + + if ( !isOnline && !hasBeenOffline) { + this.setState({ + hasBeenOffline: true + }); + } + + return ( hasBeenOffline + ? + : null) + } + render() { - const { app, classes, closeDrawer, isOnline } = this.props; - const { drawer, overlay } = app; + const { app, classes, closeDrawer } = this.props; + const { onlineIndicator } = this; + const { drawer, overlay } = app const navIsOpen = drawer === 'nav'; const cartIsOpen = drawer === 'cart'; const className = overlay ? classes.root_masked : classes.root; @@ -37,7 +59,7 @@ class AppShell extends Component { return (
- + {onlineIndicator} {renderRoutingError}
diff --git a/packages/venia-concept/src/components/Main/main.css b/packages/venia-concept/src/components/Main/main.css index b85bd1465e..30e23eb485 100644 --- a/packages/venia-concept/src/components/Main/main.css +++ b/packages/venia-concept/src/components/Main/main.css @@ -5,18 +5,8 @@ z-index: 1; } -.page { - min-height: 100vh; -} - -/* state: masked */ - .root_masked { composes: root; height: 100vh; overflow: hidden; } - -.page_masked { - composes: page; -} diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css index efbf02246a..83e5cb07b9 100644 --- a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css @@ -1,23 +1,41 @@ .root { align-items: center; display: inline-flex; - justify-content: center; - width: 100%; + font-size: .9em; height: 0px; - background-color: rgb(var(--venia-warning-light)); + justify-content: flex-start; margin-bottom: .3em; + overflow: hidden; padding: 0 2.4rem; + position: sticky; + top: 0; + width: 100%; +} + +.offline { + composes: root; + background-color: rgb(var(--venia-warning-light)); animation-name: slidein; animation-duration: 244ms; animation-fill-mode: forwards; - overflow: hidden; - font-size: .9em; } -.root span { +.online { + composes: root; + background-color: rgb(var(--venia-teal-alt)); + animation-name: slideinandhide; + animation-duration: 3s; + animation-fill-mode: forwards; +} + +.offline span { color: rgb(var(--venia-warning-dark)); } +.online span { + color: rgb(var(--venia-teal)); +} + .root p { margin-left: 1em; line-height: 1.3em; @@ -32,3 +50,20 @@ } } +@keyframes slideinandhide { + 0% { + height: 3rem; + opacity: .5; + } + 10% { + opacity: 1; + } + 80% { + height: 3rem; + } + 100% { + height: 0; + } +} + + diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js index d899fdaa8e..13ee9095c7 100644 --- a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js @@ -16,12 +16,18 @@ class OnlineIndicator extends Component { render() { const { isOnline, classes } = this.props; - return (!isOnline - ?
+ return ((!isOnline) + ?

You are offline. Some features may be unavailable.

- : null) + :
+ +

You are online.

+
) } } From 0dc1b217bc654806e1a31778789793f2444e0ed7 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Thu, 8 Nov 2018 15:24:01 -0800 Subject: [PATCH 12/20] Clean up, comment --- .../src/Router/MagentoRouteHandler.js | 15 +++- .../__tests__/resolveUnknownRoute.test.js | 68 ++++++++----------- .../src/Router/resolveUnknownRoute.js | 28 +++++--- .../plugins/ServiceWorkerPlugin.js | 16 +++-- .../__tests__/ServiceWorkerPlugin.spec.js | 4 +- .../src/components/AppShell/appShell.js | 15 ++-- .../OnlineIndicator/onlineIndicator.css | 10 ++- .../OnlineIndicator/onlineIndicator.js | 13 ++-- packages/venia-concept/src/index.css | 1 + packages/venia-concept/src/index.js | 9 ++- packages/venia-concept/src/reducers/app.js | 8 +-- packages/venia-concept/src/sw.js | 42 +++++------- packages/venia-concept/templates/fallback.mst | 9 --- packages/venia-concept/venia-upward.yml | 32 --------- packages/venia-concept/webpack.config.js | 15 ++-- 15 files changed, 124 insertions(+), 161 deletions(-) delete mode 100644 packages/venia-concept/templates/fallback.mst diff --git a/packages/peregrine/src/Router/MagentoRouteHandler.js b/packages/peregrine/src/Router/MagentoRouteHandler.js index 6148116da5..6bf09a4ad7 100644 --- a/packages/peregrine/src/Router/MagentoRouteHandler.js +++ b/packages/peregrine/src/Router/MagentoRouteHandler.js @@ -27,6 +27,14 @@ export default class MagentoRouteHandler extends Component { } }; + // TODO: Add the ability to customize the cache name + async addToCache(urls) { + const myCache = await window.caches.open( + `workbox-runtime-${location.origin}/` + ); + await myCache.addAll(urls); + } + componentDidMount() { mountedInstances.add(this); this.getRouteComponent(); @@ -37,12 +45,13 @@ export default class MagentoRouteHandler extends Component { const { pathname } = props.location; const isKnown = state.componentMap.has(pathname); - // id of -1 is currently what defines a `NOTFOUND` component + // `NOTFOUND` component needs a unique id + // currently it is set to -1 const isNotFoundComponent = isKnown ? state.componentMap.get(pathname).id === -1 : false; - const shouldReloadRoute = (isNotFoundComponent && navigator.onLine); + const shouldReloadRoute = isNotFoundComponent && navigator.onLine; if (!isKnown || shouldReloadRoute) { this.getRouteComponent(); @@ -98,6 +107,8 @@ export default class MagentoRouteHandler extends Component { return; } + this.addToCache([pathname]); + this.setState(({ componentMap }) => ({ componentMap: new Map(componentMap).set(pathname, { RootComponent, diff --git a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js index 52a5a7d173..b6dbecf8ca 100644 --- a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js +++ b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js @@ -8,26 +8,26 @@ const urlResolverRes = type => }); const NotFoundManifest = { - NotFound: { - rootChunkID: 3, - rootModuleID: 100, - pageTypes: ['NOTFOUND'] - } - } + NotFound: { + rootChunkID: 3, + rootModuleID: 100, + pageTypes: ['NOTFOUND'] + } +}; const mockManifest = { - Category: { - rootChunkID: 2, - rootModuleID: 100, - pageTypes: ['CATEGORY'] - }, - Product: { - rootChunkID: 1, - rootModuleID: 99, - pageTypes: ['PRODUCT'] - }, - ...NotFoundManifest - } + Category: { + rootChunkID: 2, + rootModuleID: 100, + pageTypes: ['CATEGORY'] + }, + Product: { + rootChunkID: 1, + rootModuleID: 99, + pageTypes: ['PRODUCT'] + }, + ...NotFoundManifest +}; const cachedResponse = JSON.stringify({ 'foo-bar.html': { @@ -37,7 +37,7 @@ const cachedResponse = JSON.stringify({ const isOnline = _value => ({ get: () => _value, - set: (v) => _value = v + set: v => (_value = v) }); Object.defineProperty(navigator, 'onLine', isOnline(true)); @@ -54,9 +54,7 @@ function clearLocalStorage(item) { test('Happy path: resolves w/ rootChunkID and rootModuleID for first matching component found', async () => { fetch.mockResponseOnce(urlResolverRes('PRODUCT')); - fetch.mockResponseOnce( - JSON.stringify(mockManifest) - ); + fetch.mockResponseOnce(JSON.stringify(mockManifest)); const res = await resolveUnknownRoute({ route: 'foo-bar.html', @@ -71,23 +69,22 @@ test('Happy path: resolves w/ rootChunkID and rootModuleID for first matching co test('returns NOTFOUND when offline and requested content is not in cache ', async () => { navigator.onLine = false; - fetch.mockResponseOnce( - JSON.stringify(mockManifest) - ); + fetch.mockResponseOnce(JSON.stringify(mockManifest)); const res = await resolveUnknownRoute({ route: 'foo-bar.html', apiBase: 'https://store.com', __tmp_webpack_public_path__: 'https://dev-server.com/pub' }); - expect(res).toHaveProperty('rootChunkID', NotFoundManifest.NotFound.rootChunkID); + expect(res).toHaveProperty( + 'rootChunkID', + NotFoundManifest.NotFound.rootChunkID + ); }); test('stores response of urlResolver in cache', async () => { fetch.mockResponseOnce(urlResolverRes('PRODUCT')); - fetch.mockResponseOnce( - JSON.stringify(mockManifest) - ); + fetch.mockResponseOnce(JSON.stringify(mockManifest)); const url = 'foo-bar.html'; @@ -97,31 +94,25 @@ test('stores response of urlResolver in cache', async () => { __tmp_webpack_public_path__: 'https://dev-server.com/pub' }); - expect(localStorage.getItem('urlResolve')).not.toBeNull() + expect(localStorage.getItem('urlResolve')).not.toBeNull(); }); test('does not call fetchRoute when response is cached', async () => { - localStorage.setItem('urlResolve', cachedResponse); - fetch.mockResponseOnce( - JSON.stringify(mockManifest) - ); + fetch.mockResponseOnce(JSON.stringify(mockManifest)); await resolveUnknownRoute({ route: 'foo-bar.html', apiBase: 'https://store.com', __tmp_webpack_public_path__: 'https://dev-server.com/pub' }); - expect(fetch).toHaveBeenCalledTimes(1); }); test('calls fetchRoute when response is not cached', async () => { fetch.mockResponseOnce(urlResolverRes('PRODUCT')); - fetch.mockResponseOnce( - JSON.stringify(mockManifest) - ); + fetch.mockResponseOnce(JSON.stringify(mockManifest)); await resolveUnknownRoute({ route: 'foo-bar.html', apiBase: 'https://store.com', @@ -130,4 +121,3 @@ test('calls fetchRoute when response is not cached', async () => { expect(fetch).toHaveBeenCalledTimes(2); }); - diff --git a/packages/peregrine/src/Router/resolveUnknownRoute.js b/packages/peregrine/src/Router/resolveUnknownRoute.js index 5d959bfe7e..e9286e3133 100644 --- a/packages/peregrine/src/Router/resolveUnknownRoute.js +++ b/packages/peregrine/src/Router/resolveUnknownRoute.js @@ -57,12 +57,13 @@ export default function resolveUnknownRoute(opts) { * @returns {Promise<{type: "PRODUCT" | "CATEGORY" | "CMS_PAGE"}>} */ function remotelyResolveRoute(opts) { - let urlResolve = localStorage.getItem('urlResolve') + let urlResolve = localStorage.getItem('urlResolve'); urlResolve = JSON.parse(urlResolve); // If it exists in localStorage, use that value - // TODO: Use simplePersistence or figure out how to manually add to workbox - if (( urlResolve && urlResolve[opts.route] ) || !navigator.onLine) { + // TODO: This can be handled by workbox once this issue is resolved in the + // graphql repo: https://github.com/magento/graphql-ce/issues/229 + if ((urlResolve && urlResolve[opts.route]) || !navigator.onLine) { if (urlResolve && urlResolve[opts.route]) { return new Promise(function(resolve) { resolve(urlResolve[opts.route].data.urlResolver); @@ -70,9 +71,9 @@ function remotelyResolveRoute(opts) { } else { return new Promise(function(resolve) { resolve({ - type: "NOTFOUND", + type: 'NOTFOUND', id: -1 - }) + }); }); } } else { @@ -106,14 +107,19 @@ function fetchRoute(opts) { }) .then(res => res.json()) .then(res => { - let urlResolve = localStorage.getItem('urlResolve') - urlResolve = JSON.parse(urlResolve); - urlResolve = urlResolve ? urlResolve : {}; - urlResolve[opts.route] = res; - localStorage.setItem('urlResolve', JSON.stringify(urlResolve)); - return res.data.urlResolver + storeURLResolveResult(res, opts); + return res.data.urlResolver; }); +} +// TODO: This can be handled by workbox once this issue is resolved in the +// graphql repo: https://github.com/magento/graphql-ce/issues/229 +function storeURLResolveResult(res, opts) { + let urlResolve = localStorage.getItem('urlResolve'); + urlResolve = JSON.parse(urlResolve); + urlResolve = urlResolve ? urlResolve : {}; + urlResolve[opts.route] = res; + localStorage.setItem('urlResolve', JSON.stringify(urlResolve)); } /** diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js index f969667056..a2d527ea13 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js @@ -37,12 +37,14 @@ class ServiceWorkerPlugin { configureInjectManifest() { let injectManifest; if (this.config.swPath) { - injectManifest = new WorkboxPlugin.InjectManifest(this.config.swPath); + injectManifest = new WorkboxPlugin.InjectManifest( + this.config.swPath + ); } else { injectManifest = new WorkboxPlugin.InjectManifest({ swSrc: this.config.paths.src + '/sw.js', swDest: this.config.paths.dest + '/sw.js' - }) + }); } return injectManifest; } @@ -54,13 +56,19 @@ class ServiceWorkerPlugin { apply(compiler) { if (this.config.env.phase === 'development') { // add a WriteFilePlugin to write out the service worker to the filesystem so it can be served by M2, even though it's under dev - if (this.config.enableServiceWorkerDebugging && !this.config.injectManifest) { + if ( + this.config.enableServiceWorkerDebugging && + !this.config.injectManifest + ) { new WriteFileWebpackPlugin({ test: new RegExp(this.config.serviceWorkerFileName + '$'), log: true }).apply(compiler); this.applyGenerateSW(compiler); - } else if (this.config.enableServiceWorkerDebugging && this.config.injectManifest) { + } else if ( + this.config.enableServiceWorkerDebugging && + this.config.injectManifest + ) { this.applyInjectManifest(compiler); } else { // TODO: (feature) emit a structured { code, severity, resolution } object diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js index bed77a83b7..7a3f85a748 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js @@ -165,8 +165,8 @@ test('.apply uses `InjectManifest` when `injectManifest` is `true`', () => { expect(WorkboxPlugin.InjectManifest).toHaveBeenCalledWith( expect.objectContaining({ - swDest: "path/to/dest", - swSrc: "path/to/sw" + swDest: 'path/to/dest', + swSrc: 'path/to/sw' }) ); }); diff --git a/packages/venia-concept/src/components/AppShell/appShell.js b/packages/venia-concept/src/components/AppShell/appShell.js index 7940927ea8..1f12b5568e 100644 --- a/packages/venia-concept/src/components/AppShell/appShell.js +++ b/packages/venia-concept/src/components/AppShell/appShell.js @@ -1,5 +1,4 @@ import React, { Component } from 'react'; -import { findDOMNode } from 'react-dom'; import { bool, func, shape, string } from 'prop-types'; import { Page } from '@magento/peregrine'; import { connect } from 'react-redux'; @@ -36,22 +35,21 @@ class AppShell extends Component { const { isOnline } = this.props; const { hasBeenOffline } = this.state; - if ( !isOnline && !hasBeenOffline) { + // Only show online indicator when + // online after being offline + if (!isOnline && !hasBeenOffline) { this.setState({ hasBeenOffline: true }); } - return ( hasBeenOffline - ? - : null) + return hasBeenOffline ? : null; } render() { const { app, classes, closeDrawer } = this.props; const { onlineIndicator } = this; - const { drawer, overlay } = app + const { drawer, overlay } = app; const navIsOpen = drawer === 'nav'; const cartIsOpen = drawer === 'cart'; const className = overlay ? classes.root_masked : classes.root; @@ -80,5 +78,4 @@ const mapStateToProps = ({ app }) => { export default connect( mapStateToProps, null -) -(classify(defaultClasses)(AppShell)); +)(classify(defaultClasses)(AppShell)); diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css index 83e5cb07b9..cbbc0ba36e 100644 --- a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.css @@ -1,10 +1,10 @@ .root { align-items: center; display: inline-flex; - font-size: .9em; + font-size: 0.9em; height: 0px; justify-content: flex-start; - margin-bottom: .3em; + margin-bottom: 0.3em; overflow: hidden; padding: 0 2.4rem; position: sticky; @@ -22,7 +22,7 @@ .online { composes: root; - background-color: rgb(var(--venia-teal-alt)); + background-color: rgb(var(--venia-teal-light)); animation-name: slideinandhide; animation-duration: 3s; animation-fill-mode: forwards; @@ -53,7 +53,7 @@ @keyframes slideinandhide { 0% { height: 3rem; - opacity: .5; + opacity: 0.5; } 10% { opacity: 1; @@ -65,5 +65,3 @@ height: 0; } } - - diff --git a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js index 13ee9095c7..e0fe9506b4 100644 --- a/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js +++ b/packages/venia-concept/src/components/OnlineIndicator/onlineIndicator.js @@ -16,18 +16,17 @@ class OnlineIndicator extends Component { render() { const { isOnline, classes } = this.props; - return ((!isOnline) - ?
+ return !isOnline ? ( +

You are offline. Some features may be unavailable.

- :
+ ) : ( +

You are online.

-
) +
+ ); } } diff --git a/packages/venia-concept/src/index.css b/packages/venia-concept/src/index.css index 9630b0a60b..29e26a2795 100644 --- a/packages/venia-concept/src/index.css +++ b/packages/venia-concept/src/index.css @@ -8,6 +8,7 @@ html { --venia-grey: 246, 246, 246; --venia-teal: 0, 134, 138; --venia-teal-alt: 224, 240, 241; + --venia-teal-light: 212, 246, 241; --venia-text: 33, 33, 33; --venia-text-alt: 117, 117, 117; --venia-text-hint: 158, 158, 158; diff --git a/packages/venia-concept/src/index.js b/packages/venia-concept/src/index.js index 5dbf3ee66e..92e51c224f 100644 --- a/packages/venia-concept/src/index.js +++ b/packages/venia-concept/src/index.js @@ -16,7 +16,6 @@ import AppShell from 'src/components/AppShell'; import ensureDirURI from 'src/util/ensureDirUri'; import './index.css'; - // store.dispatch(getUserDetails()); const { BrowserPersistence } = Util; @@ -80,5 +79,9 @@ if (process.env.SERVICE_WORKER && 'serviceWorker' in navigator) { }); } -window.addEventListener('online', () => {store.dispatch(app.setOnline())}); -window.addEventListener('offline', () => {store.dispatch(app.setOffline())}); +window.addEventListener('online', () => { + store.dispatch(app.setOnline()); +}); +window.addEventListener('offline', () => { + store.dispatch(app.setOffline()); +}); diff --git a/packages/venia-concept/src/reducers/app.js b/packages/venia-concept/src/reducers/app.js index 9fdb599c4e..e3985abc9d 100644 --- a/packages/venia-concept/src/reducers/app.js +++ b/packages/venia-concept/src/reducers/app.js @@ -19,17 +19,17 @@ const reducerMap = { overlay: !!payload }; }, - [actions.setOnline]: (state) => { + [actions.setOnline]: state => { return { ...state, isOnline: true - } + }; }, - [actions.setOffline]: (state) => { + [actions.setOffline]: state => { return { ...state, isOnline: false - } + }; } }; diff --git a/packages/venia-concept/src/sw.js b/packages/venia-concept/src/sw.js index 44abbe7a7a..88340c0dec 100644 --- a/packages/venia-concept/src/sw.js +++ b/packages/venia-concept/src/sw.js @@ -8,27 +8,21 @@ function precacheManifest() { }) .then(function(json) { let toCache = []; - Object.keys(json).forEach((key) => { + Object.keys(json).forEach(key => { toCache.push(json[key].chunkName); }); toCache = toCache.concat([ - 'fallback.html', 'roots-manifest.json', 'favicon.ico', '/' ]); - workbox.precaching.precache(toCache) - + workbox.precaching.precache(toCache); }); } - precacheManifest(); -workbox.routing.registerRoute( - '/', - workbox.strategies.staleWhileRevalidate() -); +workbox.routing.registerRoute('/', workbox.strategies.staleWhileRevalidate()); workbox.routing.registerRoute( new RegExp('\\.html$'), @@ -36,7 +30,7 @@ workbox.routing.registerRoute( ); workbox.routing.registerRoute( - new RegExp('/\.\\.js$'), + new RegExp('/.\\.js$'), workbox.strategies.staleWhileRevalidate() ); @@ -46,16 +40,16 @@ workbox.routing.registerRoute( ); workbox.routing.registerRoute( - /\.(?:png|gif|jpg|jpeg|svg)$/, - workbox.strategies.cacheFirst({ - cacheName: 'images', - plugins: [ - new workbox.expiration.Plugin({ - maxEntries: 60, - maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days - }), - ], - }) + /\.(?:png|gif|jpg|jpeg|svg)$/, + workbox.strategies.cacheFirst({ + cacheName: 'images', + plugins: [ + new workbox.expiration.Plugin({ + maxEntries: 60, + maxAgeSeconds: 30 * 24 * 60 * 60 // 30 Days + }) + ] + }) ); workbox.precaching.precacheAndRoute(self.__precacheManifest || []); @@ -63,16 +57,12 @@ workbox.precaching.precacheAndRoute(self.__precacheManifest || []); // This "catch" handler is triggered when any of the other routes fail to // generate a response. -// TODO: Decide how this fits in with the NotFound root component. -workbox.routing.setCatchHandler(({event, request, url}) => { +// TODO: Add fallbacks +workbox.routing.setCatchHandler(({ event }) => { // Use event, request, and url to figure out how to respond. // One approach would be to use request.destination, see // https://medium.com/dev-channel/service-worker-caching-strategies-based-on-request-types-57411dd7652c switch (event.request.destination) { - case 'document': - return caches.match('/fallback.html'); - break; - case 'image': return caches.match(FALLBACK_IMAGE_URL); break; diff --git a/packages/venia-concept/templates/fallback.mst b/packages/venia-concept/templates/fallback.mst deleted file mode 100644 index 9767d74aad..0000000000 --- a/packages/venia-concept/templates/fallback.mst +++ /dev/null @@ -1,9 +0,0 @@ -{{> templates/doctype-and-head-start}} - Venia - Not Found - - -
-

Not found!

-
- - diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml index 901f2058bc..72345a9e6d 100644 --- a/packages/venia-concept/venia-upward.yml +++ b/packages/venia-concept/venia-upward.yml @@ -7,12 +7,6 @@ response: - matches: request.url.pathname pattern: '^/(graphql|rest|media/)' use: proxy - - matches: request.url.pathname - pattern: '^/fallback.html' - use: fallback - - matches: request.url.pathname - pattern: '^/sw.js' - use: serviceworker - matches: request.url.pathname pattern: '^/manifest.json' use: manifest @@ -139,32 +133,6 @@ manifest: file: inline: ./manifest.json -fallback: - inline: - status: 200 - headers: - inline: - content-type: - inline: 'text/html' - body: - engine: mustache - template: ./templates/fallback.mst - provide: - model: resource.model - -serviceworker: - inline: - status: 200 - headers: - inline: - service-worker-allowed: - inline: '/' - content-type: - inline: 'application/javascript' - body: - file: - inline: ./dist/sw.js - static: directory: inline: './dist' diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index ecf66e8ea9..83f90c35af 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -133,16 +133,17 @@ module.exports = async function(passedEnv) { swDest: 'sw.js' } }), - new CopyWebpackPlugin([{ - from: `${themePaths.media}/**/*`, - to: themePaths.output, - toType: 'dir' - }]) + new CopyWebpackPlugin([ + { + from: `${themePaths.media}/**/*`, + to: themePaths.output, + toType: 'dir' + } + ]) ] }; - config.devtool = 'eval-source-map'; + config.devtool = 'eval-source-map'; if (phase === 'development') { - const devServerConfig = { publicPath: config.output.publicPath, graphqlPlayground: { From 40c8d6a5ebd0d7eebaba97e500aeb05b14de94c9 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Thu, 8 Nov 2018 15:51:25 -0800 Subject: [PATCH 13/20] Revert upward change --- packages/upward-js/lib/middleware.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/upward-js/lib/middleware.js b/packages/upward-js/lib/middleware.js index 0b3435e4ee..fc958ada80 100644 --- a/packages/upward-js/lib/middleware.js +++ b/packages/upward-js/lib/middleware.js @@ -79,8 +79,7 @@ class UpwardMiddleware { debug('status, headers, and body valid. responding'); res.status(response.status) .set(response.headers) - // TODO: Issue #408 - .send(new Buffer(response.body, 'binary')); + .send(response.body); } }; } From 16effc8a8718e44f8911f09362555e04e8540fbf Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Fri, 9 Nov 2018 10:31:26 -0800 Subject: [PATCH 14/20] Update ServiceWorkerPlugin to accept config - ServiceWorkerPlugin takes in `injectManifestConfig` - Update tests - Cache chunks via workbox-webpack --- .../__tests__/resolveUnknownRoute.test.js | 6 +----- .../plugins/ServiceWorkerPlugin.js | 4 ++-- .../__tests__/ServiceWorkerPlugin.spec.js | 16 +++++++------- packages/venia-concept/src/sw.js | 21 ------------------- packages/venia-concept/webpack.config.js | 7 ++++--- 5 files changed, 14 insertions(+), 40 deletions(-) diff --git a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js index 11169be218..dff9c27a2e 100644 --- a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js +++ b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js @@ -67,7 +67,6 @@ test('Preload path: resolves directly from preload element', async () => { }); }); - test('returns NOTFOUND when offline and requested content is not in cache ', async () => { navigator.onLine = false; @@ -78,10 +77,7 @@ test('returns NOTFOUND when offline and requested content is not in cache ', asy __tmp_webpack_public_path__: 'https://dev-server.com/pub' }); - expect(res).toHaveProperty( - 'id', - NotFoundManifest.NotFound.rootChunkID - ); + expect(res).toHaveProperty('id', NotFoundManifest.NotFound.rootChunkID); }); test('stores response of urlResolver in cache', async () => { diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js index 5abe015da1..f5d9197ef2 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/ServiceWorkerPlugin.js @@ -36,9 +36,9 @@ class ServiceWorkerPlugin { configureInjectManifest() { let injectManifest; - if (this.config.swPath) { + if (this.config.injectManifestConfig) { injectManifest = new WorkboxPlugin.InjectManifest( - this.config.swPath + this.config.injectManifestConfig ); } else { injectManifest = new WorkboxPlugin.InjectManifest({ diff --git a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js index 9b374d699d..76473a47ad 100644 --- a/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js +++ b/packages/pwa-buildpack/src/WebpackTools/plugins/__tests__/ServiceWorkerPlugin.spec.js @@ -136,9 +136,13 @@ test('.apply generates and writes out a serviceworker when enableServiceWorkerDe }); test('.apply uses `InjectManifest` when `injectManifest` is `true`', () => { + const injectManifestConfig = { + swSrc: 'path/to/sw', + swDest: 'path/to/dest' + }; const plugin = new ServiceWorkerPlugin({ env: { - phase: 'development' + mode: 'development' }, enableServiceWorkerDebugging: true, serviceWorkerFileName: 'sw.js', @@ -146,10 +150,7 @@ test('.apply uses `InjectManifest` when `injectManifest` is `true`', () => { paths: { output: 'path/to/assets' }, - swPath: { - swSrc: 'path/to/sw', - swDest: 'path/to/dest' - } + injectManifestConfig }); const fakeCompiler = {}; @@ -161,9 +162,6 @@ test('.apply uses `InjectManifest` when `injectManifest` is `true`', () => { plugin.apply(fakeCompiler); expect(WorkboxPlugin.InjectManifest).toHaveBeenCalledWith( - expect.objectContaining({ - swDest: 'path/to/dest', - swSrc: 'path/to/sw' - }) + expect.objectContaining(injectManifestConfig) ); }); diff --git a/packages/venia-concept/src/sw.js b/packages/venia-concept/src/sw.js index 88340c0dec..1f65872cdd 100644 --- a/packages/venia-concept/src/sw.js +++ b/packages/venia-concept/src/sw.js @@ -1,27 +1,6 @@ workbox.skipWaiting(); workbox.clientsClaim(); -function precacheManifest() { - fetch('/roots-manifest.json') - .then(function(response) { - return response.json(); - }) - .then(function(json) { - let toCache = []; - Object.keys(json).forEach(key => { - toCache.push(json[key].chunkName); - }); - toCache = toCache.concat([ - 'roots-manifest.json', - 'favicon.ico', - '/' - ]); - workbox.precaching.precache(toCache); - }); -} - -precacheManifest(); - workbox.routing.registerRoute('/', workbox.strategies.staleWhileRevalidate()); workbox.routing.registerRoute( diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 67c723f055..9337c6810c 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -133,7 +133,8 @@ module.exports = async function() { serviceWorkerFileName, paths: themePaths, injectManifest: true, - swPath: { + injectManifestConfig: { + include: [/\.js$/], swSrc: './src/sw.js', swDest: 'sw.js' } @@ -151,8 +152,8 @@ module.exports = async function() { cacheGroups: { vendor: { test: new RegExp( - `[\\\/]node_modules[\\\/](${libs.join('|')})[\\\/]` - ), + `[\\\/]node_modules[\\\/](${libs.join('|')})[\\\/]` + ), name: 'vendor', filename: 'js/vendor.js', chunks: 'all' From 4d8b7787fc7a4fabd07179ece9ef45c5d98a142b Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Fri, 9 Nov 2018 13:08:50 -0800 Subject: [PATCH 15/20] Rename `media` directory to `images` Resolves `/media` proxying to the magento backend instead of to the `/dist` folder to serve static content --- package-lock.json | 6 +++--- .../venia-concept/{media => images}/favicon.ico | Bin .../{media => images}/icons/venia_circle_144.png | Bin .../{media => images}/icons/venia_circle_192.png | Bin .../{media => images}/icons/venia_circle_512.png | Bin .../{media => images}/icons/venia_square_144.png | Bin .../{media => images}/icons/venia_square_192.png | Bin .../{media => images}/icons/venia_square_512.png | Bin packages/venia-concept/manifest.json | 6 +++--- packages/venia-concept/venia-upward.yml | 2 +- packages/venia-concept/webpack.config.js | 4 ++-- 11 files changed, 9 insertions(+), 9 deletions(-) rename packages/venia-concept/{media => images}/favicon.ico (100%) rename packages/venia-concept/{media => images}/icons/venia_circle_144.png (100%) rename packages/venia-concept/{media => images}/icons/venia_circle_192.png (100%) rename packages/venia-concept/{media => images}/icons/venia_circle_512.png (100%) rename packages/venia-concept/{media => images}/icons/venia_square_144.png (100%) rename packages/venia-concept/{media => images}/icons/venia_square_192.png (100%) rename packages/venia-concept/{media => images}/icons/venia_square_512.png (100%) diff --git a/package-lock.json b/package-lock.json index d0ddaa2cc0..80ba52000a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2963,7 +2963,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -6655,7 +6655,7 @@ }, "d": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { @@ -14215,7 +14215,7 @@ }, "next-tick": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, diff --git a/packages/venia-concept/media/favicon.ico b/packages/venia-concept/images/favicon.ico similarity index 100% rename from packages/venia-concept/media/favicon.ico rename to packages/venia-concept/images/favicon.ico diff --git a/packages/venia-concept/media/icons/venia_circle_144.png b/packages/venia-concept/images/icons/venia_circle_144.png similarity index 100% rename from packages/venia-concept/media/icons/venia_circle_144.png rename to packages/venia-concept/images/icons/venia_circle_144.png diff --git a/packages/venia-concept/media/icons/venia_circle_192.png b/packages/venia-concept/images/icons/venia_circle_192.png similarity index 100% rename from packages/venia-concept/media/icons/venia_circle_192.png rename to packages/venia-concept/images/icons/venia_circle_192.png diff --git a/packages/venia-concept/media/icons/venia_circle_512.png b/packages/venia-concept/images/icons/venia_circle_512.png similarity index 100% rename from packages/venia-concept/media/icons/venia_circle_512.png rename to packages/venia-concept/images/icons/venia_circle_512.png diff --git a/packages/venia-concept/media/icons/venia_square_144.png b/packages/venia-concept/images/icons/venia_square_144.png similarity index 100% rename from packages/venia-concept/media/icons/venia_square_144.png rename to packages/venia-concept/images/icons/venia_square_144.png diff --git a/packages/venia-concept/media/icons/venia_square_192.png b/packages/venia-concept/images/icons/venia_square_192.png similarity index 100% rename from packages/venia-concept/media/icons/venia_square_192.png rename to packages/venia-concept/images/icons/venia_square_192.png diff --git a/packages/venia-concept/media/icons/venia_square_512.png b/packages/venia-concept/images/icons/venia_square_512.png similarity index 100% rename from packages/venia-concept/media/icons/venia_square_512.png rename to packages/venia-concept/images/icons/venia_square_512.png diff --git a/packages/venia-concept/manifest.json b/packages/venia-concept/manifest.json index e0c9793806..1ba5ed39c5 100644 --- a/packages/venia-concept/manifest.json +++ b/packages/venia-concept/manifest.json @@ -7,15 +7,15 @@ "background_color": "#fff", "description": "Shop the look", "icons": [{ - "src": "/media/icons/venia_circle_144.png", + "src": "/images/icons/venia_circle_144.png", "sizes": "144x144", "type": "image/png" },{ - "src": "/media/icons/venia_circle_192.png", + "src": "/images/icons/venia_circle_192.png", "sizes": "192x192", "type": "image/png" },{ - "src": "/media/icons/venia_circle_512.png", + "src": "/images/icons/venia_circle_512.png", "sizes": "512x512", "type": "image/png" } diff --git a/packages/venia-concept/venia-upward.yml b/packages/venia-concept/venia-upward.yml index 72345a9e6d..caf78cc171 100644 --- a/packages/venia-concept/venia-upward.yml +++ b/packages/venia-concept/venia-upward.yml @@ -119,7 +119,7 @@ favicon: inline: image/vnd.microsoft.icon body: file: - inline: ./media/favicon.ico + inline: ./dist/images/favicon.ico encoding: binary manifest: diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 9337c6810c..d7bbcebc4c 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -17,7 +17,7 @@ const TerserPlugin = require('terser-webpack-plugin'); const configureBabel = require('./babel.config.js'); const themePaths = { - media: path.resolve(__dirname, 'media'), + images: path.resolve(__dirname, 'images'), templates: path.resolve(__dirname, 'templates'), src: path.resolve(__dirname, 'src'), output: path.resolve(__dirname, 'dist') @@ -141,7 +141,7 @@ module.exports = async function() { }), new CopyWebpackPlugin([ { - from: `${themePaths.media}/**/*`, + from: `${themePaths.images}/**/*`, to: themePaths.output, toType: 'dir' } From 8fb2ca8b21d958caa21f926523e66cdbcf46ad54 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Mon, 12 Nov 2018 07:13:44 -0800 Subject: [PATCH 16/20] Add theme color to head --- packages/venia-concept/templates/doctype-and-head-start.mst | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/venia-concept/templates/doctype-and-head-start.mst b/packages/venia-concept/templates/doctype-and-head-start.mst index 7958815ab2..e94d6d7d47 100644 --- a/packages/venia-concept/templates/doctype-and-head-start.mst +++ b/packages/venia-concept/templates/doctype-and-head-start.mst @@ -4,4 +4,5 @@ + From 233aa70ae0300c9439d0f2e1b6caad86e0560de3 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Mon, 12 Nov 2018 07:18:16 -0800 Subject: [PATCH 17/20] Remove fallback code. Opening seprate issue. --- packages/venia-concept/src/sw.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/venia-concept/src/sw.js b/packages/venia-concept/src/sw.js index 1f65872cdd..d5a6e652d6 100644 --- a/packages/venia-concept/src/sw.js +++ b/packages/venia-concept/src/sw.js @@ -37,21 +37,3 @@ workbox.precaching.precacheAndRoute(self.__precacheManifest || []); // generate a response. // TODO: Add fallbacks -workbox.routing.setCatchHandler(({ event }) => { - // Use event, request, and url to figure out how to respond. - // One approach would be to use request.destination, see - // https://medium.com/dev-channel/service-worker-caching-strategies-based-on-request-types-57411dd7652c - switch (event.request.destination) { - case 'image': - return caches.match(FALLBACK_IMAGE_URL); - break; - - case 'font': - return caches.match(FALLBACK_FONT_URL); - break; - - default: - // If we don't have a fallback, just return an error response. - return Response.error(); - } -}); From d5926466e7d22062b900a030f43c9c6650bb4496 Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Mon, 12 Nov 2018 07:30:36 -0800 Subject: [PATCH 18/20] Switch test url from store.com to example.com --- .../peregrine/src/Router/__tests__/resolveUnknownRoute.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js index dff9c27a2e..2a921e9918 100644 --- a/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js +++ b/packages/peregrine/src/Router/__tests__/resolveUnknownRoute.test.js @@ -59,7 +59,7 @@ test('Preload path: resolves directly from preload element', async () => { ''; const res = await resolveUnknownRoute({ route: 'foo-bar.html', - apiBase: 'https://store.com' + apiBase: 'https://example.com' }); expect(res).toMatchObject({ type: 'PRODUCT', From 048e98cc8a5ade590fd08157906f1e1e948aa0bf Mon Sep 17 00:00:00 2001 From: "pcvonz@gmail.com" Date: Wed, 28 Nov 2018 12:56:41 -0800 Subject: [PATCH 19/20] Address PR feedback --- .../src/Router/resolveUnknownRoute.js | 22 +++++++--------- .../venia-concept/src/components/App/app.js | 26 +++---------------- .../src/components/Main/main.css | 4 +++ packages/venia-concept/src/reducers/app.js | 6 +++-- packages/venia-concept/src/sw.js | 17 +++++++++++- 5 files changed, 36 insertions(+), 39 deletions(-) diff --git a/packages/peregrine/src/Router/resolveUnknownRoute.js b/packages/peregrine/src/Router/resolveUnknownRoute.js index f9f2000c4e..80254cc423 100644 --- a/packages/peregrine/src/Router/resolveUnknownRoute.js +++ b/packages/peregrine/src/Router/resolveUnknownRoute.js @@ -55,15 +55,11 @@ function remotelyResolveRoute(opts) { // graphql repo: https://github.com/magento/graphql-ce/issues/229 if ((urlResolve && urlResolve[opts.route]) || !navigator.onLine) { if (urlResolve && urlResolve[opts.route]) { - return new Promise(function(resolve) { - resolve(urlResolve[opts.route].data.urlResolver); - }); + return Promise.resolve(urlResolve[opts.route].data.urlResolver); } else { - return new Promise(function(resolve) { - resolve({ - type: 'NOTFOUND', - id: -1 - }); + return Promise.resolve({ + type: 'NOTFOUND', + id: -1 }); } } else { @@ -105,9 +101,9 @@ function fetchRoute(opts) { // TODO: This can be handled by workbox once this issue is resolved in the // graphql repo: https://github.com/magento/graphql-ce/issues/229 function storeURLResolveResult(res, opts) { - let urlResolve = localStorage.getItem('urlResolve'); - urlResolve = JSON.parse(urlResolve); - urlResolve = urlResolve ? urlResolve : {}; - urlResolve[opts.route] = res; - localStorage.setItem('urlResolve', JSON.stringify(urlResolve)); + const storedRoute = localStorage.getItem('urlResolve'); + const item = JSON.parse(storedRoute) || {}; + + item[opts.route] = res; + localStorage.setItem('urlResolve', JSON.stringify(item)); } diff --git a/packages/venia-concept/src/components/App/app.js b/packages/venia-concept/src/components/App/app.js index df80d9ac01..9ae74b1d6e 100644 --- a/packages/venia-concept/src/components/App/app.js +++ b/packages/venia-concept/src/components/App/app.js @@ -27,22 +27,12 @@ class App extends Component { closeDrawer: func.isRequired }; - state = { - hasBeenOffline: false - }; - get onlineIndicator() { - const { isOnline } = this.props; - const { hasBeenOffline } = this.state; + const { app } = this.props; + const { hasBeenOffline, isOnline } = app; // Only show online indicator when // online after being offline - if (!isOnline && !hasBeenOffline) { - this.setState({ - hasBeenOffline: true - }); - } - return hasBeenOffline ? : null; } @@ -68,14 +58,4 @@ class App extends Component { } } -const mapStateToProps = ({ app }) => { - const { isOnline } = app; - return { - isOnline - }; -}; - -export default connect( - mapStateToProps, - null -)(classify(defaultClasses)(App)); +export default classify(defaultClasses)(App); diff --git a/packages/venia-concept/src/components/Main/main.css b/packages/venia-concept/src/components/Main/main.css index 30e23eb485..0305367e53 100644 --- a/packages/venia-concept/src/components/Main/main.css +++ b/packages/venia-concept/src/components/Main/main.css @@ -10,3 +10,7 @@ height: 100vh; overflow: hidden; } + +.page { + min-height: 100vh; +} diff --git a/packages/venia-concept/src/reducers/app.js b/packages/venia-concept/src/reducers/app.js index e3985abc9d..32cacbd4f1 100644 --- a/packages/venia-concept/src/reducers/app.js +++ b/packages/venia-concept/src/reducers/app.js @@ -8,7 +8,8 @@ const initialState = { drawer: null, overlay: false, pending: {}, - isOnline: navigator.onLine + isOnline: navigator.onLine, + hasBeenOffline: false || !navigator.onLine }; const reducerMap = { @@ -28,7 +29,8 @@ const reducerMap = { [actions.setOffline]: state => { return { ...state, - isOnline: false + isOnline: false, + hasBeenOffline: true }; } }; diff --git a/packages/venia-concept/src/sw.js b/packages/venia-concept/src/sw.js index a8003986aa..5640b06b97 100644 --- a/packages/venia-concept/src/sw.js +++ b/packages/venia-concept/src/sw.js @@ -1,3 +1,4 @@ +const thirtyDays = 30 * 24 * 60 * 60 workbox.skipWaiting(); workbox.clientsClaim(); @@ -13,6 +14,20 @@ workbox.routing.registerRoute( workbox.strategies.staleWhileRevalidate() ); +workbox.routing.registerRoute( + /\/media\/catalog.*\.(?:png|gif|jpg|jpeg|svg)$/, + workbox.strategies.staleWhileRevalidate({ + cacheName: 'catalog', + plugins: [ + new workbox.expiration.Plugin({ + maxEntries: 60, + maxAgeSeconds: thirtyDays // 30 Days + }) + ] + }) +); + + workbox.routing.registerRoute( /\.(?:png|gif|jpg|jpeg|svg)$/, workbox.strategies.cacheFirst({ @@ -20,7 +35,7 @@ workbox.routing.registerRoute( plugins: [ new workbox.expiration.Plugin({ maxEntries: 60, - maxAgeSeconds: 30 * 24 * 60 * 60 // 30 Days + maxAgeSeconds: thirtyDays // 30 Days }) ] }) From e99a80a361fd75b9fec3916c18eb85d5c3cdce46 Mon Sep 17 00:00:00 2001 From: Jimmy Sanford Date: Wed, 28 Nov 2018 14:43:54 -0700 Subject: [PATCH 20/20] Fix small issues and lint --- packages/peregrine/src/Router/resolveUnknownRoute.js | 8 ++++---- packages/venia-concept/src/components/App/app.js | 3 +-- packages/venia-concept/src/components/Main/main.css | 4 ++++ packages/venia-concept/src/reducers/app.js | 6 +++--- packages/venia-concept/src/sw.js | 3 +-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/peregrine/src/Router/resolveUnknownRoute.js b/packages/peregrine/src/Router/resolveUnknownRoute.js index 80254cc423..1c4e46d1e9 100644 --- a/packages/peregrine/src/Router/resolveUnknownRoute.js +++ b/packages/peregrine/src/Router/resolveUnknownRoute.js @@ -101,9 +101,9 @@ function fetchRoute(opts) { // TODO: This can be handled by workbox once this issue is resolved in the // graphql repo: https://github.com/magento/graphql-ce/issues/229 function storeURLResolveResult(res, opts) { - const storedRoute = localStorage.getItem('urlResolve'); - const item = JSON.parse(storedRoute) || {}; + const storedRoute = localStorage.getItem('urlResolve'); + const item = JSON.parse(storedRoute) || {}; - item[opts.route] = res; - localStorage.setItem('urlResolve', JSON.stringify(item)); + item[opts.route] = res; + localStorage.setItem('urlResolve', JSON.stringify(item)); } diff --git a/packages/venia-concept/src/components/App/app.js b/packages/venia-concept/src/components/App/app.js index 9ae74b1d6e..b4298a4b7c 100644 --- a/packages/venia-concept/src/components/App/app.js +++ b/packages/venia-concept/src/components/App/app.js @@ -1,7 +1,6 @@ import React, { Component } from 'react'; import { bool, func, shape, string } from 'prop-types'; import { Page } from '@magento/peregrine'; -import { connect } from 'react-redux'; import classify from 'src/classify'; import ErrorView from 'src/components/ErrorView'; @@ -9,8 +8,8 @@ import Main from 'src/components/Main'; import Mask from 'src/components/Mask'; import MiniCart from 'src/components/MiniCart'; import Navigation from 'src/components/Navigation'; -import defaultClasses from './app.css'; import OnlineIndicator from 'src/components/OnlineIndicator'; +import defaultClasses from './app.css'; const renderRoutingError = props => ; diff --git a/packages/venia-concept/src/components/Main/main.css b/packages/venia-concept/src/components/Main/main.css index 0305367e53..3b91ade884 100644 --- a/packages/venia-concept/src/components/Main/main.css +++ b/packages/venia-concept/src/components/Main/main.css @@ -14,3 +14,7 @@ .page { min-height: 100vh; } + +.page_masked { + composes: page; +} diff --git a/packages/venia-concept/src/reducers/app.js b/packages/venia-concept/src/reducers/app.js index 32cacbd4f1..55da7c89d6 100644 --- a/packages/venia-concept/src/reducers/app.js +++ b/packages/venia-concept/src/reducers/app.js @@ -6,10 +6,10 @@ export const name = 'app'; const initialState = { drawer: null, - overlay: false, - pending: {}, + hasBeenOffline: !navigator.onLine, isOnline: navigator.onLine, - hasBeenOffline: false || !navigator.onLine + overlay: false, + pending: {} }; const reducerMap = { diff --git a/packages/venia-concept/src/sw.js b/packages/venia-concept/src/sw.js index 5640b06b97..8d26e1a92a 100644 --- a/packages/venia-concept/src/sw.js +++ b/packages/venia-concept/src/sw.js @@ -1,4 +1,4 @@ -const thirtyDays = 30 * 24 * 60 * 60 +const thirtyDays = 30 * 24 * 60 * 60; workbox.skipWaiting(); workbox.clientsClaim(); @@ -27,7 +27,6 @@ workbox.routing.registerRoute( }) ); - workbox.routing.registerRoute( /\.(?:png|gif|jpg|jpeg|svg)$/, workbox.strategies.cacheFirst({