Merge pull request #27 from ueokande/react-settings

Use React in settings
jh-changes
Shin'ya Ueoka 7 years ago committed by GitHub
commit 447466808f
  1. 9
      .eslintrc
  2. 370
      package-lock.json
  3. 7
      package.json
  4. 0
      src/background/actions/settings.js
  5. 12
      src/background/components/background.js
  6. 5
      src/content/actions/index.js
  7. 9
      src/content/actions/input.js
  8. 16
      src/content/components/keymapper.js
  9. 4
      src/content/index.js
  10. 3
      src/content/reducers/index.js
  11. 5
      src/content/reducers/input.js
  12. 16
      src/settings/actions/setting.js
  13. 91
      src/settings/components/index.jsx
  14. 45
      src/settings/components/setting.js
  15. 2
      src/settings/components/site.scss
  16. 10
      src/settings/index.html
  17. 15
      src/settings/index.js
  18. 18
      src/settings/index.jsx
  19. 12
      src/settings/reducers/setting.js
  20. 1
      src/shared/default-settings.js
  21. 18
      src/shared/store/provider.jsx
  22. 12
      test/settings/reducers/setting.test.js
  23. 6
      webpack.config.js

@ -5,13 +5,14 @@
"browser" : true,
"webextensions": true
},
"plugins": ["react"],
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
"extends": [ "eslint:all" ],
"extends": [ "eslint:all", "plugin:react/recommended" ],
"rules": {
"array-bracket-newline": ["error", { "multiline": true }],
"array-element-newline": "off",
@ -27,6 +28,7 @@
"function-paren-newline": "off",
"id-length": "off",
"indent": ["error", 2],
"jsx-quotes": ["error", "prefer-single"],
"max-statements": ["error", 15],
"multiline-ternary": "off",
"newline-after-var": "off",
@ -56,6 +58,9 @@
"sort-imports": "off",
"sort-keys": "off",
"sort-vars": "off",
"space-before-function-paren": ["error", "never"]
"space-before-function-paren": ["error", "never"],
"react/jsx-indent": ["error", 2],
"react/prop-types": "off",
}
}

370
package-lock.json generated

