Implemented background page HTTP, flux + simple data store, basic request sending

This commit is contained in:
Chen Asraf
2017-12-23 17:45:46 +02:00
parent 8f4dbcb3b3
commit 85993e26ed
17 changed files with 220 additions and 98 deletions

View File

@@ -6,4 +6,4 @@ indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
insert_final_newline = true

View File

@@ -73,10 +73,7 @@ module.exports = {
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253
modules: [
// path.resolve(__dirname, '..', 'src'),
// path.resolve(__dirname, '..', 'src', 'components'),
'node_modules', paths.appNodeModules, paths.appSrc].concat(
modules: [paths.appSrc, paths.appNodeModules, 'node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
@@ -111,7 +108,7 @@ module.exports = {
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
// new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
module: {

View File

@@ -5,17 +5,20 @@
"homepage": "https://redar.com/",
"dependencies": {
"@types/chrome": "^0.0.56",
"@types/flux": "^3.1.4",
"autoprefixer": "7.1.2",
"axios": "^0.17.1",
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"chrome": "^0.1.0",
"css-loader": "0.28.4",
"dotenv": "4.0.0",
"extract-text-webpack-plugin": "3.0.0",
"file-loader": "0.11.2",
"flux": "^3.1.3",
"fs-extra": "3.0.1",
"html-webpack-plugin": "2.29.0",
"ifdef-loader": "^2.0.3",
"immutable": "^3.8.2",
"jest": "20.0.4",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
@@ -24,7 +27,6 @@
"react": "^16.0.0",
"react-dev-utils": "^4.0.1",
"react-dom": "^16.0.0",
"react-flux": "^1.0.1",
"source-map-loader": "^0.2.1",
"style-loader": "0.18.2",
"sw-precache-webpack-plugin": "0.11.4",

View File

@@ -1,4 +1,17 @@
chrome .browserAction.onClicked.addListener((activeTab) => {
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
chrome.browserAction.onClicked.addListener((activeTab) => {
const newURL = chrome.extension.getURL('index.html')
chrome.tabs.create({ url: newURL })
})
})
if (typeof chrome.runtime.onMessage.addListener === 'function') {
chrome.runtime.onMessage.addListener(
(message: {action: string, payload?: any}, sender: any, sendResponse: (response: any) => void) => {
if (message.action === 'request') {
console.debug('message received:', message.payload)
axios.request(message.payload).then((response: AxiosResponse) => sendResponse(response))
}
return true
})
}

43
src/common/Dispatcher.tsx Normal file
View File

@@ -0,0 +1,43 @@
import { Dispatcher } from 'flux'
import { ReduceStore } from 'flux/utils'
import * as Immutable from 'immutable'
export interface Action<T = any> {
name: string,
payload?: T
}
export const AppDispatcher = new Dispatcher<Action>()
type TState = {}
class AppStore extends ReduceStore<TState, Action> {
private state: TState
constructor() {
super(AppDispatcher)
this.state = {}
}
getInitialState() {
return Immutable.OrderedMap([])
}
getState() {
return this.state
}
reduce(state: TState, action: Action) {
switch (action.name) {
case 'get-data':
return state
default:
return state
}
}
}
const Store = new AppStore()
export default AppDispatcher
export { Store }

View File

@@ -1,7 +1,8 @@
export interface IProps {
url?: string
handleChange?(value: string, e: React.ChangeEvent<HTMLInputElement>): void
}
export interface IState {
}
url: string
}

View File

@@ -1,8 +1,16 @@
import * as React from 'react'
import * as css from './AddressBar.css'
import * as types from './AddressBar.module'
import * as I from './AddressBar.module'
import Dispatcher from 'src/common/Dispatcher'
class AddressBar extends React.Component<types.IProps, types.IState> {
class AddressBar extends React.Component<I.IProps, I.IState> {
constructor(props: I.IProps) {
super(props)
this.state = {
url: props.url || ''
}
}
private handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const { value } = e.target as HTMLInputElement
if (typeof this.props.handleChange === 'function') {
@@ -10,10 +18,17 @@ class AddressBar extends React.Component<types.IProps, types.IState> {
}
}
public componentWillReceiveProps(nextProps: I.IProps) {
if (nextProps.url && nextProps.url !== this.props.url) {
this.setState({ url: nextProps.url })
}
}
render() {
return (
<div className={css.addressBar}>
<input placeholder="https://www.example.com/request"
value={this.state.url}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.handleChange(e)}
/>
</div>

View File

@@ -1,10 +1,15 @@
import * as React from 'react'
import * as css from './Button.css'
class Button extends React.Component {
interface Props {
className?: string
[k: string]: any | null | undefined
}
class Button extends React.Component<Props> {
render() {
return (
<button className={css.button}>
<button className={css.button} {...this.props}>
{this.props.children}
</button>
)

View File

@@ -2,6 +2,6 @@ export interface IProps {
}
export interface IState {
uri: string
url: string
method: string
}
}

View File

@@ -1,34 +1,43 @@
import * as React from 'react'
import * as css from './Header.css'
import * as I from './Header.module'
import * as selectBoxStyle from 'components/SelectBox/SelectBox.css'
import AddressBar from 'components/AddressBar/AddressBar'
import SelectBox from 'components/SelectBox/SelectBox'
import SelectBox, { Option, styles as selectBoxStyle } from 'components/SelectBox/SelectBox'
import Button from 'components/Button/Button'
import { Option } from 'components/SelectBox/SelectBox.module'
import axios from 'axios'
class Header extends React.Component<I.IProps, I.IState> {
private httpMethods = [
{ label: 'GET', value: 'GET', className: selectBoxStyle.option },
{ label: 'POST', value: 'POST', className: selectBoxStyle.option },
{ label: 'PUT', value: 'PUT', className: selectBoxStyle.option },
{ label: 'DELETE', value: 'DELETE', className: selectBoxStyle.option },
] as Option<string>[]
{ label: 'GET', value: 'GET' },
{ label: 'POST', value: 'POST' },
{ label: 'PUT', value: 'PUT' },
{ label: 'DELETE', value: 'DELETE' },
] as Array<Option<string>>
constructor(props: I.IProps) {
super(props)
this.state = {
uri: '',
method: 'GET',
url: localStorage.lastUrl || '',
method: localStorage.lastMethod || 'GET',
}
}
private changeURI(location: string) {
this.setState({ uri: location })
private changeURL(url: string) {
this.setState({ url })
localStorage.lastUrl = url
}
private changeMethod(method: string) {
this.setState({ method })
localStorage.lastMethod = method
}
private go() {
const { method, url: url } = this.state
axios.request({ method, url, data: [
{ a: 1, b: 2, c: 3 }
]
})
}
render() {
@@ -44,10 +53,11 @@ class Header extends React.Component<I.IProps, I.IState> {
/>
</div>
<div className={css.address}>
<AddressBar handleChange={(value) => this.changeURI(value)}/>
<AddressBar url={this.state.url}
handleChange={(value) => this.changeURL(value)}/>
</div>
<div className={css.go}>
<Button>Go</Button>
<Button onClick={() => this.go()}>Go</Button>
</div>
</div>
)

View File

@@ -5,7 +5,7 @@ export interface Props<T = ValueTypes> {
value?: T
options: Option<T>[]
placeholder?: string
onChange?(value: T, e: React.ChangeEvent<HTMLInputElement>): void
onChange?(option: Option<T>, e?: React.MouseEvent<HTMLDivElement>): void
className?: string
}
@@ -23,4 +23,4 @@ export interface Option<T = ValueTypes> {
className?: string
}
export as namespace ISelectBox
export as namespace ISelectBox

View File

@@ -12,7 +12,7 @@ class SelectBox extends React.Component<I.Props, I.State> {
this.state = {
selected,
options: this.props.options,
options: props.options,
open: false,
input: ''
}
@@ -74,7 +74,12 @@ class SelectBox extends React.Component<I.Props, I.State> {
this.setState({
selected: option,
input: ''
}, () => this.toggleDropdown(false))
}, () => {
this.toggleDropdown(false)
if (typeof this.props.onChange === 'function') {
this.props.onChange(this.state.selected!, e)
}
})
}
public render() {
@@ -118,4 +123,6 @@ class SelectBox extends React.Component<I.Props, I.State> {
}
}
export default SelectBox
export default SelectBox
export * from './SelectBox.module'
export { css as styles }

View File

@@ -1,6 +1,6 @@
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import App from './layouts/App/App'
import App from './layouts/AppContainer'
import registerServiceWorker from './registerServiceWorker'
import './index.css'

View File

@@ -2,22 +2,41 @@ import * as React from 'react'
import * as css from './App.css'
import Header from 'components/Header/Header'
import DataTable from 'components/DataTable/DataTable'
import axios, { AxiosResponse } from 'axios'
const data = [
{ first: 'John', last: 'Doe', age: 22 },
{ first: 'Jane', last: 'Doe', age: 24 },
]
class App extends React.Component {
class App extends React.Component<{}> {
constructor(props: {}) {
super(props)
console.debug('App Init!')
/// #if EXTENSION
axios.defaults.adapter = (config) => {
return new Promise<AxiosResponse>(
(resolve: (response: AxiosResponse) => void, reject: (reason: any) => void) => {
chrome.runtime.sendMessage({ action: 'request', payload: config }, (response) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError)
}
resolve(response)
})
})
}
/// #endif
}
render() {
return (
<div className={css.App}>
<Header />
<div style={{background: 'yellow'}}>
{JSON.stringify(this.props)}
</div>
<DataTable data={data} />
</div>
)

View File

@@ -0,0 +1,20 @@
import App from './App/App'
import { Container } from 'flux/utils'
import { Store } from 'common/Dispatcher'
import * as React from 'react'
function getStores() {
return [ Store ]
}
function getState() {
return {
store: Store.getState()
}
}
function AppView<TProps>(props: any): React.ReactElement<TProps> {
return <App {...props} />
}
export default Container.createFunctional(AppView, getStores, getState)

View File

@@ -19,8 +19,9 @@
"baseUrl": "./",
"allowSyntheticDefaultImports": true,
"paths": {
"src/*": ["src/*"],
"components/*": ["src/components/*"]
"src/*": ["./src/*"],
"components/*": ["./src/components/*"],
"common/*": ["./src/common/*"]
}
},
"exclude": [

101
yarn.lock
View File

@@ -8,6 +8,10 @@
dependencies:
"@types/filesystem" "*"
"@types/fbemitter@*":
version "2.0.32"
resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c"
"@types/filesystem@*":
version "0.0.28"
resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.28.tgz#3fd7735830f2c7413cb5ac45780bc45904697b0e"
@@ -18,6 +22,13 @@
version "0.0.28"
resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.28.tgz#c054e8af4d9dd75db4e63abc76f885168714d4b3"
"@types/flux@^3.1.4":
version "3.1.4"
resolved "https://registry.yarnpkg.com/@types/flux/-/flux-3.1.4.tgz#2573d724ad85edb53727af118003efd755af9a6b"
dependencies:
"@types/fbemitter" "*"
"@types/react" "*"
"@types/jest@^21.1.8":
version "21.1.8"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-21.1.8.tgz#d497213725684f1e5a37900b17a47c9c018f1a97"
@@ -789,6 +800,13 @@ aws4@^1.2.1, aws4@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
axios@^0.17.1:
version "0.17.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.1.tgz#2d8e3e5d0bdbd7327f91bc814f5c57660f81824d"
dependencies:
follow-redirects "^1.2.5"
is-buffer "^1.1.5"
babel-code-frame@6.26.0, babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -1405,10 +1423,6 @@ base-task@^0.7.0:
composer "^0.14.0"
is-valid-app "^0.3.0"
base64-js@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
base64-js@^1.0.2:
version "1.2.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886"
@@ -1453,7 +1467,7 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
bluebird@^3.0.3, bluebird@^3.4.7, bluebird@^3.5.0:
bluebird@^3.4.7, bluebird@^3.5.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
@@ -1840,13 +1854,6 @@ chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
chrome@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/chrome/-/chrome-0.1.0.tgz#f61d9b792fefe8c194c7056ddc102c726a864329"
dependencies:
exeq "^2.2.0"
plist "^1.1.0"
ci-info@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.1.tgz#47b44df118c48d2597b56d342e7e25791060171a"
@@ -3174,13 +3181,6 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
exeq@^2.2.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/exeq/-/exeq-2.4.0.tgz#4ddf2a684648c427ad799349cf33bd75358f884a"
dependencies:
bluebird "^3.0.3"
native-or-bluebird "^1.2.0"
exit-hook@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
@@ -3409,7 +3409,13 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"
fbjs@^0.8.16, fbjs@^0.8.9:
fbemitter@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-2.1.1.tgz#523e14fdaf5248805bb02f62efc33be703f51865"
dependencies:
fbjs "^0.8.4"
fbjs@^0.8.0, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9:
version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
dependencies:
@@ -3586,6 +3592,19 @@ flush-write-stream@^1.0.0:
inherits "^2.0.1"
readable-stream "^2.0.4"
flux@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/flux/-/flux-3.1.3.tgz#d23bed515a79a22d933ab53ab4ada19d05b2f08a"
dependencies:
fbemitter "^2.0.0"
fbjs "^0.8.0"
follow-redirects@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.6.tgz#4dcdc7e4ab3dd6765a97ff89c3b4c258117c79bf"
dependencies:
debug "^3.1.0"
for-in@^0.1.3:
version "0.1.8"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
@@ -4432,6 +4451,10 @@ ignore@^3.3.5:
version "3.3.7"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
immutable@^3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
import-lazy@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
@@ -5732,10 +5755,6 @@ lodash.where@^3.1.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
lodash@^3.10.1, lodash@^3.5.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
log-ok@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/log-ok/-/log-ok-0.1.1.tgz#bea3dd36acd0b8a7240d78736b5b97c65444a334"
@@ -6237,10 +6256,6 @@ nanoseconds@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/nanoseconds/-/nanoseconds-0.1.0.tgz#69ec39fcd00e77ab3a72de0a43342824cd79233a"
native-or-bluebird@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz#39c47bfd7825d1fb9ffad32210ae25daadf101c9"
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -6917,15 +6932,6 @@ pkg-store@^0.2.2:
union-value "^0.2.3"
write-json "^0.2.2"
plist@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/plist/-/plist-1.2.0.tgz#084b5093ddc92506e259f874b8d9b1afb8c79593"
dependencies:
base64-js "0.0.8"
util-deprecate "1.0.2"
xmlbuilder "4.0.0"
xmldom "0.1.x"
plugin-error@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace"
@@ -7311,7 +7317,7 @@ promise@8.0.1:
dependencies:
asap "~2.0.3"
promise@^7.0.1, promise@^7.1.1:
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
dependencies:
@@ -7592,13 +7598,6 @@ react-error-overlay@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-3.0.0.tgz#c2bc8f4d91f1375b3dad6d75265d51cd5eeaf655"
react-flux@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/react-flux/-/react-flux-1.0.1.tgz#eb19f465a626a91edcf887b08c8df71a576fb300"
dependencies:
lodash "^3.10.1"
promise "^7.0.1"
react-input-autosize@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.0.1.tgz#e92190497b4026c2780ad0f2fd703c835ba03e33"
@@ -9323,7 +9322,7 @@ use@^2.0.0:
isobject "^3.0.0"
lazy-cache "^2.0.2"
util-deprecate@1.0.2, util-deprecate@~1.0.1:
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -9717,16 +9716,6 @@ xml-name-validator@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
xmlbuilder@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.0.0.tgz#98b8f651ca30aa624036f127d11cc66dc7b907a3"
dependencies:
lodash "^3.5.0"
xmldom@0.1.x:
version "0.1.27"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9"
"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"