From 4fe32050fc49884b9dc8349fe4481224900dd07d Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Fri, 16 Sep 2016 10:16:50 +0200 Subject: [PATCH 01/29] chore fix gulp/webpack watcher --- gulp/tasks/build.js | 42 +++++++++---- gulp/tasks/webpack.js | 13 ---- gulpfile.babel.js | 3 +- webpack/replace/JsonpMainTemplate.runtime.js | 63 -------------------- webpack/replace/log-apply-result.js | 32 ---------- 5 files changed, 33 insertions(+), 120 deletions(-) delete mode 100644 gulp/tasks/webpack.js delete mode 100644 webpack/replace/JsonpMainTemplate.runtime.js delete mode 100644 webpack/replace/log-apply-result.js diff --git a/gulp/tasks/build.js b/gulp/tasks/build.js index 0c5c2647c..0ed73ae3c 100644 --- a/gulp/tasks/build.js +++ b/gulp/tasks/build.js @@ -1,28 +1,48 @@ import gulp from 'gulp'; import gutil from 'gulp-util'; import webpack from 'webpack'; +import path from 'path'; import devConfig from '../../webpack/dev.config'; import stagingConfig from '../../webpack/staging.config'; import extConfig from '../../webpack/production.config'; -const build = (config, callback) => { - let myConfig = Object.create(config); - webpack(myConfig, (err, stats) => { - if (err) { - throw new gutil.PluginError('webpack:build', err); - } - gutil.log('[webpack:build]', stats.toString({ colors: true })); +function compiler(config) { + return webpack(Object.create(config)); +} + +function staticCompiler(config) { + if (!staticCompiler.instance) { + staticCompiler.instance = compiler(config); + } + return staticCompiler.instance; +} + +function build(compiler, callback) { + compiler.run((err, stats) => { + if (err) throw new gutil.PluginError('webpack:build', err); + gutil.log('[webpack:build]', stats.toString({ + chunks: false, + colors: true, + })); callback(); }); -}; +} +gulp.task('webpack:watch', () => { + const globs = [ + path.join(__dirname, '../../src/') + '**/*{js, scss}', + path.join(__dirname, '../../test/') + '**/*{js}', + ]; + return gulp.watch(globs, ['webpack:build:dev']); +}); gulp.task('webpack:build:dev', (callback) => { - build(devConfig, callback); + // Instance webpack compiler once over multiple times (watch) + build(staticCompiler(devConfig), callback); }); gulp.task('webpack:build:staging', (callback) => { - build(stagingConfig, callback); + build(compiler(stagingConfig), callback); }); gulp.task('webpack:build:production', (callback) => { - build(extConfig, callback); + build(compiler(extConfig), callback); }); \ No newline at end of file diff --git a/gulp/tasks/webpack.js b/gulp/tasks/webpack.js deleted file mode 100644 index f943f5273..000000000 --- a/gulp/tasks/webpack.js +++ /dev/null @@ -1,13 +0,0 @@ -import gulp from 'gulp'; -import fs from 'fs'; - -gulp.task('replace-webpack-code', () => { - const replaceTasks = [{ - from: './webpack/replace/JsonpMainTemplate.runtime.js', - to: './node_modules/webpack/lib/JsonpMainTemplate.runtime.js' - }, { - from: './webpack/replace/log-apply-result.js', - to: './node_modules/webpack/hot/log-apply-result.js' - }]; - replaceTasks.forEach(task => fs.writeFileSync(task.to, fs.readFileSync(task.from))); -}); diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 2aca0f6a9..345f5700a 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -4,7 +4,8 @@ requireDir('./gulp/tasks'); gulp.task('default',['build:dev']); gulp.task('build:dev', - ['webpack:build:dev', 'views:build:dev', 'copy:build:dev', 'copy:watch']); + ['webpack:build:dev', 'views:build:dev', 'copy:build:dev', + 'copy:watch', 'webpack:watch']); gulp.task('build:staging', ['webpack:build:staging', 'views:build:staging', 'copy:build:staging']); gulp.task('build:production', diff --git a/webpack/replace/JsonpMainTemplate.runtime.js b/webpack/replace/JsonpMainTemplate.runtime.js deleted file mode 100644 index 492be0858..000000000 --- a/webpack/replace/JsonpMainTemplate.runtime.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -/*globals hotAddUpdateChunk parentHotUpdateCallback document XMLHttpRequest $require$ $hotChunkFilename$ $hotMainFilename$ */ -module.exports = function() { - function webpackHotUpdateCallback(chunkId, moreModules) { // eslint-disable-line no-unused-vars - hotAddUpdateChunk(chunkId, moreModules); - if(parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules); - } - - var context = this; - function evalCode(code, context) { - return (function() { return eval(code); }).call(context); - } - - context.hotDownloadUpdateChunk = function (chunkId) { // eslint-disable-line no-unused-vars - var src = __webpack_require__.p + "" + chunkId + "." + hotCurrentHash + ".hot-update.js"; - var request = new XMLHttpRequest(); - - request.onload = function() { - evalCode(this.responseText, context); - }; - request.open("get", src, true); - request.send(); - } - - function hotDownloadManifest(callback) { // eslint-disable-line no-unused-vars - if(typeof XMLHttpRequest === "undefined") - return callback(new Error("No browser support")); - try { - var request = new XMLHttpRequest(); - var requestPath = $require$.p + $hotMainFilename$; - request.open("GET", requestPath, true); - request.timeout = 10000; - request.send(null); - } catch(err) { - return callback(err); - } - request.onreadystatechange = function() { - if(request.readyState !== 4) return; - if(request.status === 0) { - // timeout - callback(new Error("Manifest request to " + requestPath + " timed out.")); - } else if(request.status === 404) { - // no update available - callback(); - } else if(request.status !== 200 && request.status !== 304) { - // other failure - callback(new Error("Manifest request to " + requestPath + " failed.")); - } else { - // success - try { - var update = JSON.parse(request.responseText); - } catch(e) { - callback(e); - return; - } - callback(null, update); - } - }; - } -}; diff --git a/webpack/replace/log-apply-result.js b/webpack/replace/log-apply-result.js deleted file mode 100644 index 83aaaef4a..000000000 --- a/webpack/replace/log-apply-result.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -module.exports = function(updatedModules, renewedModules) { - var unacceptedModules = updatedModules.filter(function(moduleId) { - return renewedModules && renewedModules.indexOf(moduleId) < 0; - }); - - if(unacceptedModules.length > 0) { - console.warn("[HMR] The following modules couldn't be hot updated: (They would need a full reload!)"); - unacceptedModules.forEach(function(moduleId) { - console.warn("[HMR] - " + moduleId); - }); - - if(chrome && chrome.runtime && chrome.runtime.reload) { - console.warn("[HMR] extension reload"); - chrome.runtime.reload(); - } else { - console.warn("[HMR] Can't extension reload. not found chrome.runtime.reload."); - } - } - - if(!renewedModules || renewedModules.length === 0) { - console.log("[HMR] Nothing hot updated."); - } else { - console.log("[HMR] Updated modules:"); - renewedModules.forEach(function(moduleId) { - console.log("[HMR] - " + moduleId); - }); - } -}; From 1efb391596112b6e057d29f2bcb2a90e13ad6964 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Wed, 14 Sep 2016 17:18:09 +0200 Subject: [PATCH 02/29] catch up legacy extension iframe styles: fix #45 --- src/browser/extension/content/index.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index e003993b8..d05d5ef72 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -2,10 +2,6 @@ import { Record, Set as ImmutableSet } from 'immutable'; import React from 'react'; import { render } from 'react-dom'; import Root from '../../../app/containers/Root'; -import configureStore from '../../../app/store/configureStore'; - -import Alternative from '../../../app/components/Alternatives'; -import { STYLES_URL, IMAGES_URL } from '../../../app/constants/assetsUrls'; import { createStore } from 'redux'; @@ -15,11 +11,6 @@ import alternativeFound from '../../../app/content/actions/alternatives'; import updateDeactivatedWebsites from '../../../app/content/actions/preferences'; import portCommunication from '../../../app/content/portCommunication'; -import { - REDUCE_ALTERNATIVE_IFRAME, - EXTEND_ALTERNATIVE_IFRAME -} from '../../../app/constants/ActionTypes.js'; - const IFRAME_EXTENDED_HEIGHT = '255px'; const IFRAME_REDUCED_HEIGHT = '60px'; @@ -54,10 +45,14 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { iframe.width = '100%'; iframe.height = reduced ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; iframe.style.position = 'fixed'; - iframe.style.bottom = '0px'; - iframe.style.left = '0px'; - iframe.style.right = '0px'; - iframe.style.zIndex = '999999999'; + iframe.style.bottom = 0; + iframe.style.left = 0; + iframe.style.right = 0; + iframe.style.zIndex = 2147483647; // Max z-index value (signed 32bits integer) + iframe.style.background = '#FDF6E3'; // UI background color (avoid having a transparent iframe after injection) + iframe.style.border = 'none'; + iframe.style.transition = 'height .1s'; + iframe.style.boxShadow = '0 0 15px #888'; iframe.srcdoc = ` From 11e47bfb5a694263bc0ead11d444a9081f540e70 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Wed, 14 Sep 2016 18:35:32 +0200 Subject: [PATCH 03/29] fix git list of file to lint at pre-commit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6a9604107..7c9e64c6e 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build:dev": "gulp build:dev", "clean": "rm -rf build", "test": "LMEM_BACKEND_ORIGIN='' NODE_ENV=test mocha --compilers js:babel-core/register test/app --recursive", - "lint": "git ls-files -om --exclude-standard | grep \\.js$ | xargs eslint --fix", + "lint": "git diff-index --name-only --cached HEAD | grep \\.js$ | xargs eslint --fix", "deploy:production": "gulp deploy:production", "deploy:staging": "gulp deploy:staging" }, From 4afa3cc33870eeb25bcba2e0a24cfd3402b94e60 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Wed, 14 Sep 2016 19:51:11 +0200 Subject: [PATCH 04/29] fix topbar styles: ref #32 --- src/app/styles/reco-header.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/styles/reco-header.scss b/src/app/styles/reco-header.scss index 35fe154d2..f24bd0cc3 100644 --- a/src/app/styles/reco-header.scss +++ b/src/app/styles/reco-header.scss @@ -11,6 +11,8 @@ justify-content: flex-start; padding: $margin-size; + max-height: $block-size; + line-height: $double-line-height; > .logo{ position: relative; // so that the tooltip with position: absolute works From 7f807f8add1ee8f9c290a34833dd47c19311e889 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Thu, 15 Sep 2016 12:39:36 +0200 Subject: [PATCH 05/29] fix reduce button styles --- src/app/styles/reco-header.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/styles/reco-header.scss b/src/app/styles/reco-header.scss index f24bd0cc3..50d277939 100644 --- a/src/app/styles/reco-header.scss +++ b/src/app/styles/reco-header.scss @@ -48,14 +48,14 @@ // Win some space hiding secondary UI elements. - @media(max-width: #{ $media-query-threshold-small-edge - rem-size(1) }) { + @media(max-width: #{$media-query-threshold-small-edge - rem-size(1)}) { z-index: 20; .button-label { @include visually-hidden; } .button-compact.with-image img { - margin-left: .5ex; + margin-left: initial; } } From 80741471eb11ba8eab99fac607bf3857bacc3053 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Thu, 15 Sep 2016 15:50:35 +0200 Subject: [PATCH 06/29] fix prefs styles and topbar plus lint --- src/app/components/AlternativeHeader.js | 104 +++++++++++++----------- src/app/components/PreferenceScreen.js | 28 +++---- src/app/constants/ui.js | 10 ++- src/app/styles/_variables.scss | 3 +- src/app/styles/main.scss | 32 ++++++-- src/app/styles/reco-header.scss | 7 +- src/app/styles/reco-main.scss | 8 +- src/app/styles/top-level.scss | 2 + 8 files changed, 111 insertions(+), 83 deletions(-) diff --git a/src/app/components/AlternativeHeader.js b/src/app/components/AlternativeHeader.js index 43b1b6298..c2a46abcf 100644 --- a/src/app/components/AlternativeHeader.js +++ b/src/app/components/AlternativeHeader.js @@ -41,11 +41,12 @@ class AlternativeHeader extends Component {
  • -
  • -
) : [(
  • -
  • ), (
  • -
  • ), + (
  • +
  • )]; @@ -135,14 +159,13 @@ class AlternativeHeader extends Component { const extendReduceButton = preferenceScreenPanel ? undefined : (
    - - - ); } @@ -208,13 +216,13 @@ class AlternativeHeader extends Component { componentWillUnmount() { this.refs.deactivateMenu.ownerDocument - .removeEventListener('click', this._closeMenuDocumentClickHandler); + .removeEventListener('click', this.closeMenuDocumentClickHandler); } watchForMenuExit() { const menuElement = this.refs.deactivateMenu; - this._closeMenuDocumentClickHandler = event => { + this.closeMenuDocumentClickHandler = event => { if (!this.state.deactivateMenuOpen) return; if (!event.target.matches('.menu-deactivate, .menu-deactivate *')) { @@ -222,7 +230,7 @@ class AlternativeHeader extends Component { } }; - menuElement.ownerDocument.addEventListener('click', this._closeMenuDocumentClickHandler); + menuElement.ownerDocument.addEventListener('click', this.closeMenuDocumentClickHandler); } diff --git a/src/app/components/PreferenceScreen.js b/src/app/components/PreferenceScreen.js index 6d51dfeaf..97c5ad8af 100644 --- a/src/app/components/PreferenceScreen.js +++ b/src/app/components/PreferenceScreen.js @@ -8,7 +8,7 @@ import { import PreferenceAboutPanel from './PreferenceAboutPanel'; import PreferenceDeactivatedPanel from './PreferenceDeactivatedPanel'; -export default function(props) { +export default function (props) { const { preferenceScreenPanel, deactivatedWebsites, onReactivateWebsite, openPrefScreen @@ -18,16 +18,16 @@ export default function(props) { switch (preferenceScreenPanel){ case PREFERENCE_SCREEN_PANEL_ABOUT: - mainContent = ; + mainContent = ; break; case PREFERENCE_SCREEN_PANEL_DEACTIVATED_WEBSITES: - mainContent = + />); break; default: - console.error('Unknown content value', content); + console.error('Unknown content value', preferenceScreenPanel); } const changePanel = e => { @@ -36,15 +36,15 @@ export default function(props) { }; return (
    - -
    - {mainContent} -
    + +
    + {mainContent} +
    ); } diff --git a/src/app/constants/ui.js b/src/app/constants/ui.js index 63d428562..986270b40 100644 --- a/src/app/constants/ui.js +++ b/src/app/constants/ui.js @@ -5,22 +5,24 @@ export const PREFERENCE_SCREEN_PANEL_DEACTIVATED_WEBSITES = 'PREFERENCE_SCREEN_P export const HEADER_CONTENT = { [PREFERENCE_SCREEN_PANEL_ABOUT]: imagesUrl => ( - + - Préférences de l'extension - A propos + Préférences de l’extension + À propos ), [PREFERENCE_SCREEN_PANEL_DEACTIVATED_WEBSITES]: imagesUrl => ( - + - Préférences de l'extension - Sites désactivés + Préférences de l’extension + Sites désactivés ), diff --git a/src/app/styles/_variables.scss b/src/app/styles/_variables.scss index a7d25756a..001f6ff88 100644 --- a/src/app/styles/_variables.scss +++ b/src/app/styles/_variables.scss @@ -54,7 +54,8 @@ $pythagoras-const: 1.41421; // Layout widths $global-width: rem-size(940); -$sideframe-width: $global-width * 1/3; +//$sideframe-width: $global-width * 1/3; +$sideframe-width: $block-size * 4; $controls-inner-width: $block-size * 1.5; // Media Query Thresholds diff --git a/src/app/styles/main.scss b/src/app/styles/main.scss index cf61721ad..383bdac51 100644 --- a/src/app/styles/main.scss +++ b/src/app/styles/main.scss @@ -102,12 +102,12 @@ main { } .menu-right { - left: calc(100% + #{ rem-size(3) }); + left: calc(100% + #{rem-size(3)}); top: 0; } .menu-left { - right: calc(100% + #{ rem-size(3) }); + right: calc(100% + #{rem-size(3)}); top: 0; } @@ -362,7 +362,7 @@ ul.summary-entry-content { .pane-opened::after { @include iso-rect-triangle('to top', $simple-line-height, $heavy-border-color); bottom: -$midway-font-size; - left: calc(50% - #{ $simple-line-height * $pythagoras-const / 2 }); + left: calc(50% - #{$simple-line-height * $pythagoras-const / 2}); } } @@ -736,7 +736,7 @@ ul.summary-entry-content { } .fieldset-inner-wrapper { - margin: #{ $margin-size * 1/3 } 0 0; + margin: #{$margin-size * 1/3} 0 0; } } @@ -900,7 +900,7 @@ input[type=text] { max-height: 2 * $double-line-height; } - @media(max-width: #{ $media-query-threshold-wide - $block-size }) { + @media(max-width: #{$media-query-threshold-wide - $block-size}) { label small { @include visually-hidden; } @@ -1085,6 +1085,19 @@ input[type=text] { strong { color: $highlight-color; } + + .lmem-topbar-preferences { + color: inherit; + + > img { + margin-right: $half-adjusted-margin; + } + + > span:not(:last-child)::after { + content: '-'; + margin: 0 $half-adjusted-margin; + } + } } @@ -1127,9 +1140,12 @@ input[type=text] { } -.lmem-controls-list > li { - display: inline-block; - margin-right: $margin-size; +.lmem-controls-list { + display: flex; + + > li { + margin-left: $margin-size; + } } .lmem-controls-picto { diff --git a/src/app/styles/reco-header.scss b/src/app/styles/reco-header.scss index 50d277939..43a4f48a8 100644 --- a/src/app/styles/reco-header.scss +++ b/src/app/styles/reco-header.scss @@ -4,6 +4,7 @@ > header{ background: $background-em-color; + color: $font-em-color; display: flex; flex-direction: row; @@ -17,6 +18,7 @@ > .logo{ position: relative; // so that the tooltip with position: absolute works + max-height: $block-size - $margin-size; background: inherit; box-shadow: none; border: 0; @@ -74,8 +76,5 @@ min-width: $controls-inner-width; } } - } - - -} \ No newline at end of file +} diff --git a/src/app/styles/reco-main.scss b/src/app/styles/reco-main.scss index 322cb0812..79edc8484 100644 --- a/src/app/styles/reco-main.scss +++ b/src/app/styles/reco-main.scss @@ -9,9 +9,7 @@ a.mainframe{ flex: 20 1 0; - - margin-left: $adjusted-margin; - padding-left: $margin-size; + align-self: flex-start; border: solid $border-width $background-light-color; display: block; @@ -19,6 +17,9 @@ text-decoration: none; line-height: rem-size(20); + margin-left: $adjusted-margin; + padding-left: $margin-size; + &:hover, &:focus, &:active { @@ -27,6 +28,5 @@ } } } - } \ No newline at end of file diff --git a/src/app/styles/top-level.scss b/src/app/styles/top-level.scss index ada5b8fdd..229690b0b 100644 --- a/src/app/styles/top-level.scss +++ b/src/app/styles/top-level.scss @@ -24,3 +24,5 @@ body { } + + From 6652604d3c805aa7339ece977caadd7deccdc4d6 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Fri, 16 Sep 2016 19:09:59 +0200 Subject: [PATCH 07/29] chore fix watcher to include scss --- gulp/tasks/build.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gulp/tasks/build.js b/gulp/tasks/build.js index 0ed73ae3c..bcc575fe4 100644 --- a/gulp/tasks/build.js +++ b/gulp/tasks/build.js @@ -31,8 +31,8 @@ function build(compiler, callback) { gulp.task('webpack:watch', () => { const globs = [ - path.join(__dirname, '../../src/') + '**/*{js, scss}', - path.join(__dirname, '../../test/') + '**/*{js}', + path.join(__dirname, '../../src/') + '**/*', + path.join(__dirname, '../../test/') + '**/*', ]; return gulp.watch(globs, ['webpack:build:dev']); }); From c40809eab1bb56b284301f3a73c1667d60f093d5 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Fri, 16 Sep 2016 19:11:43 +0200 Subject: [PATCH 08/29] style about prefs screen --- src/app/actions/install.js | 24 ++++++++++ src/app/components/Alternatives.js | 8 +++- src/app/components/PreferenceAboutPanel.js | 44 +++++++++++++++-- src/app/components/PreferenceScreen.js | 39 +++++++++++---- src/app/constants/ActionTypes.js | 5 ++ src/app/constants/assetsUrls.js | 3 +- src/app/constants/ui.js | 6 ++- src/app/containers/App.js | 5 +- src/app/content/actions/preferences.js | 11 ++++- src/app/content/reducers/index.js | 7 ++- src/app/reducers/index.js | 8 +++- src/app/styles/preference-screen.scss | 55 +++++++++++++++++----- src/app/tabs/index.js | 5 +- src/browser/extension/background/index.js | 12 +++-- src/browser/extension/content/index.js | 12 +++-- 15 files changed, 198 insertions(+), 46 deletions(-) create mode 100644 src/app/actions/install.js diff --git a/src/app/actions/install.js b/src/app/actions/install.js new file mode 100644 index 000000000..08d7955cc --- /dev/null +++ b/src/app/actions/install.js @@ -0,0 +1,24 @@ +import { INSTALLED } from '../constants/ActionTypes'; + +function onInstalledPromise() { + return new Promise(resolve => { + + chrome.runtime.onInstalled.addListener(details => { + if (details.reason !== 'install') return; + + resolve(Object.assign({}, details, { + datetime: new Date(), + version: chrome.runtime.getManifest().version, + })); + }); + }); +} + +export function onInstalled() { + return dispatch => { + onInstalledPromise().then(onInstalledDetails => dispatch({ + type: INSTALLED, + onInstalledDetails + })); + }; +} diff --git a/src/app/components/Alternatives.js b/src/app/components/Alternatives.js index 436b9c9ce..0fc589e5f 100644 --- a/src/app/components/Alternatives.js +++ b/src/app/components/Alternatives.js @@ -17,15 +17,18 @@ class Alternatives extends Component { const { props, state } = this; const { recommendation, imagesUrl, reduced, contributorUrl, preferenceScreenPanel, deactivatedWebsites, - onExtend, onReduce, onDeactivate, togglePrefPanel, onReactivateWebsite, closePrefScreen, openPrefScreen + onExtend, onReduce, onDeactivate, togglePrefPanel, onReactivateWebsite, closePrefScreen, openPrefScreen, + onInstalledDetails } = props; - + const body = (preferenceScreenPanel ? : ); @@ -55,6 +58,7 @@ Alternatives.propTypes = { reduced: PropTypes.bool.isRequired, onExtend: PropTypes.func.isRequired, onReduce: PropTypes.func.isRequired, + onInstalledDetails: PropTypes.object.isRequired, }; export default Alternatives; diff --git a/src/app/components/PreferenceAboutPanel.js b/src/app/components/PreferenceAboutPanel.js index 32839f27f..bab834fd9 100644 --- a/src/app/components/PreferenceAboutPanel.js +++ b/src/app/components/PreferenceAboutPanel.js @@ -1,5 +1,43 @@ import React, { Component, PropTypes } from 'react'; +import { EXTENSION_VERSION } from '../constants/ui'; -export default function(){ - return Le Même En Mieux vous recommande des alternatives pertinentes, blablabla; -} \ No newline at end of file +export default function ({ onInstalledDetails }) { + const dateOfInstall = new Date(onInstalledDetails.get('datetime')); + const localeDateOfInstall = dateOfInstall.toLocaleDateString('fr', + { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); + + return ( +
    +
    +

    + Le Même en Mieux est un assistant d’achat que vous avez installé le  + . +

    +

    + Quand vous consultez un produit sur Internet, il vous trouve des conseils d’achat, + des comparatifs et de meilleures alternatives, selon vos préférences. +

    + + {/*

    Localisation

    */} + {/*

    */} + {/* Si possible, l’extension filtre les recommandations pertinentes pour votre localité : */} + {/* .*/} + {/*

    */} +
    + +
    + ); +} diff --git a/src/app/components/PreferenceScreen.js b/src/app/components/PreferenceScreen.js index 97c5ad8af..6c5331889 100644 --- a/src/app/components/PreferenceScreen.js +++ b/src/app/components/PreferenceScreen.js @@ -11,14 +11,17 @@ import PreferenceDeactivatedPanel from './PreferenceDeactivatedPanel'; export default function (props) { const { preferenceScreenPanel, deactivatedWebsites, - onReactivateWebsite, openPrefScreen + onReactivateWebsite, openPrefScreen, imagesUrl, + onInstalledDetails } = props; let mainContent; switch (preferenceScreenPanel){ case PREFERENCE_SCREEN_PANEL_ABOUT: - mainContent = ; + mainContent = (); break; case PREFERENCE_SCREEN_PANEL_DEACTIVATED_WEBSITES: mainContent = ( { - const newContent = e.target.getAttribute('data-panel'); + const newContent = e.currentTarget.getAttribute('data-panel'); openPrefScreen(newContent); }; - return (
    + return (
    +
    {mainContent}
    ); } - diff --git a/src/app/constants/ActionTypes.js b/src/app/constants/ActionTypes.js index b6cec4849..de353a027 100644 --- a/src/app/constants/ActionTypes.js +++ b/src/app/constants/ActionTypes.js @@ -11,9 +11,13 @@ export const HEAP_EVENT_TRACKED = 'heap/EVENT_TRACKED'; export const UPDATE_DRAFT_RECOMMANDATIONS = 'UPDATE_DRAFT_RECOMMANDATIONS'; +export const INSTALLED = 'INSTALLED'; + // content actions export const ALTERNATIVE_FOUND = 'ALTERNATIVE_FOUND'; +export const INSTALLED_DETAILS = 'INSTALLED_DETAILS'; + export const REDUCE_ALTERNATIVE_IFRAME = 'REDUCE_ALTERNATIVE_IFRAME'; export const EXTEND_ALTERNATIVE_IFRAME = 'EXTEND_ALTERNATIVE_IFRAME'; @@ -24,3 +28,4 @@ export const DEACTIVATED_WEBSITES = 'DEACTIVATED_WEBSITES'; export const DEACTIVATE = 'DEACTIVATE'; export const REACTIVATE_WEBSITE = 'REACTIVATE_WEBSITE'; + diff --git a/src/app/constants/assetsUrls.js b/src/app/constants/assetsUrls.js index 948366b54..e554c90ca 100644 --- a/src/app/constants/assetsUrls.js +++ b/src/app/constants/assetsUrls.js @@ -1,4 +1,3 @@ export const IMAGES_URL = chrome.extension.getURL('img/'); export const CONTRIBUTOR_IMAGES_URL = 'https://lmem-craft-backend.cleverapps.io/uploads/avatars/'; -export const FONTS_URL = chrome.extension.getURL('fonts/'); -export const STYLES_URL = chrome.extension.getURL('styles/'); + diff --git a/src/app/constants/ui.js b/src/app/constants/ui.js index 986270b40..a5d1b4464 100644 --- a/src/app/constants/ui.js +++ b/src/app/constants/ui.js @@ -3,6 +3,8 @@ import React, { Component } from 'react'; export const PREFERENCE_SCREEN_PANEL_ABOUT = 'PREFERENCE_SCREEN_PANEL_ABOUT'; export const PREFERENCE_SCREEN_PANEL_DEACTIVATED_WEBSITES = 'PREFERENCE_SCREEN_PANEL_DEACTIVATED_WEBSITES'; +export const EXTENSION_VERSION = chrome.runtime.getManifest().version; + export const HEADER_CONTENT = { [PREFERENCE_SCREEN_PANEL_ABOUT]: imagesUrl => ( @@ -10,7 +12,7 @@ export const HEADER_CONTENT = { role="presentation" src={ imagesUrl + 'settings.svg' } className="lmem-controls-picto" /> - Préférences de l’extension + Préférences À propos ), @@ -21,7 +23,7 @@ export const HEADER_CONTENT = { role="presentation" src={ imagesUrl + 'settings.svg' } className="lmem-controls-picto" /> - Préférences de l’extension + Préférences Sites désactivés
    ), diff --git a/src/app/containers/App.js b/src/app/containers/App.js index 04cb6c9ca..067567241 100644 --- a/src/app/containers/App.js +++ b/src/app/containers/App.js @@ -4,7 +4,7 @@ import Alternative from '../components/Alternatives'; import uiActions from '../content/actions/ui.js'; import { IMAGES_URL, CONTRIBUTOR_IMAGES_URL } from '../constants/assetsUrls'; -import portCommunication from 'app/content/portCommunication'; +import portCommunication from '../content/portCommunication'; const { reduce, extend, deactivate, closePrefScreen, openPrefScreen, reactivateWebsite @@ -17,7 +17,8 @@ function mapStateToProps(state) { contributorUrl: CONTRIBUTOR_IMAGES_URL, reduced: state.get('reduced'), preferenceScreenPanel: state.get('preferenceScreenPanel'), - deactivatedWebsites: state.get('deactivatedWebsites') + deactivatedWebsites: state.get('deactivatedWebsites'), + onInstalledDetails: state.get('onInstalledDetails') }; } function mapDispatchToProps(dispatch) { diff --git a/src/app/content/actions/preferences.js b/src/app/content/actions/preferences.js index 0d6d3dbe7..6076962e4 100644 --- a/src/app/content/actions/preferences.js +++ b/src/app/content/actions/preferences.js @@ -1,8 +1,15 @@ -import { DEACTIVATED_WEBSITES } from '../../constants/ActionTypes'; +import { DEACTIVATED_WEBSITES, INSTALLED_DETAILS } from '../../constants/ActionTypes'; -export default function (deactivatedWebsites) { +export function updateDeactivatedWebsites(deactivatedWebsites) { return { type: DEACTIVATED_WEBSITES, deactivatedWebsites }; } + +export function updateInstalledDetails(onInstalledDetails) { + return { + type: INSTALLED_DETAILS, + onInstalledDetails + }; +} diff --git a/src/app/content/reducers/index.js b/src/app/content/reducers/index.js index a8433bae1..8d9087fc9 100644 --- a/src/app/content/reducers/index.js +++ b/src/app/content/reducers/index.js @@ -6,7 +6,8 @@ import { OPEN_PREFERENCE_PANEL, CLOSE_PREFERENCE_PANEL, DEACTIVATED_WEBSITES, - REACTIVATE_WEBSITE + REACTIVATE_WEBSITE, + INSTALLED_DETAILS, } from '../../constants/ActionTypes'; export default function (state = {}, action) { @@ -39,6 +40,10 @@ export default function (state = {}, action) { const { deactivatedWebsites } = action; return state.set('deactivatedWebsites', deactivatedWebsites); + case INSTALLED_DETAILS: + const { onInstalledDetails } = action; + return state.set('onInstalledDetails', onInstalledDetails); + case REACTIVATE_WEBSITE: const { website } = action; return state.set('deactivatedWebsites', state.get('deactivatedWebsites').delete(website)); diff --git a/src/app/reducers/index.js b/src/app/reducers/index.js index a3dd0e857..97520968d 100644 --- a/src/app/reducers/index.js +++ b/src/app/reducers/index.js @@ -3,7 +3,8 @@ import { RECEIVED_MATCHING_CONTEXTS, DEACTIVATE, REACTIVATE_WEBSITE, - UPDATE_DRAFT_RECOMMANDATIONS + UPDATE_DRAFT_RECOMMANDATIONS, + INSTALLED } from '../constants/ActionTypes'; import { DEACTIVATE_EVERYWHERE, DEACTIVATE_WEBSITE_ALWAYS } from '../constants/preferences'; @@ -77,6 +78,11 @@ export default function (state = {}, action) { return Object.assign({}, state, { draftRecommandations }); } + case INSTALLED: { + const { onInstalledDetails } = action; + return Object.assign({}, state, { onInstalledDetails }); + } + default: return state; } diff --git a/src/app/styles/preference-screen.scss b/src/app/styles/preference-screen.scss index 2eafd82b8..c72519517 100644 --- a/src/app/styles/preference-screen.scss +++ b/src/app/styles/preference-screen.scss @@ -8,28 +8,59 @@ } nav { - width: 10%; + ul { + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + } - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: flex-start; + li { + margin-bottom: $half-adjusted-margin; + } button { - background: transparent; - border: 0; - padding: 1em; + color: inherit; + font-size: $simple-line-height; + line-height: $double-line-height; + + align-items: center; - &:hover{ - background: hsla(0, 0%, 50%, 0.3); - } + padding: 0 $margin-size 0 $half-adjusted-margin; + width: 100%; + } + + img { + margin-right: 1ex; + margin-left: initial; + opacity: .9; } } main { flex: 1; + } - padding: 1em; + h1, + h2 { + font-size: $simple-line-height; + margin: $half-adjusted-margin 0 #{$margin-size - $half-adjusted-margin}; } + aside { + margin-top: $margin-size; + font-size: $tiny-font-size; + + ul { + display: flex; + } + li:not(:last-child)::after { + content: '-'; + margin: 0 1ex; + } + } + + time { + white-space: nowrap; + } } \ No newline at end of file diff --git a/src/app/tabs/index.js b/src/app/tabs/index.js index 482f2db54..a23d12951 100644 --- a/src/app/tabs/index.js +++ b/src/app/tabs/index.js @@ -1,6 +1,6 @@ export default function ( tabs, - { findMatchingOffers, dispatch, contentCode, contentStyle, getDeactivatedWebsites } + { findMatchingOffers, dispatch, contentCode, contentStyle, getDeactivatedWebsites, getOnInstalledDetails } ) { const matchingTabIdToPortP = new Map(); @@ -27,7 +27,8 @@ export default function ( tabPort.postMessage({ type: 'init', style: contentStyle, - deactivatedWebsites: [...getDeactivatedWebsites()] + deactivatedWebsites: [...getDeactivatedWebsites()], + onInstalledDetails: getOnInstalledDetails() }); resolve(tabPort); diff --git a/src/browser/extension/background/index.js b/src/browser/extension/background/index.js index b04dce897..eec3cf209 100644 --- a/src/browser/extension/background/index.js +++ b/src/browser/extension/background/index.js @@ -10,8 +10,9 @@ import prepareDraftPreview from '../../../app/lmem/draft-preview/main.js'; import { dispatchInitialStateFromBackend } from '../../../app/actions/kraftBackend'; import updateDraftRecommandations from '../../../app/actions/updateDraftRecommandations'; - import heap from './../../../lib/heap'; +import { onInstalled } from '../../../app/actions/install'; + /** * FIXME import styles from components instead and let Webpack taking care of them... * @@ -71,11 +72,14 @@ configureStore(store => { const deactivated = prefs.deactivated || {}; return deactivated.deactivatedWebsites || new Set(); }, + getOnInstalledDetails: () => { + const state = store.getState(); + return state.onInstalledDetails || {}; + }, dispatch: store.dispatch, contentCode, contentStyle: mainStyles - } - ); + }); }); draftRecoContentCodeP @@ -86,6 +90,8 @@ configureStore(store => { ) ); + store.dispatch(onInstalled()); + store.dispatch(dispatchInitialStateFromBackend()); // store initialization from the kraft server if (process.env.NODE_ENV !== 'production') { diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index d05d5ef72..012683358 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -1,4 +1,4 @@ -import { Record, Set as ImmutableSet } from 'immutable'; +import { Record, Set as ImmutableSet, Map as ImmutableMap, fromJS as immutableFromJS } from 'immutable'; import React from 'react'; import { render } from 'react-dom'; import Root from '../../../app/containers/Root'; @@ -8,7 +8,7 @@ import { createStore } from 'redux'; import rootReducer from '../../../app/content/reducers'; import alternativeFound from '../../../app/content/actions/alternatives'; -import updateDeactivatedWebsites from '../../../app/content/actions/preferences'; +import { updateDeactivatedWebsites, updateInstalledDetails } from '../../../app/content/actions/preferences'; import portCommunication from '../../../app/content/portCommunication'; const IFRAME_EXTENDED_HEIGHT = '255px'; @@ -22,7 +22,8 @@ const store = createStore( reduced: false, preferenceScreenPanel: undefined, // preference screen close alternative: undefined, - deactivatedWebsites: new ImmutableSet() + deactivatedWebsites: new ImmutableSet(), + onInstalledDetails: new ImmutableMap(), })() ); @@ -37,7 +38,7 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { switch (type) { case 'init': - const { style, deactivatedWebsites } = msg; + const { style, deactivatedWebsites, onInstalledDetails } = msg; const reduced = store.getState().get('reduced'); const lmemContentContainerP = new Promise(resolve => { const iframe = document.createElement('iframe'); @@ -49,7 +50,7 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { iframe.style.left = 0; iframe.style.right = 0; iframe.style.zIndex = 2147483647; // Max z-index value (signed 32bits integer) - iframe.style.background = '#FDF6E3'; // UI background color (avoid having a transparent iframe after injection) + iframe.style.background = '#FDF6E3'; // UI bg color (avoid having a transparent iframe after injection) iframe.style.border = 'none'; iframe.style.transition = 'height .1s'; iframe.style.boxShadow = '0 0 15px #888'; @@ -82,6 +83,7 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { }); store.dispatch(updateDeactivatedWebsites(new ImmutableSet(deactivatedWebsites))); + store.dispatch(updateInstalledDetails(immutableFromJS(onInstalledDetails))); lmemContentContainerP.then(lmemContentContainer => { render( From 5730e913455cf99fb4696ab520bd38ee5253be16 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Sat, 17 Sep 2016 12:29:09 +0200 Subject: [PATCH 09/29] fix dateOfInstall when missing (i.e. install prior to this feature release) --- src/app/components/PreferenceAboutPanel.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/app/components/PreferenceAboutPanel.js b/src/app/components/PreferenceAboutPanel.js index bab834fd9..4ac31b654 100644 --- a/src/app/components/PreferenceAboutPanel.js +++ b/src/app/components/PreferenceAboutPanel.js @@ -1,17 +1,28 @@ import React, { Component, PropTypes } from 'react'; import { EXTENSION_VERSION } from '../constants/ui'; -export default function ({ onInstalledDetails }) { - const dateOfInstall = new Date(onInstalledDetails.get('datetime')); - const localeDateOfInstall = dateOfInstall.toLocaleDateString('fr', +function formatLocaleDate(strDate) { + const dateOfInstall = new Date(strDate); + + if (isNaN(dateOfInstall.valueOf())) + return undefined; + + return dateOfInstall.toLocaleDateString('fr', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); +} + +export default function ({ onInstalledDetails }) { + const ISODateOfInstall = onInstalledDetails && onInstalledDetails.get('datetime'); + const localeDateOfInstall = ISODateOfInstall && formatLocaleDate(ISODateOfInstall); return (

    - Le Même en Mieux est un assistant d’achat que vous avez installé le  - . + Le Même en Mieux est un assistant d’achat + { localeDateOfInstall ? ( + que vous avez installé le + ) : ''}.

    Quand vous consultez un produit sur Internet, il vous trouve des conseils d’achat, From b8fa14ad75af0cbe42760913b0ac652fc0694466 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Sat, 17 Sep 2016 12:39:20 +0200 Subject: [PATCH 10/29] chore build remove window from webpack entries --- webpack/dev.config.js | 2 +- webpack/production.config.js | 2 +- webpack/staging.config.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/webpack/dev.config.js b/webpack/dev.config.js index bd1f83988..8a139d25e 100644 --- a/webpack/dev.config.js +++ b/webpack/dev.config.js @@ -7,7 +7,7 @@ const testPath = path.join(__dirname, '../test/'); export default baseConfig({ input: { background: [`${srcPath}extension/background/`], - window: [`${srcPath}window/`], + // window: [`${srcPath}window/`], //popup: [`${srcPath}extension/popup/`], content: [`${srcPath}extension/content/`], test: [`${testPath}integration/`] diff --git a/webpack/production.config.js b/webpack/production.config.js index d649ec5a0..7661a8a82 100644 --- a/webpack/production.config.js +++ b/webpack/production.config.js @@ -6,7 +6,7 @@ const srcPath = path.join(__dirname, '../src/browser/'); export default baseConfig({ input: { background: [`${srcPath}extension/background/`], - window: [`${srcPath}window/`], + // window: [`${srcPath}window/`], //popup: [`${srcPath}extension/popup/`], content: [`${srcPath}extension/content/`] }, diff --git a/webpack/staging.config.js b/webpack/staging.config.js index 35c985e26..7e15d49c3 100644 --- a/webpack/staging.config.js +++ b/webpack/staging.config.js @@ -5,7 +5,7 @@ const srcPath = path.join(__dirname, '../src/browser/'); export default baseConfig({ input: { background: [`${srcPath}extension/background/`], - window: [`${srcPath}window/`], + // window: [`${srcPath}window/`], //popup: [`${srcPath}extension/popup/`], content: [`${srcPath}extension/content/`] }, From a4db9a644cdab9ef5dc1e4370006f5612014d399 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Sat, 17 Sep 2016 12:40:15 +0200 Subject: [PATCH 11/29] fix dateOfInstall missing whitespace --- src/app/components/PreferenceAboutPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/PreferenceAboutPanel.js b/src/app/components/PreferenceAboutPanel.js index 4ac31b654..3bbdad957 100644 --- a/src/app/components/PreferenceAboutPanel.js +++ b/src/app/components/PreferenceAboutPanel.js @@ -21,7 +21,7 @@ export default function ({ onInstalledDetails }) {

    Le Même en Mieux est un assistant d’achat { localeDateOfInstall ? ( - que vous avez installé le + que vous avez installé le ) : ''}.

    From 24e326fc668d233475c52e213805c3fd6e752442 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Sun, 18 Sep 2016 13:40:35 +0200 Subject: [PATCH 12/29] complete preference deactivated websites --- .../components/PreferenceDeactivatedPanel.js | 71 +- src/app/components/PreferenceScreen.js | 14 +- src/app/styles/elements.scss | 1 + src/app/styles/preference-screen.scss | 129 +- src/app/styles/reco-header.scss | 4 + src/app/styles/reco-main.scss | 5 +- src/app/styles/top-level.scss | 7 +- src/assets/img/ball.svg | 1048 +---------------- src/assets/img/close.svg | 145 +-- src/assets/img/valid.svg | 6 + 10 files changed, 208 insertions(+), 1222 deletions(-) create mode 100644 src/assets/img/valid.svg diff --git a/src/app/components/PreferenceDeactivatedPanel.js b/src/app/components/PreferenceDeactivatedPanel.js index 9c09f7b9e..d55a97dc2 100644 --- a/src/app/components/PreferenceDeactivatedPanel.js +++ b/src/app/components/PreferenceDeactivatedPanel.js @@ -15,7 +15,7 @@ class PreferenceDeactivatedPanel extends Component { render() { const { props, state } = this; const { - deactivatedWebsites, onReactivateWebsite + deactivatedWebsites, onReactivateWebsite, imagesUrl } = props; const { reactivatedWebsites } = state; @@ -27,32 +27,59 @@ class PreferenceDeactivatedPanel extends Component { const reactivatedWebsitesArray = [...reactivatedWebsites] .map(w => ({ website: w, active: true })); - console.log('d, r', websitesDisplayedAsDeactivatedArray, reactivatedWebsitesArray); + // console.log('d, r', websitesDisplayedAsDeactivatedArray, reactivatedWebsitesArray); const displayedWebsites = websitesDisplayedAsDeactivatedArray.concat(reactivatedWebsitesArray); displayedWebsites.sort(({ website: w1 }, { website: w2 }) => w1.localeCompare(w2)); const lis = displayedWebsites - .map(({ website, active }) =>

  • - {website} - -
  • ); - - return (
    -
      - {lis} -
    -
    ); + .map(({ website, active }) =>
  • + + + {website.replace(/^www\./, '')} + + +
  • ); + + return lis.length > 0 ? + (
    +
      {lis}
    +
    + +
    ) : + (

    Aucun site n’est désactivé :
    + L’assistant vous accompagne partout pour vous trouver + des recommandations susceptibles de vous intéresser. +

    ); } } diff --git a/src/app/components/PreferenceScreen.js b/src/app/components/PreferenceScreen.js index 6c5331889..b42731c33 100644 --- a/src/app/components/PreferenceScreen.js +++ b/src/app/components/PreferenceScreen.js @@ -8,6 +8,17 @@ import { import PreferenceAboutPanel from './PreferenceAboutPanel'; import PreferenceDeactivatedPanel from './PreferenceDeactivatedPanel'; +function mainClassName(screenPanel) { + switch (screenPanel) { + case PREFERENCE_SCREEN_PANEL_ABOUT: + return 'preference-about'; + case PREFERENCE_SCREEN_PANEL_DEACTIVATED_WEBSITES: + return 'preference-deactivated-websites'; + default: + return ''; + } +} + export default function (props) { const { preferenceScreenPanel, deactivatedWebsites, @@ -27,6 +38,7 @@ export default function (props) { mainContent = (); break; default: @@ -64,7 +76,7 @@ export default function (props) {
    -
    +
    {mainContent}
    ); diff --git a/src/app/styles/elements.scss b/src/app/styles/elements.scss index b56503c14..717884095 100644 --- a/src/app/styles/elements.scss +++ b/src/app/styles/elements.scss @@ -14,6 +14,7 @@ html, body{ margin: 0; padding: 0; + height: 100%; } // Content elements diff --git a/src/app/styles/preference-screen.scss b/src/app/styles/preference-screen.scss index c72519517..7d238fb0f 100644 --- a/src/app/styles/preference-screen.scss +++ b/src/app/styles/preference-screen.scss @@ -2,12 +2,16 @@ display: flex; flex-direction: row; + margin-top: $half-adjusted-margin; + box-sizing: border-box; * { box-sizing: border-box; } nav { + flex: 0 0 auto; + ul { display: flex; flex-direction: column; @@ -38,14 +42,11 @@ } main { - flex: 1; + flex: 1 1 auto; } +} - h1, - h2 { - font-size: $simple-line-height; - margin: $half-adjusted-margin 0 #{$margin-size - $half-adjusted-margin}; - } +.preference-about { aside { margin-top: $margin-size; @@ -58,9 +59,125 @@ content: '-'; margin: 0 1ex; } + + h1, + h2 { + font-size: $simple-line-height; + margin: $half-adjusted-margin 0 #{$margin-size - $half-adjusted-margin}; + } } time { white-space: nowrap; } +} + +.preference-deactivated-websites { + p { + margin-top: $half-adjusted-margin; + } + + > div { + display: flex; + } + aside { + flex: 1 1 50%; + color: $font-quiet-color; + + h1, + h2 { + font-weight: 600; + margin: $half-adjusted-margin 0 #{$margin-size - $half-adjusted-margin}; + } + + img { + vertical-align: bottom; + } + } + ul { + flex: 1 0 auto; + display: flex; + flex-direction: column; + align-items: flex-start; + overflow-y: auto; + max-height: $block-size * 3; + } + li { + flex: 0 0 auto; + display: flex; + line-height: $double-line-height; + margin-bottom: $half-adjusted-margin; + + &.reactivated { + > button, + > .deactivated-website-title { + opacity: .5; + } + + > button { + background: none; + + width: $block-size / 2; + margin-left: $block-size / 2; + margin-right: $block-size / 2; + padding: 0; + + border-radius: 100%; + + > img { + width: $double-line-height; + opacity: .8; + } + } + + &:hover { + > button, + > span { opacity: initial } + } + } + + > button { + align-self: center; + height: $double-line-height; + width: $block-size * 3 / 2; + + font-size: $simple-line-height; + line-height: $simple-line-height; + font-weight: inherit; + + opacity: 0; + transition: opacity, border-radius .2s ease-out 50ms; + } + + &:hover > button, + > button:focus, + > button:active { + opacity: initial; + } + + transition: background-color .2s ease-out; + &:hover { + background-color: $background-light-color; + + img { + filter: initial; + -webkit-filter: initial; + opacity: initial; + } + } + } + + .deactivated-website-title { + font-size: $midway-font-size; + margin: 0 $margin-size 0 $half-adjusted-margin; + + img { + margin-right: $half-adjusted-margin; + vertical-align: middle; + + filter: grayscale(100%); + -webkit-filter: grayscale(100%); + opacity: .7; + } + } } \ No newline at end of file diff --git a/src/app/styles/reco-header.scss b/src/app/styles/reco-header.scss index 43a4f48a8..52ef457a6 100644 --- a/src/app/styles/reco-header.scss +++ b/src/app/styles/reco-header.scss @@ -5,6 +5,9 @@ > header{ background: $background-em-color; color: $font-em-color; + + flex: 0 0 100%; + display: flex; flex-direction: row; @@ -42,6 +45,7 @@ button.reduce{ align-items: center; line-height: $double-line-height; + display: flex; } .separation-bar{ diff --git a/src/app/styles/reco-main.scss b/src/app/styles/reco-main.scss index 79edc8484..688285ef3 100644 --- a/src/app/styles/reco-main.scss +++ b/src/app/styles/reco-main.scss @@ -5,7 +5,7 @@ & > main{ display: flex; justify-content: flex-end; - margin-top: 0.539em; + margin-top: $half-adjusted-margin; a.mainframe{ flex: 20 1 0; @@ -17,9 +17,6 @@ text-decoration: none; line-height: rem-size(20); - margin-left: $adjusted-margin; - padding-left: $margin-size; - &:hover, &:focus, &:active { diff --git a/src/app/styles/top-level.scss b/src/app/styles/top-level.scss index 229690b0b..848ba7d73 100644 --- a/src/app/styles/top-level.scss +++ b/src/app/styles/top-level.scss @@ -15,12 +15,13 @@ body { } .lmem-top-level { + height: inherit; display: flex; flex-direction: column; - > *{ - padding: 0.539em $margin-size; - } + > * { + padding: $half-adjusted-margin $margin-size; + } } diff --git a/src/assets/img/ball.svg b/src/assets/img/ball.svg index fd73a0cc3..fc5c82bb1 100644 --- a/src/assets/img/ball.svg +++ b/src/assets/img/ball.svg @@ -1,1046 +1,6 @@ - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ? - - - ? - - - - - - i - - - - - i - - - i - - - i - - - i - - - - - - - - - + + + + diff --git a/src/assets/img/close.svg b/src/assets/img/close.svg index 9bcac290f..9130abd24 100644 --- a/src/assets/img/close.svg +++ b/src/assets/img/close.svg @@ -1,145 +1,6 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ? - - - ? - - - - - - i - - - i - - - i - - - i - - - i - - - i - - - - - - - - - + + + diff --git a/src/assets/img/valid.svg b/src/assets/img/valid.svg new file mode 100644 index 000000000..ad954e964 --- /dev/null +++ b/src/assets/img/valid.svg @@ -0,0 +1,6 @@ + + + + + + From 1f5b3a1464d2f60732a8a63e38eb805092951ecb Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Sun, 18 Sep 2016 19:45:50 +0200 Subject: [PATCH 13/29] use proper favicon for reco --- src/app/components/AlternativeMain.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/components/AlternativeMain.js b/src/app/components/AlternativeMain.js index 7fe753940..c0adf44c6 100644 --- a/src/app/components/AlternativeMain.js +++ b/src/app/components/AlternativeMain.js @@ -39,7 +39,9 @@ const AlternativeMain = ({ imagesUrl, contributorUrl, recommendation }) => {

    {recommendation.description}

    - Logo le même en mieux + Logo le même en mieux { recommendation.alternatives[0].url_to_redirect.replace(/^https?:\/\/(www.)?/, '') } @@ -58,7 +60,7 @@ const AlternativeMain = ({ imagesUrl, contributorUrl, recommendation }) => {
    - ) + ); }; AlternativeMain.propTypes = { From 39c0758f19eac3875bec2cb00feb7c3a120678e9 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Sun, 18 Sep 2016 19:46:13 +0200 Subject: [PATCH 14/29] polish reco style --- src/app/styles/reco.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/styles/reco.scss b/src/app/styles/reco.scss index 3c2f9958c..2fe4fa317 100644 --- a/src/app/styles/reco.scss +++ b/src/app/styles/reco.scss @@ -10,7 +10,7 @@ .reco-summary-header { display: flex; justify-content: space-between; - align-items: baseline; + align-items: flex-start; } .reco-summary-title { @@ -79,7 +79,7 @@ } img { - height: rem-size(20); + height: rem-size(16); margin-right: 1ex; } } From 142176049b8c5161ddc86f3fa944d9bfa7a6ed69 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Thu, 22 Sep 2016 10:57:54 +0200 Subject: [PATCH 15/29] fix isNaN error prone type conversion --- src/app/components/PreferenceAboutPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/PreferenceAboutPanel.js b/src/app/components/PreferenceAboutPanel.js index 3bbdad957..533526e0b 100644 --- a/src/app/components/PreferenceAboutPanel.js +++ b/src/app/components/PreferenceAboutPanel.js @@ -4,7 +4,7 @@ import { EXTENSION_VERSION } from '../constants/ui'; function formatLocaleDate(strDate) { const dateOfInstall = new Date(strDate); - if (isNaN(dateOfInstall.valueOf())) + if (Number.isNaN(dateOfInstall.getTime())) return undefined; return dateOfInstall.toLocaleDateString('fr', From 568afef6adcf0dc06d9ac0284db95310ac1ed4a6 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Thu, 22 Sep 2016 11:28:55 +0200 Subject: [PATCH 16/29] detect user-agent locale --- src/app/components/PreferenceAboutPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/PreferenceAboutPanel.js b/src/app/components/PreferenceAboutPanel.js index 533526e0b..06622e368 100644 --- a/src/app/components/PreferenceAboutPanel.js +++ b/src/app/components/PreferenceAboutPanel.js @@ -7,7 +7,7 @@ function formatLocaleDate(strDate) { if (Number.isNaN(dateOfInstall.getTime())) return undefined; - return dateOfInstall.toLocaleDateString('fr', + return dateOfInstall.toLocaleDateString(navigator.language, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } From 1e066bab8a1f54cd156b332c34113c780129d8f2 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Thu, 22 Sep 2016 17:22:40 +0200 Subject: [PATCH 17/29] fix onInstall to catch early install event --- src/app/actions/install.js | 25 +++++++++++------------ src/browser/extension/background/index.js | 11 ++++++---- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/app/actions/install.js b/src/app/actions/install.js index 08d7955cc..6c30c0bb9 100644 --- a/src/app/actions/install.js +++ b/src/app/actions/install.js @@ -1,22 +1,21 @@ import { INSTALLED } from '../constants/ActionTypes'; -function onInstalledPromise() { - return new Promise(resolve => { +// Promise constructed when the module is first imported (very early) +// in order to not miss the "install" event. +const onInstalledPromise = new Promise(resolve => { + chrome.runtime.onInstalled.addListener(details => { + if (details.reason !== 'install') return; - chrome.runtime.onInstalled.addListener(details => { - if (details.reason !== 'install') return; - - resolve(Object.assign({}, details, { - datetime: new Date(), - version: chrome.runtime.getManifest().version, - })); - }); + resolve(Object.assign({}, details, { + datetime: new Date(), + version: chrome.runtime.getManifest().version, + })); }); -} +}); -export function onInstalled() { +export default function () { return dispatch => { - onInstalledPromise().then(onInstalledDetails => dispatch({ + onInstalledPromise.then(onInstalledDetails => dispatch({ type: INSTALLED, onInstalledDetails })); diff --git a/src/browser/extension/background/index.js b/src/browser/extension/background/index.js index eec3cf209..711abbfac 100644 --- a/src/browser/extension/background/index.js +++ b/src/browser/extension/background/index.js @@ -1,7 +1,10 @@ /* eslint global-require: "off" */ +// Early imports with high priority stuff involved, such as event listeners creation +import onInstalled from '../../../app/actions/install'; +import heap from './../../../lib/heap'; + import configureStore from './../../../app/store/configureStore'; -import initBadge from './badge'; import findMatchingOffersAccordingToPreferences from '../../../app/lmem/findMatchingOffersAccordingToPreferences'; @@ -10,8 +13,6 @@ import prepareDraftPreview from '../../../app/lmem/draft-preview/main.js'; import { dispatchInitialStateFromBackend } from '../../../app/actions/kraftBackend'; import updateDraftRecommandations from '../../../app/actions/updateDraftRecommandations'; -import heap from './../../../lib/heap'; -import { onInstalled } from '../../../app/actions/install'; /** * FIXME import styles from components instead and let Webpack taking care of them... @@ -90,7 +91,9 @@ configureStore(store => { ) ); - store.dispatch(onInstalled()); + if (!store.getState().onInstalledDetails) { + store.dispatch(onInstalled()); + } store.dispatch(dispatchInitialStateFromBackend()); // store initialization from the kraft server From b7a28970174fee299bc460e87e8f5bb313e43472 Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 13 Sep 2016 15:35:44 +0200 Subject: [PATCH 18/29] Delay extension display even when an alternative is available quickly --- src/browser/extension/content/index.js | 179 ++++++++++++++++++------- 1 file changed, 129 insertions(+), 50 deletions(-) diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index 012683358..f9b12bd6b 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -14,6 +14,93 @@ import portCommunication from '../../../app/content/portCommunication'; const IFRAME_EXTENDED_HEIGHT = '255px'; const IFRAME_REDUCED_HEIGHT = '60px'; +const EXTENSION_STATE_SHOW_LOADING = 'EXTENSION_STATE_SHOW_LOADING'; +const EXTENSION_STATE_SHOW_ALTERNATIVE = 'EXTENSION_STATE_SHOW_ALTERNATIVE'; + +const AFTER_DOMCOMPLETE_DELAY = 5000; +const AFTER_LOADEND_DELAY = 1000; +const LOADING_SCREEN_DELAY = 4000; + +/* + LIB +*/ +function createExtensionIframe(reduced, style, onLoad){ + const iframe = document.createElement('iframe'); + iframe.id = 'lmemFrame'; + iframe.width = '100%'; + iframe.height = reduced ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; + iframe.style.position = 'fixed'; + iframe.style.bottom = '0px'; + iframe.style.left = '0px'; + iframe.style.right = '0px'; + iframe.style.zIndex = '999999999'; + iframe.srcdoc = ` + + + + + + + + `; + + iframe.onload = onLoad; + + return iframe; +} + + +/* + SETUP +*/ + +const DOMCompleteP = Promise.resolve(); // because the content script is loaded at "document_end" + +const DOMCompletePlusDelayP = DOMCompleteP.then(() => { + return new Promise(resolve => { + const {navigationStart, domContentLoadedEventStart} = performance.timing; + + const diff = domContentLoadedEventStart - navigationStart; + + if(diff >= AFTER_DOMCOMPLETE_DELAY) + resolve(); + else + setTimeout(resolve, AFTER_DOMCOMPLETE_DELAY - diff); + }); +}); + + +const LoadEndP = new Promise(resolve => { + document.addEventListener('load', resolve); +}); + +const LoadEndPlusDelayP = LoadEndP.then(() => { + return new Promise(resolve => { + const {navigationStart, loadEventStart} = performance.timing; + + const diff = loadEventStart - navigationStart; + + if(diff >= AFTER_LOADEND_DELAY) + resolve(); + else + setTimeout(resolve, AFTER_LOADEND_DELAY - diff); + }); +}); + +// Wait for some time before showing the extension to the user in loading mode +const CanShowIframeLoadingP = Promise.race([DOMCompletePlusDelayP, LoadEndPlusDelayP]); + +// User research showed that the LMEM loading screen is important so people don't +// think the LMEM iframe is an ad. +// Wait for some time loading before showing an alternative. +const CanShowAlternativeIfAvailableP = CanShowIframeLoadingP.then(() => { + return new Promise(resolve => { + setTimeout(resolve, LOADING_SCREEN_DELAY); + }); +}); + + + // create redux store const store = createStore( rootReducer, @@ -27,6 +114,9 @@ const store = createStore( })() ); + + + // reach back to background script chrome.runtime.onConnect.addListener(function listener(portToBackground) { portCommunication.port = portToBackground; @@ -38,65 +128,54 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { switch (type) { case 'init': - const { style, deactivatedWebsites, onInstalledDetails } = msg; - const reduced = store.getState().get('reduced'); - const lmemContentContainerP = new Promise(resolve => { - const iframe = document.createElement('iframe'); - iframe.id = 'lmemFrame'; - iframe.width = '100%'; - iframe.height = reduced ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; - iframe.style.position = 'fixed'; - iframe.style.bottom = 0; - iframe.style.left = 0; - iframe.style.right = 0; - iframe.style.zIndex = 2147483647; // Max z-index value (signed 32bits integer) - iframe.style.background = '#FDF6E3'; // UI bg color (avoid having a transparent iframe after injection) - iframe.style.border = 'none'; - iframe.style.transition = 'height .1s'; - iframe.style.boxShadow = '0 0 15px #888'; - iframe.srcdoc = ` - - - - - - - - `; - - iframe.onload = function () { - resolve(iframe.contentDocument.body); - }; - document.body.appendChild(iframe); - - store.subscribe(() => { - const state = store.getState(); - - if (!state.get('open')) { - iframe.remove(); - } - else { - iframe.height = state.get('reduced') ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; - } - - }); - }); + const { style, deactivatedWebsites } = msg; store.dispatch(updateDeactivatedWebsites(new ImmutableSet(deactivatedWebsites))); store.dispatch(updateInstalledDetails(immutableFromJS(onInstalledDetails))); - lmemContentContainerP.then(lmemContentContainer => { - render( - , - lmemContentContainer - ); + // Let the page load a bit before showing the iframe in loading mode + CanShowIframeLoadingP + .then(() => { + + return new Promise(resolve => { + const iframe = createExtensionIframe( + store.getState().get('reduced'), + style, + () => { resolve(iframe.contentDocument.body); } + ); + + document.body.appendChild(iframe); + + store.subscribe(() => { + const state = store.getState(); + + if (!state.get('open')) { + iframe.remove(); + } + else { + iframe.height = state.get('reduced') ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; + } + + }); + }) + .then(lmemContainer => { + render(, lmemContainer); + }); + }); + break; case 'alternative': const { alternative } = msg; - // console.log('alternative in content', alternative); - store.dispatch(alternativeFound(alternative)); + + // Even if the alternative arrived early, let the page load a bit before + // showing the iframe in loading mode + CanShowAlternativeIfAvailableP + .then(() => { + store.dispatch(alternativeFound(alternative)); + }); + break; default: console.error('Content script: unrecognized message type from background', type, msg); From 404fd1f576185de1ffd04882687df318306a07ce Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 27 Sep 2016 13:49:35 +0200 Subject: [PATCH 19/29] post-rebase fixes --- src/browser/extension/content/index.js | 2 +- src/browser/extension/manifest/dev.js | 2 +- src/browser/extension/manifest/prod.js | 2 +- src/browser/extension/manifest/staging.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index f9b12bd6b..3a757cc43 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -128,7 +128,7 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { switch (type) { case 'init': - const { style, deactivatedWebsites } = msg; + const { style, deactivatedWebsites, onInstalledDetails } = msg; store.dispatch(updateDeactivatedWebsites(new ImmutableSet(deactivatedWebsites))); store.dispatch(updateInstalledDetails(immutableFromJS(onInstalledDetails))); diff --git a/src/browser/extension/manifest/dev.js b/src/browser/extension/manifest/dev.js index 166748c5d..43a8cfde9 100644 --- a/src/browser/extension/manifest/dev.js +++ b/src/browser/extension/manifest/dev.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from "content-security-policy-builder"; +import csp from 'content-security-policy-builder'; export default Object.assign( {}, diff --git a/src/browser/extension/manifest/prod.js b/src/browser/extension/manifest/prod.js index 9e39d278d..958f2d817 100644 --- a/src/browser/extension/manifest/prod.js +++ b/src/browser/extension/manifest/prod.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from "content-security-policy-builder"; +import csp from 'content-security-policy-builder'; export default Object.assign( {}, diff --git a/src/browser/extension/manifest/staging.js b/src/browser/extension/manifest/staging.js index 5547cd254..7416004e4 100644 --- a/src/browser/extension/manifest/staging.js +++ b/src/browser/extension/manifest/staging.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from "content-security-policy-builder"; +import csp from 'content-security-policy-builder'; export default Object.assign( {}, From d0d073188ec2b5594c8441e2e09c7edeccc49ceb Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 27 Sep 2016 13:54:40 +0200 Subject: [PATCH 20/29] extension is reduced until a recommandation is found --- src/app/content/reducers/index.js | 2 +- src/browser/extension/content/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/content/reducers/index.js b/src/app/content/reducers/index.js index 8d9087fc9..056183749 100644 --- a/src/app/content/reducers/index.js +++ b/src/app/content/reducers/index.js @@ -16,7 +16,7 @@ export default function (state = {}, action) { switch (type) { case ALTERNATIVE_FOUND: { const { alternative } = action; - return state.set('alternative', alternative); + return state.set('alternative', alternative).set('reduced', false); } case REDUCE_ALTERNATIVE_IFRAME: diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index 3a757cc43..07de0070e 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -106,7 +106,7 @@ const store = createStore( rootReducer, new Record({ open: true, - reduced: false, + reduced: true, preferenceScreenPanel: undefined, // preference screen close alternative: undefined, deactivatedWebsites: new ImmutableSet(), From 589f9e60202f45169f590f83bde5b158b8502456 Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 27 Sep 2016 13:59:22 +0200 Subject: [PATCH 21/29] Don't delay showing the iframe when NODE_ENV is development --- src/browser/extension/content/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index 07de0070e..a40e2de8c 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -93,11 +93,13 @@ const CanShowIframeLoadingP = Promise.race([DOMCompletePlusDelayP, LoadEndPlusDe // User research showed that the LMEM loading screen is important so people don't // think the LMEM iframe is an ad. // Wait for some time loading before showing an alternative. -const CanShowAlternativeIfAvailableP = CanShowIframeLoadingP.then(() => { - return new Promise(resolve => { - setTimeout(resolve, LOADING_SCREEN_DELAY); +const CanShowAlternativeIfAvailableP = process.env.NODE_ENV === 'development' ? + Promise.resolve() : // otherwise the delay is annoying when developing + CanShowIframeLoadingP.then(() => { + return new Promise(resolve => { + setTimeout(resolve, LOADING_SCREEN_DELAY); + }); }); -}); From 35841a9af47cfdef8dbfeeb3026b71bdffe1b659 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Wed, 28 Sep 2016 23:09:53 +0200 Subject: [PATCH 22/29] fetch content remotely instead of locally --- src/app/constants/origins.js | 13 ++++++++----- src/browser/extension/background/index.js | 10 ++++++---- src/browser/extension/manifest/prod.js | 3 ++- src/browser/extension/manifest/staging.js | 3 ++- webpack/dev.config.js | 3 ++- webpack/production.config.js | 3 ++- webpack/staging.config.js | 3 ++- 7 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/app/constants/origins.js b/src/app/constants/origins.js index 74667d6aa..1414b69e0 100644 --- a/src/app/constants/origins.js +++ b/src/app/constants/origins.js @@ -1,7 +1,10 @@ -const _LMEM_BACKEND_ORIGIN = process.env.LMEM_BACKEND_ORIGIN; - -if(typeof _LMEM_BACKEND_ORIGIN !== 'string'){ - throw new TypeError('Missing LMEM backend origin ' + _LMEM_BACKEND_ORIGIN); +function originFromEnv(key) { + const origin = process.env[key]; + if (typeof origin !== 'string') { + throw new TypeError(`Missing LMEM env '${key}': ${origin}`); + } + return origin; } -export const LMEM_BACKEND_ORIGIN = _LMEM_BACKEND_ORIGIN; \ No newline at end of file +export const LMEM_BACKEND_ORIGIN = originFromEnv('LMEM_BACKEND_ORIGIN'); +export const LMEM_SCRIPTS_ORIGIN = originFromEnv('LMEM_SCRIPTS_ORIGIN'); \ No newline at end of file diff --git a/src/browser/extension/background/index.js b/src/browser/extension/background/index.js index 711abbfac..7f6c1efc0 100644 --- a/src/browser/extension/background/index.js +++ b/src/browser/extension/background/index.js @@ -14,6 +14,8 @@ import prepareDraftPreview from '../../../app/lmem/draft-preview/main.js'; import { dispatchInitialStateFromBackend } from '../../../app/actions/kraftBackend'; import updateDraftRecommandations from '../../../app/actions/updateDraftRecommandations'; +import {LMEM_BACKEND_ORIGIN, LMEM_SCRIPTS_ORIGIN} from '../../../app/constants/origins'; + /** * FIXME import styles from components instead and let Webpack taking care of them... * @@ -30,12 +32,12 @@ import mainStyles from './../../../app/styles/main.scss'; if(process.env.NODE_ENV !== 'production'){ console.info('NODE_ENV', process.env.NODE_ENV); } -console.info('LMEM_BACKEND_ORIGIN', process.env.LMEM_BACKEND_ORIGIN); - +console.info(`LMEM_BACKEND_ORIGIN "${LMEM_BACKEND_ORIGIN}"`); +console.info(`LMEM_SCRIPTS_ORIGIN "${LMEM_SCRIPTS_ORIGIN}"`); // Load content code when the extension is loaded -const contentCodeP = fetch('./js/content.bundle.js').then(resp => resp.text()); -const draftRecoContentCodeP = fetch('./js/grabDraftRecommandations.js').then(resp => resp.text()); +const contentCodeP = fetch(LMEM_SCRIPTS_ORIGIN + '/js/content.bundle.js').then(resp => resp.text()); +const draftRecoContentCodeP = fetch(LMEM_SCRIPTS_ORIGIN + '/js/grabDraftRecommandations.js').then(resp => resp.text()); configureStore(store => { window.store = store; diff --git a/src/browser/extension/manifest/prod.js b/src/browser/extension/manifest/prod.js index 958f2d817..58137e063 100644 --- a/src/browser/extension/manifest/prod.js +++ b/src/browser/extension/manifest/prod.js @@ -9,7 +9,8 @@ export default Object.assign( 'content_security_policy': csp({ 'directives': { 'default-src': [ - 'https://lmem-craft-backend.cleverapps.io' + 'https://lmem-craft-backend.cleverapps.io', + 'https://ui.lmem.net', ], 'script-src': [ 'https://ui.lmem.net', diff --git a/src/browser/extension/manifest/staging.js b/src/browser/extension/manifest/staging.js index 7416004e4..cb602d0f6 100644 --- a/src/browser/extension/manifest/staging.js +++ b/src/browser/extension/manifest/staging.js @@ -9,7 +9,8 @@ export default Object.assign( 'content_security_policy': csp({ 'directives': { 'default-src': [ - 'https://preprod-lmem-craft-backend.cleverapps.io' + 'https://preprod-lmem-craft-backend.cleverapps.io', + 'https://testing.ui.lmem.net' ], 'script-src': [ 'https://testing.ui.lmem.net', diff --git a/webpack/dev.config.js b/webpack/dev.config.js index 8a139d25e..107f43c97 100644 --- a/webpack/dev.config.js +++ b/webpack/dev.config.js @@ -19,7 +19,8 @@ export default baseConfig({ globals: { 'process.env': { NODE_ENV: '"development"', - LMEM_BACKEND_ORIGIN: '"https://preprod-lmem-craft-backend.cleverapps.io"' + LMEM_BACKEND_ORIGIN: '"https://preprod-lmem-craft-backend.cleverapps.io"', + LMEM_SCRIPTS_ORIGIN: "'.'", // Use local build } } }); diff --git a/webpack/production.config.js b/webpack/production.config.js index 7661a8a82..c8edb84be 100644 --- a/webpack/production.config.js +++ b/webpack/production.config.js @@ -32,7 +32,8 @@ export default baseConfig({ globals: { 'process.env': { NODE_ENV: '"production"', - LMEM_BACKEND_ORIGIN: '"https://lmem-craft-backend.cleverapps.io"' + LMEM_BACKEND_ORIGIN: '"https://lmem-craft-backend.cleverapps.io"', + LMEM_SCRIPTS_ORIGIN: "'https://ui.lmem.net'", } } }); diff --git a/webpack/staging.config.js b/webpack/staging.config.js index 7e15d49c3..e3bf75c1c 100644 --- a/webpack/staging.config.js +++ b/webpack/staging.config.js @@ -22,7 +22,8 @@ export default baseConfig({ globals: { 'process.env': { NODE_ENV: '"staging"', - LMEM_BACKEND_ORIGIN: '"https://preprod-lmem-craft-backend.cleverapps.io"' + LMEM_BACKEND_ORIGIN: '"https://preprod-lmem-craft-backend.cleverapps.io"', + LMEM_SCRIPTS_ORIGIN: "'https://testing.ui.lmem.net'", } } }); From c381c745b2c93e99e4b5967f75011cd7ab76a627 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Wed, 28 Sep 2016 23:21:02 +0200 Subject: [PATCH 23/29] fix test with missing LMEM_SCRIPTS_ORIGIN env --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7c9e64c6e..c9e980e4e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build:staging": "gulp build:staging", "build:dev": "gulp build:dev", "clean": "rm -rf build", - "test": "LMEM_BACKEND_ORIGIN='' NODE_ENV=test mocha --compilers js:babel-core/register test/app --recursive", + "test": "LMEM_BACKEND_ORIGIN='' LMEM_SCRIPTS_ORIGIN='.' NODE_ENV=test mocha --compilers js:babel-core/register test/app --recursive", "lint": "git diff-index --name-only --cached HEAD | grep \\.js$ | xargs eslint --fix", "deploy:production": "gulp deploy:production", "deploy:staging": "gulp deploy:staging" From a3834c32cee61bb67be64c39bac076211db17103 Mon Sep 17 00:00:00 2001 From: Benjamin MENANT Date: Fri, 30 Sep 2016 14:25:38 +0200 Subject: [PATCH 24/29] Revert "Delay extension display even when an alternative is available quickly" --- src/app/content/reducers/index.js | 2 +- src/browser/extension/content/index.js | 181 ++++++---------------- src/browser/extension/manifest/dev.js | 2 +- src/browser/extension/manifest/prod.js | 2 +- src/browser/extension/manifest/staging.js | 2 +- 5 files changed, 54 insertions(+), 135 deletions(-) diff --git a/src/app/content/reducers/index.js b/src/app/content/reducers/index.js index 056183749..8d9087fc9 100644 --- a/src/app/content/reducers/index.js +++ b/src/app/content/reducers/index.js @@ -16,7 +16,7 @@ export default function (state = {}, action) { switch (type) { case ALTERNATIVE_FOUND: { const { alternative } = action; - return state.set('alternative', alternative).set('reduced', false); + return state.set('alternative', alternative); } case REDUCE_ALTERNATIVE_IFRAME: diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index a40e2de8c..012683358 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -14,101 +14,12 @@ import portCommunication from '../../../app/content/portCommunication'; const IFRAME_EXTENDED_HEIGHT = '255px'; const IFRAME_REDUCED_HEIGHT = '60px'; -const EXTENSION_STATE_SHOW_LOADING = 'EXTENSION_STATE_SHOW_LOADING'; -const EXTENSION_STATE_SHOW_ALTERNATIVE = 'EXTENSION_STATE_SHOW_ALTERNATIVE'; - -const AFTER_DOMCOMPLETE_DELAY = 5000; -const AFTER_LOADEND_DELAY = 1000; -const LOADING_SCREEN_DELAY = 4000; - -/* - LIB -*/ -function createExtensionIframe(reduced, style, onLoad){ - const iframe = document.createElement('iframe'); - iframe.id = 'lmemFrame'; - iframe.width = '100%'; - iframe.height = reduced ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; - iframe.style.position = 'fixed'; - iframe.style.bottom = '0px'; - iframe.style.left = '0px'; - iframe.style.right = '0px'; - iframe.style.zIndex = '999999999'; - iframe.srcdoc = ` - - - - - - - - `; - - iframe.onload = onLoad; - - return iframe; -} - - -/* - SETUP -*/ - -const DOMCompleteP = Promise.resolve(); // because the content script is loaded at "document_end" - -const DOMCompletePlusDelayP = DOMCompleteP.then(() => { - return new Promise(resolve => { - const {navigationStart, domContentLoadedEventStart} = performance.timing; - - const diff = domContentLoadedEventStart - navigationStart; - - if(diff >= AFTER_DOMCOMPLETE_DELAY) - resolve(); - else - setTimeout(resolve, AFTER_DOMCOMPLETE_DELAY - diff); - }); -}); - - -const LoadEndP = new Promise(resolve => { - document.addEventListener('load', resolve); -}); - -const LoadEndPlusDelayP = LoadEndP.then(() => { - return new Promise(resolve => { - const {navigationStart, loadEventStart} = performance.timing; - - const diff = loadEventStart - navigationStart; - - if(diff >= AFTER_LOADEND_DELAY) - resolve(); - else - setTimeout(resolve, AFTER_LOADEND_DELAY - diff); - }); -}); - -// Wait for some time before showing the extension to the user in loading mode -const CanShowIframeLoadingP = Promise.race([DOMCompletePlusDelayP, LoadEndPlusDelayP]); - -// User research showed that the LMEM loading screen is important so people don't -// think the LMEM iframe is an ad. -// Wait for some time loading before showing an alternative. -const CanShowAlternativeIfAvailableP = process.env.NODE_ENV === 'development' ? - Promise.resolve() : // otherwise the delay is annoying when developing - CanShowIframeLoadingP.then(() => { - return new Promise(resolve => { - setTimeout(resolve, LOADING_SCREEN_DELAY); - }); - }); - - - // create redux store const store = createStore( rootReducer, new Record({ open: true, - reduced: true, + reduced: false, preferenceScreenPanel: undefined, // preference screen close alternative: undefined, deactivatedWebsites: new ImmutableSet(), @@ -116,9 +27,6 @@ const store = createStore( })() ); - - - // reach back to background script chrome.runtime.onConnect.addListener(function listener(portToBackground) { portCommunication.port = portToBackground; @@ -131,53 +39,64 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { switch (type) { case 'init': const { style, deactivatedWebsites, onInstalledDetails } = msg; + const reduced = store.getState().get('reduced'); + const lmemContentContainerP = new Promise(resolve => { + const iframe = document.createElement('iframe'); + iframe.id = 'lmemFrame'; + iframe.width = '100%'; + iframe.height = reduced ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; + iframe.style.position = 'fixed'; + iframe.style.bottom = 0; + iframe.style.left = 0; + iframe.style.right = 0; + iframe.style.zIndex = 2147483647; // Max z-index value (signed 32bits integer) + iframe.style.background = '#FDF6E3'; // UI bg color (avoid having a transparent iframe after injection) + iframe.style.border = 'none'; + iframe.style.transition = 'height .1s'; + iframe.style.boxShadow = '0 0 15px #888'; + iframe.srcdoc = ` + + + + + + + + `; + + iframe.onload = function () { + resolve(iframe.contentDocument.body); + }; + document.body.appendChild(iframe); + + store.subscribe(() => { + const state = store.getState(); + + if (!state.get('open')) { + iframe.remove(); + } + else { + iframe.height = state.get('reduced') ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; + } + + }); + }); store.dispatch(updateDeactivatedWebsites(new ImmutableSet(deactivatedWebsites))); store.dispatch(updateInstalledDetails(immutableFromJS(onInstalledDetails))); - // Let the page load a bit before showing the iframe in loading mode - CanShowIframeLoadingP - .then(() => { - - return new Promise(resolve => { - const iframe = createExtensionIframe( - store.getState().get('reduced'), - style, - () => { resolve(iframe.contentDocument.body); } - ); - - document.body.appendChild(iframe); - - store.subscribe(() => { - const state = store.getState(); - - if (!state.get('open')) { - iframe.remove(); - } - else { - iframe.height = state.get('reduced') ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; - } - - }); - }) - .then(lmemContainer => { - render(, lmemContainer); - }); - + lmemContentContainerP.then(lmemContentContainer => { + render( + , + lmemContentContainer + ); }); - break; case 'alternative': const { alternative } = msg; - // console.log('alternative in content', alternative); - - // Even if the alternative arrived early, let the page load a bit before - // showing the iframe in loading mode - CanShowAlternativeIfAvailableP - .then(() => { - store.dispatch(alternativeFound(alternative)); - }); + // console.log('alternative in content', alternative); + store.dispatch(alternativeFound(alternative)); break; default: console.error('Content script: unrecognized message type from background', type, msg); diff --git a/src/browser/extension/manifest/dev.js b/src/browser/extension/manifest/dev.js index 43a8cfde9..166748c5d 100644 --- a/src/browser/extension/manifest/dev.js +++ b/src/browser/extension/manifest/dev.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from 'content-security-policy-builder'; +import csp from "content-security-policy-builder"; export default Object.assign( {}, diff --git a/src/browser/extension/manifest/prod.js b/src/browser/extension/manifest/prod.js index 958f2d817..9e39d278d 100644 --- a/src/browser/extension/manifest/prod.js +++ b/src/browser/extension/manifest/prod.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from 'content-security-policy-builder'; +import csp from "content-security-policy-builder"; export default Object.assign( {}, diff --git a/src/browser/extension/manifest/staging.js b/src/browser/extension/manifest/staging.js index 7416004e4..5547cd254 100644 --- a/src/browser/extension/manifest/staging.js +++ b/src/browser/extension/manifest/staging.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from 'content-security-policy-builder'; +import csp from "content-security-policy-builder"; export default Object.assign( {}, From 29d8caa8952fd646a4c1e7d610910ab0a30a9b09 Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 13 Sep 2016 15:35:44 +0200 Subject: [PATCH 25/29] Delay extension display even when an alternative is available quickly --- src/browser/extension/content/index.js | 181 ++++++++++++++++++------- 1 file changed, 132 insertions(+), 49 deletions(-) diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index 012683358..66e60ca64 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -14,6 +14,97 @@ import portCommunication from '../../../app/content/portCommunication'; const IFRAME_EXTENDED_HEIGHT = '255px'; const IFRAME_REDUCED_HEIGHT = '60px'; +const EXTENSION_STATE_SHOW_LOADING = 'EXTENSION_STATE_SHOW_LOADING'; +const EXTENSION_STATE_SHOW_ALTERNATIVE = 'EXTENSION_STATE_SHOW_ALTERNATIVE'; + +const AFTER_DOMCOMPLETE_DELAY = 5000; +const AFTER_LOADEND_DELAY = 1000; +const LOADING_SCREEN_DELAY = 4000; + +/* + LIB +*/ +function createExtensionIframe(reduced, style, onLoad){ + const iframe = document.createElement('iframe'); + iframe.id = 'lmemFrame'; + iframe.width = '100%'; + iframe.height = reduced ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; + iframe.style.position = 'fixed'; + iframe.style.bottom = 0; + iframe.style.left = 0; + iframe.style.right = 0; + iframe.style.zIndex = 2147483647; // Max z-index value (signed 32bits integer) + iframe.style.background = '#FDF6E3'; // UI bg color (avoid having a transparent iframe after injection) + iframe.style.border = 'none'; + iframe.style.transition = 'height .1s'; + iframe.style.boxShadow = '0 0 15px #888'; + iframe.srcdoc = ` + + + + + + + + `; + + iframe.onload = onLoad; + + return iframe; +} + + +/* + SETUP +*/ + +const DOMCompleteP = Promise.resolve(); // because the content script is loaded at "document_end" + +const DOMCompletePlusDelayP = DOMCompleteP.then(() => { + return new Promise(resolve => { + const {navigationStart, domContentLoadedEventStart} = performance.timing; + + const diff = domContentLoadedEventStart - navigationStart; + + if(diff >= AFTER_DOMCOMPLETE_DELAY) + resolve(); + else + setTimeout(resolve, AFTER_DOMCOMPLETE_DELAY - diff); + }); +}); + + +const LoadEndP = new Promise(resolve => { + document.addEventListener('load', resolve); +}); + +const LoadEndPlusDelayP = LoadEndP.then(() => { + return new Promise(resolve => { + const {navigationStart, loadEventStart} = performance.timing; + + const diff = loadEventStart - navigationStart; + + if(diff >= AFTER_LOADEND_DELAY) + resolve(); + else + setTimeout(resolve, AFTER_LOADEND_DELAY - diff); + }); +}); + +// Wait for some time before showing the extension to the user in loading mode +const CanShowIframeLoadingP = Promise.race([DOMCompletePlusDelayP, LoadEndPlusDelayP]); + +// User research showed that the LMEM loading screen is important so people don't +// think the LMEM iframe is an ad. +// Wait for some time loading before showing an alternative. +const CanShowAlternativeIfAvailableP = CanShowIframeLoadingP.then(() => { + return new Promise(resolve => { + setTimeout(resolve, LOADING_SCREEN_DELAY); + }); +}); + + + // create redux store const store = createStore( rootReducer, @@ -27,6 +118,9 @@ const store = createStore( })() ); + + + // reach back to background script chrome.runtime.onConnect.addListener(function listener(portToBackground) { portCommunication.port = portToBackground; @@ -39,64 +133,53 @@ chrome.runtime.onConnect.addListener(function listener(portToBackground) { switch (type) { case 'init': const { style, deactivatedWebsites, onInstalledDetails } = msg; - const reduced = store.getState().get('reduced'); - const lmemContentContainerP = new Promise(resolve => { - const iframe = document.createElement('iframe'); - iframe.id = 'lmemFrame'; - iframe.width = '100%'; - iframe.height = reduced ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; - iframe.style.position = 'fixed'; - iframe.style.bottom = 0; - iframe.style.left = 0; - iframe.style.right = 0; - iframe.style.zIndex = 2147483647; // Max z-index value (signed 32bits integer) - iframe.style.background = '#FDF6E3'; // UI bg color (avoid having a transparent iframe after injection) - iframe.style.border = 'none'; - iframe.style.transition = 'height .1s'; - iframe.style.boxShadow = '0 0 15px #888'; - iframe.srcdoc = ` - - - - - - - - `; - - iframe.onload = function () { - resolve(iframe.contentDocument.body); - }; - document.body.appendChild(iframe); - - store.subscribe(() => { - const state = store.getState(); - - if (!state.get('open')) { - iframe.remove(); - } - else { - iframe.height = state.get('reduced') ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; - } - - }); - }); store.dispatch(updateDeactivatedWebsites(new ImmutableSet(deactivatedWebsites))); store.dispatch(updateInstalledDetails(immutableFromJS(onInstalledDetails))); - lmemContentContainerP.then(lmemContentContainer => { - render( - , - lmemContentContainer - ); + // Let the page load a bit before showing the iframe in loading mode + CanShowIframeLoadingP + .then(() => { + + return new Promise(resolve => { + const iframe = createExtensionIframe( + store.getState().get('reduced'), + style, + () => { resolve(iframe.contentDocument.body); } + ); + + document.body.appendChild(iframe); + + store.subscribe(() => { + const state = store.getState(); + + if (!state.get('open')) { + iframe.remove(); + } + else { + iframe.height = state.get('reduced') ? IFRAME_REDUCED_HEIGHT : IFRAME_EXTENDED_HEIGHT; + } + + }); + }) + .then(lmemContainer => { + render(, lmemContainer); + }); + }); + break; case 'alternative': const { alternative } = msg; - // console.log('alternative in content', alternative); - store.dispatch(alternativeFound(alternative)); + + // Even if the alternative arrived early, let the page load a bit before + // showing the iframe in loading mode + CanShowAlternativeIfAvailableP + .then(() => { + store.dispatch(alternativeFound(alternative)); + }); + break; default: console.error('Content script: unrecognized message type from background', type, msg); From 0cca5ed13b147168288b6b09098ffb4f73d1b53f Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 27 Sep 2016 13:49:35 +0200 Subject: [PATCH 26/29] post-rebase fixes --- src/browser/extension/manifest/dev.js | 2 +- src/browser/extension/manifest/prod.js | 2 +- src/browser/extension/manifest/staging.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/extension/manifest/dev.js b/src/browser/extension/manifest/dev.js index 166748c5d..43a8cfde9 100644 --- a/src/browser/extension/manifest/dev.js +++ b/src/browser/extension/manifest/dev.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from "content-security-policy-builder"; +import csp from 'content-security-policy-builder'; export default Object.assign( {}, diff --git a/src/browser/extension/manifest/prod.js b/src/browser/extension/manifest/prod.js index 9e39d278d..958f2d817 100644 --- a/src/browser/extension/manifest/prod.js +++ b/src/browser/extension/manifest/prod.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from "content-security-policy-builder"; +import csp from 'content-security-policy-builder'; export default Object.assign( {}, diff --git a/src/browser/extension/manifest/staging.js b/src/browser/extension/manifest/staging.js index 5547cd254..7416004e4 100644 --- a/src/browser/extension/manifest/staging.js +++ b/src/browser/extension/manifest/staging.js @@ -1,5 +1,5 @@ import base from './base.js'; -import csp from "content-security-policy-builder"; +import csp from 'content-security-policy-builder'; export default Object.assign( {}, From a6ca2fcfc0e2d2533eea2df80521c25cca8fe461 Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 27 Sep 2016 13:54:40 +0200 Subject: [PATCH 27/29] extension is reduced until a recommandation is found --- src/app/content/reducers/index.js | 2 +- src/browser/extension/content/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/content/reducers/index.js b/src/app/content/reducers/index.js index 8d9087fc9..056183749 100644 --- a/src/app/content/reducers/index.js +++ b/src/app/content/reducers/index.js @@ -16,7 +16,7 @@ export default function (state = {}, action) { switch (type) { case ALTERNATIVE_FOUND: { const { alternative } = action; - return state.set('alternative', alternative); + return state.set('alternative', alternative).set('reduced', false); } case REDUCE_ALTERNATIVE_IFRAME: diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index 66e60ca64..0e8453ad4 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -110,7 +110,7 @@ const store = createStore( rootReducer, new Record({ open: true, - reduced: false, + reduced: true, preferenceScreenPanel: undefined, // preference screen close alternative: undefined, deactivatedWebsites: new ImmutableSet(), From a2b5d6312e199d2e2f61b5afaa090f2771229401 Mon Sep 17 00:00:00 2001 From: David Bruant Date: Tue, 27 Sep 2016 13:59:22 +0200 Subject: [PATCH 28/29] Don't delay showing the iframe when NODE_ENV is development --- src/browser/extension/content/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/browser/extension/content/index.js b/src/browser/extension/content/index.js index 0e8453ad4..40e65902a 100644 --- a/src/browser/extension/content/index.js +++ b/src/browser/extension/content/index.js @@ -97,11 +97,13 @@ const CanShowIframeLoadingP = Promise.race([DOMCompletePlusDelayP, LoadEndPlusDe // User research showed that the LMEM loading screen is important so people don't // think the LMEM iframe is an ad. // Wait for some time loading before showing an alternative. -const CanShowAlternativeIfAvailableP = CanShowIframeLoadingP.then(() => { - return new Promise(resolve => { - setTimeout(resolve, LOADING_SCREEN_DELAY); +const CanShowAlternativeIfAvailableP = process.env.NODE_ENV === 'development' ? + Promise.resolve() : // otherwise the delay is annoying when developing + CanShowIframeLoadingP.then(() => { + return new Promise(resolve => { + setTimeout(resolve, LOADING_SCREEN_DELAY); + }); }); -}); From a3f6ce9cfc07cabb5820fa9de38ee30034a422d9 Mon Sep 17 00:00:00 2001 From: Benjamin Menant Date: Fri, 30 Sep 2016 12:15:54 +0200 Subject: [PATCH 29/29] Heap loads with proper env appIds, fix #63 --- src/app/events/trackEvents.js | 12 ++++++++---- src/browser/extension/background/index.js | 11 ++++++++++- src/browser/extension/heap/index.js | 9 +++++---- src/lib/heap.js | 6 ++++-- webpack/production.config.js | 1 + webpack/staging.config.js | 1 + 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/app/events/trackEvents.js b/src/app/events/trackEvents.js index 924ba1a64..977155d74 100644 --- a/src/app/events/trackEvents.js +++ b/src/app/events/trackEvents.js @@ -1,4 +1,4 @@ -import { trackHeapEvent } from '../actions/heap'; +// import { trackHeapEvent } from '../actions/heap'; // Arbitrary set max payload size // @TODO find a nicer way to handle the error @@ -24,7 +24,12 @@ const MAX_PAYLOAD_SIZE = 10000; * Could be implemented in a much nicer way though. */ const events = store => next => action => { - // Check payload size to avoid HTTP 414 Request-URI Too Large url + if (!window.heap) { + console.log(`Heap analytics disabled: ignore tracking of "${action.type}"`); + return next(action); + } + + // Check payload size to avoid HTTP 414 Request-URI Too Large url const payloadSize = JSON.stringify(action).length; if (payloadSize > MAX_PAYLOAD_SIZE) { console.log('Payload size too large', payloadSize); @@ -32,8 +37,7 @@ const events = store => next => action => { } else { window.heap.track(action.type, action.payload); } - const result = next(action); - return result; + return next(action); }; export default events; \ No newline at end of file diff --git a/src/browser/extension/background/index.js b/src/browser/extension/background/index.js index 7f6c1efc0..e9862c92c 100644 --- a/src/browser/extension/background/index.js +++ b/src/browser/extension/background/index.js @@ -2,7 +2,7 @@ // Early imports with high priority stuff involved, such as event listeners creation import onInstalled from '../../../app/actions/install'; -import heap from './../../../lib/heap'; +import loadHeap from '../../../lib/heap'; import configureStore from './../../../app/store/configureStore'; @@ -35,6 +35,15 @@ if(process.env.NODE_ENV !== 'production'){ console.info(`LMEM_BACKEND_ORIGIN "${LMEM_BACKEND_ORIGIN}"`); console.info(`LMEM_SCRIPTS_ORIGIN "${LMEM_SCRIPTS_ORIGIN}"`); +const heapAppId = process.env.HEAP_APPID; +if (typeof heapAppId === 'string') { + console.info(`Heap loading with appId "${heapAppId}"`); + loadHeap(heapAppId); +} +else { + console.warn('Heap analytics disabled: assuming "process.env.HEAP_APPID" is deliberately not defined.'); +} + // Load content code when the extension is loaded const contentCodeP = fetch(LMEM_SCRIPTS_ORIGIN + '/js/content.bundle.js').then(resp => resp.text()); const draftRecoContentCodeP = fetch(LMEM_SCRIPTS_ORIGIN + '/js/grabDraftRecommandations.js').then(resp => resp.text()); diff --git a/src/browser/extension/heap/index.js b/src/browser/extension/heap/index.js index bad70f298..c0f55c7a7 100644 --- a/src/browser/extension/heap/index.js +++ b/src/browser/extension/heap/index.js @@ -1,14 +1,15 @@ import React from 'react'; import { render } from 'react-dom'; -import Root from 'app/containers/Root'; -import configureStore from 'app/store/configureStore'; +import Root from '../../../app/containers/Root'; +import configureStore from '../../../app/store/configureStore'; +import { LMEM_SCRIPTS_ORIGIN } from '../../../app/constants/origins'; configureStore(store => { window.addEventListener('load', () => { console.log('Injecting heap analytics'); - let injectScript = document.createElement('script'); - injectScript.src('https://ui.lmem.net/js/heap.js'); + const injectScript = document.createElement('script'); + injectScript.src(LMEM_SCRIPTS_ORIGIN + '/js/heap.js'); document.getElementsByTagName('head')[0].appendChild(injectScript); render( diff --git a/src/lib/heap.js b/src/lib/heap.js index 6b2bb0cfe..963b78cfd 100644 --- a/src/lib/heap.js +++ b/src/lib/heap.js @@ -1,2 +1,4 @@ -window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=t.forceSSL||"https:"===document.location.protocol,a=document.createElement("script");a.type="text/javascript",a.async=!0,a.src=(r?"https:":"http:")+"//cdn.heapanalytics.com/js/heap-"+e+".js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(a,n);for(var o=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","removeEventProperty","setEventProperties","track","unsetEventProperty"],c=0;c