@ -180,6 +180,16 @@
"integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
"dev": true
},
"array-includes": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
"integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
"dev": true,
"requires": {
"define-properties": "1.1.2",
"es-abstract": "1.9.0"
}
},
"array-slice": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
@ -219,6 +229,12 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
"dev": true
},
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
@ -399,6 +415,47 @@
"trim-right": "1.0.1"
}
},
"babel-helper-builder-react-jsx": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
"integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=",
"dev": true,
"requires": {
"babel-runtime": "6.26.0",
"babel-types": "6.26.0",
"esutils": "2.0.2"
},
"dependencies": {
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
"core-js": "2.5.0",
"regenerator-runtime": "0.11.0"
}
},
"babel-types": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
"integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
"dev": true,
"requires": {
"babel-runtime": "6.26.0",
"esutils": "2.0.2",
"lodash": "4.17.4",
"to-fast-properties": "1.0.3"
}
},
"regenerator-runtime": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
"integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==",
"dev": true
}
}
},
"babel-helper-call-delegate": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
@ -530,6 +587,18 @@
"babel-runtime": "6.25.0"
}
},
"babel-plugin-syntax-flow": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
"integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=",
"dev": true
},
"babel-plugin-syntax-jsx": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
"dev": true
},
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
@ -764,6 +833,56 @@
"regexpu-core": "2.0.0"
}
},
"babel-plugin-transform-flow-strip-types": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz",
"integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=",
"dev": true,
"requires": {
"babel-plugin-syntax-flow": "6.18.0",
"babel-runtime": "6.25.0"
}
},
"babel-plugin-transform-react-display-name": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz",
"integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=",
"dev": true,
"requires": {
"babel-runtime": "6.25.0"
}
},
"babel-plugin-transform-react-jsx": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
"integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=",
"dev": true,
"requires": {
"babel-helper-builder-react-jsx": "6.26.0",
"babel-plugin-syntax-jsx": "6.18.0",
"babel-runtime": "6.25.0"
}
},
"babel-plugin-transform-react-jsx-self": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz",
"integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=",
"dev": true,
"requires": {
"babel-plugin-syntax-jsx": "6.18.0",
"babel-runtime": "6.25.0"
}
},
"babel-plugin-transform-react-jsx-source": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz",
"integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=",
"dev": true,
"requires": {
"babel-plugin-syntax-jsx": "6.18.0",
"babel-runtime": "6.25.0"
}
},
"babel-plugin-transform-regenerator": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz",
@ -826,6 +945,29 @@
"babel-plugin-transform-regenerator": "6.24.1"
}
},
"babel-preset-flow": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz",
"integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=",
"dev": true,
"requires": {
"babel-plugin-transform-flow-strip-types": "6.22.0"
}
},
"babel-preset-react": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz",
"integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=",
"dev": true,
"requires": {
"babel-plugin-syntax-jsx": "6.18.0",
"babel-plugin-transform-react-display-name": "6.25.0",
"babel-plugin-transform-react-jsx": "6.24.1",
"babel-plugin-transform-react-jsx-self": "6.22.0",
"babel-plugin-transform-react-jsx-source": "6.22.0",
"babel-preset-flow": "6.23.0"
}
},
"babel-register": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz",
@ -1905,6 +2047,16 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"define-properties": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
"integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
"dev": true,
"requires": {
"foreach": "2.0.5",
"object-keys": "1.0.11"
}
},
"defined": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
@ -2129,6 +2281,15 @@
"integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=",
"dev": true
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"dev": true,
"requires": {
"iconv-lite": "0.4.19"
}
},
"engine.io": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz",
@ -2259,6 +2420,38 @@
"is-arrayish": "0.2.1"
}
},
"es-abstract": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz",
"integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==",
"dev": true,
"requires": {
"es-to-primitive": "1.1.1",
"function-bind": "1.1.1",
"has": "1.0.1",
"is-callable": "1.1.3",
"is-regex": "1.0.4"
},
"dependencies": {
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
}
}
},
"es-to-primitive": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
"integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
"dev": true,
"requires": {
"is-callable": "1.1.3",
"is-date-object": "1.0.1",
"is-symbol": "1.0.1"
}
},
"es5-ext": {
"version": "0.10.27",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.27.tgz",
@ -2453,6 +2646,18 @@
}
}
},
"eslint-plugin-react": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz",
"integrity": "sha512-tvjU9u3VqmW2vVuYnE8Qptq+6ji4JltjOjJ9u7VAOxVYkUkyBZWRvNYKbDv5fN+L6wiA+4we9+qQahZ0m63XEA==",
"dev": true,
"requires": {
"doctrine": "2.0.0",
"has": "1.0.1",
"jsx-ast-utils": "2.0.1",
"prop-types": "15.6.0"
}
},
"eslint-scope": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
@ -2668,6 +2873,29 @@
"integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=",
"dev": true
},
"fbjs": {
"version": "0.8.16",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
"integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=",
"dev": true,
"requires": {
"core-js": "1.2.7",
"isomorphic-fetch": "2.2.1",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
"promise": "7.3.1",
"setimmediate": "1.0.5",
"ua-parser-js": "0.7.14"
},
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
"dev": true
}
}
},
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@ -2774,6 +3002,12 @@
"for-in": "1.0.2"
}
},
"foreach": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@ -3554,6 +3788,18 @@
"builtin-modules": "1.1.1"
}
},
"is-callable": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
"integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
"dev": true
},
"is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
},
"is-dotfile": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
@ -3682,6 +3928,15 @@
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
"dev": true
},
"is-regex": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
"has": "1.0.1"
}
},
"is-resolvable": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
@ -3706,6 +3961,12 @@
"html-comment-regex": "1.1.1"
}
},
"is-symbol": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
"integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
"dev": true
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@ -3745,6 +4006,16 @@
"isarray": "1.0.0"
}
},
"isomorphic-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"dev": true,
"requires": {
"node-fetch": "1.7.3",
"whatwg-fetch": "2.0.3"
}
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@ -3863,6 +4134,15 @@
}
}
},
"jsx-ast-utils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
"integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
"dev": true,
"requires": {
"array-includes": "3.0.3"
}
},
"karma": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/karma/-/karma-1.7.0.tgz",
@ -4652,6 +4932,16 @@
"lower-case": "1.1.4"
}
},
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"dev": true,
"requires": {
"encoding": "0.1.12",
"is-stream": "1.1.0"
}
},
"node-gyp": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
@ -4866,6 +5156,12 @@
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
"dev": true
},
"object-keys": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
"integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
"dev": true
},
"object.omit": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
@ -5766,6 +6062,26 @@
"integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
"dev": true
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"dev": true,
"requires": {
"asap": "2.0.6"
}
},
"prop-types": {
"version": "15.6.0",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz",
"integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=",
"dev": true,
"requires": {
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
"object-assign": "4.1.1"
}
},
"prr": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz",
@ -5912,6 +6228,30 @@
}
}
},
"react": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.0.0.tgz",
"integrity": "sha1-zn348ZQbA28Cssyp29DLHw6FXi0=",
"dev": true,
"requires": {
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
"prop-types": "15.6.0"
}
},
"react-dom": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.0.0.tgz",
"integrity": "sha1-nMMHnD3NcNTG4BuEqrKn40wwP1g=",
"dev": true,
"requires": {
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
"prop-types": "15.6.0"
}
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@ -6853,15 +7193,6 @@
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
}
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@ -6895,6 +7226,15 @@
}
}
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
@ -7176,6 +7516,12 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
"ua-parser-js": {
"version": "0.7.14",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz",
"integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=",
"dev": true
},
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
@ -7475,6 +7821,12 @@
"source-map": "0.5.6"
}
},
"whatwg-fetch": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
"integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=",
"dev": true
},
"whet.extend": {
"version": "0.9.9",
"resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz",

@ -3,7 +3,7 @@
"description": "Vim vixen",
"scripts": {
"start": "webpack -w --debug",
"lint": "eslint src",
"lint": "eslint --ext .jsx,.js src",
"test": "karma start"
},
"repository": {
@ -20,10 +20,13 @@
"babel-cli": "^6.24.1",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.1",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"chai": "^4.1.1",
"css-loader": "^0.28.4",
"eslint": "^4.7.0",
"eslint-plugin-react": "^7.4.0",
"html-webpack-plugin": "^2.30.1",
"karma": "^1.7.0",
"karma-firefox-launcher": "^1.0.1",
@ -34,6 +37,8 @@
"karma-webpack": "^2.0.4",
"mocha": "^3.5.0",
"node-sass": "^4.5.3",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"webpack": "^3.5.3"

@ -22,15 +22,7 @@ export default class BackgroundComponent {
}
update() {
let state = this.store.getState();
this.updateSettings(state);
}
updateSettings(setting) {
if (!setting.settings.json) {
return;
}
this.settings = JSON.parse(setting.settings.json);
this.settings = this.store.getState();
}
onMessage(message, sender) {
@ -58,7 +50,7 @@ export default class BackgroundComponent {
});
});
case messages.SETTINGS_QUERY:
return Promise.resolve(this.store.getState().settings);
return Promise.resolve(this.store.getState().value);
case messages.CONSOLE_QUERY_COMPLETIONS:
return commands.complete(message.text, this.settings);
case messages.SETTINGS_RELOAD:

@ -2,16 +2,13 @@ export default {
// User input
INPUT_KEY_PRESS: 'input.key,press',
INPUT_CLEAR_KEYS: 'input.clear.keys',
INPUT_SET_KEYMAPS: 'input.set,keymaps',
INPUT_SET_KEYMAPS: 'input.set.keymaps',
// Completion
COMPLETION_SET_ITEMS: 'completion.set.items',
COMPLETION_SELECT_NEXT: 'completions.select.next',
COMPLETION_SELECT_PREV: 'completions.select.prev',
// Settings
SETTING_SET_SETTINGS: 'setting.set.settings',
// Follow
FOLLOW_ENABLE: 'follow.enable',
FOLLOW_DISABLE: 'follow.disable',

@ -20,4 +20,11 @@ const clearKeys = () => {
};
};
export { keyPress, clearKeys };
const setKeymaps = (keymaps) => {
return {
type: actions.INPUT_SET_KEYMAPS,
keymaps,
};
};
export { keyPress, clearKeys, setKeymaps };

@ -10,14 +10,10 @@ export default class KeymapperComponent {
}
key(key, ctrl) {
let keymaps = this.keymaps();
if (!keymaps) {
return;
}
this.store.dispatch(inputActions.keyPress(key, ctrl));
let input = this.store.getState().input;
let matched = Object.keys(keymaps).filter((keyStr) => {
let matched = Object.keys(input.keymaps).filter((keyStr) => {
return keyStr.startsWith(input.keys);
});
if (matched.length === 0) {
@ -27,17 +23,9 @@ export default class KeymapperComponent {
matched.length === 1 && input.keys !== matched[0]) {
return true;
}
let operation = keymaps[matched];
let operation = input.keymaps[matched];
this.store.dispatch(operationActions.exec(operation));
this.store.dispatch(inputActions.clearKeys());
return true;
}
keymaps() {
let settings = this.store.getState().setting.settings;
if (!settings || !settings.json) {
return null;
}
return JSON.parse(settings.json).keymaps;
}
}

@ -1,6 +1,6 @@
import './console-frame.scss';
import * as consoleFrames from './console-frames';
import * as settingActions from 'settings/actions/setting';
import * as inputActions from './actions/input';
import { createStore } from 'shared/store';
import ContentInputComponent from 'content/components/content-input';
import KeymapperComponent from 'content/components/keymapper';
@ -34,7 +34,7 @@ const reloadSettings = () => {
return browser.runtime.sendMessage({
type: messages.SETTINGS_QUERY,
}).then((settings) => {
store.dispatch(settingActions.set(settings));
store.dispatch(inputActions.setKeymaps(settings.keymaps));
});
};

@ -1,18 +1,15 @@
import settingReducer from 'settings/reducers/setting';
import inputReducer from './input';
import followReducer from './follow';
// Make setting reducer instead of re-use
const defaultState = {
input: inputReducer(undefined, {}),
setting: settingReducer(undefined, {}),
follow: followReducer(undefined, {}),
};
export default function reducer(state = defaultState, action = {}) {
return Object.assign({}, state, {
input: inputReducer(state.input, action),
setting: settingReducer(state.setting, action),
follow: followReducer(state.follow, action),
});
}

@ -2,6 +2,7 @@ import actions from 'content/actions';
const defaultState = {
keys: '',
keymaps: {},
};
export default function reducer(state = defaultState, action = {}) {
@ -14,6 +15,10 @@ export default function reducer(state = defaultState, action = {}) {
return Object.assign({}, state, {
keys: '',
});
case actions.INPUT_SET_KEYMAPS:
return Object.assign({}, state, {
keymaps: action.keymaps,
});
default:
return state;
}

@ -3,9 +3,9 @@ import messages from 'shared/messages';
import DefaultSettings from 'shared/default-settings';
const load = () => {
return browser.storage.local.get('settings').then((value) => {
if (value.settings) {
return set(value.settings);
return browser.storage.local.get('settings').then(({ settings }) => {
if (settings) {
return set(settings);
}
return set(DefaultSettings);
}, console.error);
@ -13,10 +13,12 @@ const load = () => {
const save = (settings) => {
return browser.storage.local.set({
settings
settings,
}).then(() => {
return browser.runtime.sendMessage({
type: messages.SETTINGS_RELOAD
}).then(() => {
return set(settings);
});
});
};
@ -24,8 +26,10 @@ const save = (settings) => {
const set = (settings) => {
return {
type: actions.SETTING_SET_SETTINGS,
settings,
source: settings.source,
json: settings.json,
value: JSON.parse(settings.json),
};
};
export { load, save, set };
export { load, save };

@ -0,0 +1,91 @@
import './site.scss';
import React from 'react';
import PropTypes from 'prop-types';
import * as settingActions from 'settings/actions/setting';
import * as validator from 'shared/validators/setting';
class SettingsComponent extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
settings: {
json: '',
}
};
this.context.store.subscribe(this.stateChanged.bind(this));
}
componentDidMount() {
this.context.store.dispatch(settingActions.load());
}
stateChanged() {
let settings = this.context.store.getState();
this.setState({
settings: {
source: settings.source,
json: settings.json,
}
});
}
render() {
return (
<div>
<h1>Configure Vim-Vixen</h1>
<form className='vimvixen-settings-form'>
<p>Load settings from:</p>
<input type='radio' id='setting-source-json'
name='source'
value='json'
onChange={this.bindAndSave.bind(this)}
checked={this.state.settings.source === 'json'} />
<label htmlFor='settings-source-json'>JSON</label>
<textarea name='json' spellCheck='false'
onInput={this.validate.bind(this)}
onChange={this.bindValue.bind(this)}
onBlur={this.bindAndSave.bind(this)}
value={this.state.settings.json} />
</form>
</div>
);
}
validate(e) {
try {
let settings = JSON.parse(e.target.value);
validator.validate(settings);
e.target.setCustomValidity('');
} catch (err) {
e.target.setCustomValidity(err.message);
}
}
bindValue(e) {
let nextSettings = Object.assign({}, this.state.settings);
nextSettings[e.target.name] = e.target.value;
this.setState({ settings: nextSettings });
}
bindAndSave(e) {
this.bindValue(e);
try {
let json = this.state.settings.json;
validator.validate(JSON.parse(json));
this.context.store.dispatch(settingActions.save(this.state.settings));
} catch (err) {
// error already shown
}
}
}
SettingsComponent.contextTypes = {
store: PropTypes.any,
};
export default SettingsComponent;

@ -1,45 +0,0 @@
import * as settingActions from 'settings/actions/setting';
import { validate } from 'shared/validators/setting';
export default class SettingComponent {
constructor(wrapper, store) {
this.wrapper = wrapper;
this.store = store;
let doc = wrapper.ownerDocument;
let form = doc.getElementById('vimvixen-settings-form');
form.addEventListener('submit', this.onSubmit.bind(this));
let plainJson = form.elements['plain-json'];
plainJson.addEventListener('input', this.onPlainJsonChanged.bind(this));
store.dispatch(settingActions.load());
}
onSubmit(e) {
let settings = {
json: e.target.elements['plain-json'].value,
};
this.store.dispatch(settingActions.save(settings));
e.preventDefault();
}
onPlainJsonChanged(e) {
try {
let settings = JSON.parse(e.target.value);
validate(settings);
e.target.setCustomValidity('');
} catch (err) {
e.target.setCustomValidity(err.message);
}
}
update() {
let { settings } = this.store.getState();
let doc = this.wrapper.ownerDocument;
let form = doc.getElementById('vimvixen-settings-form');
let plainJsonInput = form.elements['plain-json'];
plainJsonInput.value = settings.json;
}
}

@ -1,5 +1,5 @@
.vimvixen-settings-form {
textarea[name=plain-json] {
textarea[name=json] {
font-family: monospace;
width: 100%;
min-height: 64ex;

@ -4,15 +4,7 @@
<meta charset='utf-8'>
</head>
<body>
<h1>Configure</h1>
<h2>Home page</h2>
<form id='vimvixen-settings-form' class='vimvixen-settings-form'>
<label for='load-from-json'>Load from JSON:</label>
<textarea name='plain-json' spellcheck='false'></textarea>
<button type='submit'>Save</button>
</form>
<div id='vimvixen-settings'></div>
<script src='settings.js'></script>
</body>
</html>

@ -1,15 +0,0 @@
import './site.scss';
import SettingComponent from 'settings/components/setting';
import settingReducer from 'settings/reducers/setting';
import { createStore } from 'shared/store';
const store = createStore(settingReducer);
let settingComponent = null;
store.subscribe(() => {
settingComponent.update();
});
document.addEventListener('DOMContentLoaded', () => {
settingComponent = new SettingComponent(document.body, store);
});

@ -0,0 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom';
import SettingsComponent from './components';
import reducer from 'settings/reducers/setting';
import Provider from 'shared/store/provider';
import { createStore } from 'shared/store';
const store = createStore(reducer);
document.addEventListener('DOMContentLoaded', () => {
let wrapper = document.getElementById('vimvixen-settings');
ReactDOM.render(
<Provider store={store}>
<SettingsComponent />
</Provider>,
wrapper
);
});

@ -1,15 +1,19 @@
import actions from 'settings/actions';
const defaultState = {
settings: {}
source: '',
json: '',
value: {}
};
export default function reducer(state = defaultState, action = {}) {
switch (action.type) {
case actions.SETTING_SET_SETTINGS:
return Object.assign({}, state, {
settings: action.settings,
});
return {
source: action.source,
json: action.json,
value: action.value,
};
default:
return state;
}

@ -1,4 +1,5 @@
export default {
source: 'json',
json: `{
"keymaps": {
"0": { "type": "scroll.home" },

@ -0,0 +1,18 @@
import React from 'react';
import PropTypes from 'prop-types';
class Provider extends React.PureComponent {
getChildContext() {
return { store: this.props.store };
}
render() {
return React.Children.only(this.props.children);
}
}
Provider.childContextTypes = {
store: PropTypes.any,
};
export default Provider;

@ -5,18 +5,18 @@ import settingReducer from 'settings/reducers/setting';
describe("setting reducer", () => {
it('return the initial state', () => {
let state = settingReducer(undefined, {});
expect(state).to.have.deep.property('settings', {});
expect(state).to.have.deep.property('json', '');
expect(state).to.have.deep.property('value', {});
});
it('return next state for SETTING_SET_SETTINGS', () => {
let action = {
type: actions.SETTING_SET_SETTINGS,
settings: { value1: 'hello', value2: 'world' },
json: '{ "key": "value" }',
value: { key: 123 },
};
let state = settingReducer(undefined, action);
expect(state).to.have.deep.property('settings', {
value1: 'hello',
value2: 'world',
});
expect(state).to.have.deep.property('json', '{ "key": "value" }');
expect(state).to.have.deep.property('value', { key: 123 });
});
});

@ -20,11 +20,11 @@ module.exports = {
module: {
loaders: [
{
test: /\.js$/,
test: [ /\.js$/, /\.jsx$/ ],
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: [ 'es2015' ]
presets: ['es2015', 'react']
}
},
{
@ -39,7 +39,7 @@ module.exports = {
},
resolve: {
extensions: [ '.js' ],
extensions: [ '.js', '.jsx' ],
modules: [path.join(__dirname, 'src'), 'node_modules']
},