Skip to content

Commit 65df963

Browse files
authoredJun 9, 2017
Merge pull request #23 from superphy/release-4.0.0
MERGE: switch to Redux for storing job data
2 parents a0a2cc0 + cfa4cf3 commit 65df963

25 files changed

+383
-146
lines changed
 

‎package.json

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"description": "An example usage of react-md and create-react-app.",
66
"dependencies": {
77
"axios": "^0.16.1",
8+
"history": "^4.6.1",
89
"immutability-helper": "^2.2.0",
910
"material-ui": "^0.18.0",
1011
"node-sass": "^4.5.2",
@@ -15,9 +16,11 @@
1516
"react-addons-transition-group": "^15.5.2",
1617
"react-dom": "^15.5.4",
1718
"react-md": "*",
19+
"react-redux": "^5.0.5",
1820
"react-refetch": "^1.0.0",
1921
"react-router-dom": "^4.1.1",
2022
"react-tap-event-plugin": "^2.0.1",
23+
"redux": "^3.6.0",
2124
"webfontloader": "^1.6.27"
2225
},
2326
"devDependencies": {

‎public/favicon.ico

-20.1 KB
Binary file not shown.

‎public/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="utf-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1">
6-
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
6+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico">
77
<title>Spfy: Grouch</title>
88
</head>
99
<body>

‎src/App.scss

-24
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,3 @@
33
.App {
44
text-align: center;
55
}
6-
7-
.App-logo {
8-
animation: App-logo-spin infinite 20s linear;
9-
height: 80px;
10-
}
11-
12-
.App-header {
13-
background-color: #222;
14-
height: 150px;
15-
padding: 20px;
16-
17-
h2 {
18-
color: $md-white-base;
19-
}
20-
}
21-
22-
.App-intro {
23-
font-size: large;
24-
}
25-
26-
@keyframes App-logo-spin {
27-
from { transform: rotate(0deg); }
28-
to { transform: rotate(360deg); }
29-
}

‎src/_globals.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33

44
// Variable overrides
55

6-
$md-primary-color: $md-blue-grey-500;
6+
$md-primary-color: $md-deep-orange-500;
77
$md-secondary-color: $md-light-blue-a-200;

‎src/actions/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
let nextJobId = 0
2+
export const addJob = (hash, analysis, date, description) => {
3+
return {
4+
type: 'ADD_JOB',
5+
id: nextJobId++,
6+
hash,
7+
analysis,
8+
date,
9+
description
10+
}
11+
}

‎src/components/Job.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React, { PropTypes, Component } from 'react'
2+
import { connect } from 'react-refetch'
3+
import Card from 'react-md/lib/Cards/Card'
4+
import CardTitle from 'react-md/lib/Cards/CardTitle'
5+
import CardText from 'react-md/lib/Cards/CardText'
6+
import CardActions from 'react-md/lib/Cards/CardActions'
7+
import Button from 'react-md/lib/Buttons/Button'
8+
import Avatar from 'react-md/lib/Avatars'
9+
import { Link } from 'react-router-dom'
10+
// requests
11+
import { API_ROOT } from '../middleware/api'
12+
13+
class Job extends Component {
14+
render(){
15+
// set up for status checking
16+
const { results } = this.props
17+
var complete = false
18+
if (results.fulfilled){
19+
if (results.value){
20+
complete = true
21+
} else {
22+
complete = false
23+
}
24+
}
25+
// actual card
26+
return (
27+
<Card style={{ maxWidth: 600 }} className="md-block-centered">
28+
<CardTitle
29+
avatar={<Avatar random >{this.props.analysis.substring(0,2)}</Avatar>}
30+
title={this.props.description}
31+
subtitle={String('Submitted: ' + this.props.date + ', Status: ' + (complete ? 'Complete':'Pending'))}
32+
/>
33+
<CardText>
34+
{'JobId: ' + this.props.hash}
35+
</CardText>
36+
<CardActions>
37+
{
38+
complete ?
39+
<Link to={'/results/' + this.props.hash}>
40+
<Button flat primary label="See Result">input</Button>
41+
</Link> : ''
42+
}
43+
</CardActions>
44+
</Card>
45+
)
46+
}
47+
}
48+
49+
Job.propTypes = {
50+
hash: PropTypes.string.isRequired,
51+
description: PropTypes.string.isRequired,
52+
analysis: PropTypes.string.isRequired,
53+
date: PropTypes.string.isRequired
54+
}
55+
56+
export default connect(props => ({
57+
results: {url: API_ROOT + `results/${props.hash}`, refreshInterval: 5000 }
58+
}))(Job)

‎src/components/JobsList.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React, { PropTypes } from 'react'
2+
import Job from './Job'
3+
4+
const JobsList = ({ jobs }) => (
5+
<ul>
6+
{jobs.map(job =>
7+
<Job
8+
key={job.id}
9+
{...job}
10+
/>
11+
)}
12+
</ul>
13+
)
14+
15+
JobsList.propTypes = {
16+
jobs: PropTypes.arrayOf(PropTypes.shape({
17+
id: PropTypes.number.isRequired,
18+
hash: PropTypes.string.isRequired
19+
}).isRequired).isRequired
20+
}
21+
22+
export default JobsList

‎src/components/Loading.js

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// a generic loading object to check status of currently submitted job
2+
// redirects to corresponding results page when done
3+
4+
import React, { Component } from 'react';
5+
import { Redirect } from 'react-router'
6+
import { connect } from 'react-refetch'
7+
// progress bar
8+
import CircularProgress from 'react-md/lib/Progress/CircularProgress';
9+
import LinearProgress from 'react-md/lib/Progress/LinearProgress';
10+
// requests
11+
import { API_ROOT } from '../middleware/api'
12+
13+
class Loading extends Component {
14+
render() {
15+
const { results } = this.props
16+
if (results.pending){
17+
return <div>Waiting for server response...<CircularProgress key="progress" id='contentLoadingProgress' /></div>
18+
} else if (results.rejected){
19+
return <div>Couldn't retrieve job: {this.props.jobId}</div>
20+
} else if (results.fulfilled){
21+
console.log(results)
22+
if (results.value){
23+
return (
24+
<Redirect to={'/results/' + this.props.jobId} />
25+
);
26+
} else {
27+
return (
28+
<div>Server is crunching away...
29+
<LinearProgress key="progress" id='contentLoadingProgress' />
30+
</div>
31+
)
32+
}
33+
}
34+
}
35+
}
36+
37+
export default connect(props => ({
38+
results: {url: API_ROOT + `results/${props.jobId}`, refreshInterval: 5000 }
39+
}))(Loading)

‎src/components/ResultFishers.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, { Component } from 'react';
2+
import { connect } from 'react-refetch'
3+
import DataTable from 'react-md/lib/DataTables/DataTable';
4+
import TableHeader from 'react-md/lib/DataTables/TableHeader';
5+
import TableBody from 'react-md/lib/DataTables/TableBody';
6+
import TableRow from 'react-md/lib/DataTables/TableRow';
7+
import TableColumn from 'react-md/lib/DataTables/TableColumn';
8+
// progress bar
9+
import CircularProgress from 'react-md/lib/Progress/CircularProgress';
10+
// requests
11+
import { API_ROOT } from '../middleware/api'
12+
13+
class ResultFishers extends Component {
14+
render() {
15+
const { results } = this.props
16+
if (results.pending){
17+
return <div>Waiting for server response...<CircularProgress key="progress" id='contentLoadingProgress' /></div>
18+
} else if (results.rejected){
19+
return <div>Couldn't retrieve job: {this.props.jobId}</div>
20+
} else if (results.fulfilled){
21+
console.log(results)
22+
const rows = results.value.data.map((row, i) => (
23+
<TableRow key={i}>
24+
{row.map((value, ci) => (
25+
<TableColumn key={ci}>{value}</TableColumn>
26+
))}
27+
</TableRow>
28+
));
29+
return (
30+
<DataTable plain>
31+
<TableHeader>
32+
<TableRow>
33+
{results.value.columns.map((value, i) => (
34+
<TableColumn key={i}>{value}</TableColumn>
35+
))}
36+
</TableRow>
37+
</TableHeader>
38+
<TableBody>
39+
{rows}
40+
</TableBody>
41+
</DataTable>
42+
);
43+
}
44+
}
45+
}
46+
47+
export default connect(props => ({
48+
results: {url: API_ROOT + `results/${props.jobId}`}
49+
}))(ResultFishers)

‎src/components/ResultsTable.js

-61
This file was deleted.

‎src/components/ResultsTemplates.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// displays the view corresponding to job.analysis
2+
import React, { PropTypes } from 'react'
3+
import ResultFishers from './ResultFishers'
4+
5+
const ResultsTemplates = ({ job }) => {
6+
switch (job.analysis) {
7+
case 'fishers':
8+
return <ResultFishers jobId={job.hash} />
9+
default:
10+
return <div>ERROR: no matching analysis view found.</div>
11+
}
12+
}
13+
14+
ResultsTemplates.propTypes = {
15+
job: PropTypes.shape({
16+
id: PropTypes.number.isRequired,
17+
hash: PropTypes.string.isRequired,
18+
description: PropTypes.string.isRequired,
19+
date: PropTypes.string.isRequired
20+
}).isRequired
21+
}
22+
23+
export default ResultsTemplates

‎src/containers/App.js

+20-9
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,40 @@ import { Route, Switch } from 'react-router-dom'
33
// Components from react-md
44
import NavigationDrawer from 'react-md/lib/NavigationDrawers'
55
import NavLink from '../containers/NavLink'
6-
import Home from '../containers/Home'
6+
import Fishers from '../containers/Fishers'
7+
import Results from '../containers/Results'
8+
// import ResultsTemplates from '../containers/ResultsTemplates'
9+
import VisibleResult from './VisibleResult'
10+
import Avatar from 'react-md/lib/Avatars';
11+
import logo from '../spfy.png'
712

813
var navItems = [{
914
exact: true,
10-
label: 'New Comparison',
15+
label: 'Tasks',
1116
to: '/',
12-
icon: 'add_box',
17+
icon: 'dashboard'
18+
}, {
19+
label: 'Results',
20+
to: '/results',
21+
icon: 'bubble_chart'
1322
}];
1423

15-
const App = (groups, actions) => (
24+
const App = () => (
1625
<div>
1726
<Route
18-
// passing groups & actions to every route as props
19-
groups={groups}
20-
actions={actions}
2127
render={({ location }) => (
2228
<NavigationDrawer
2329
drawerTitle="spfy"
24-
toolbarTitle="Group Comparisons"
30+
drawerHeaderChildren={
31+
<Avatar src={logo} alt="logo" />
32+
}
33+
toolbarStyle={{'visibility':'hidden'}}
2534
navItems={navItems.map(props => <NavLink {...props} key={props.to} />)}
2635
>
2736
<Switch key={location.key}>
28-
<Route exact path="/" location={location} component={Home} />
37+
<Route exact path="/" location={location} component={Fishers} />
38+
<Route exact path="/results" location={location} component={Results} />
39+
<Route path="/results/:hash" location={location} component={VisibleResult} />
2940
</Switch>
3041
</NavigationDrawer>
3142
)}

‎src/containers/Home.js ‎src/containers/Fishers.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
import React, { PureComponent } from 'react';
22
import GroupsForm from '../containers/GroupsForm'
3-
import ResultsTable from '../components/ResultsTable'
3+
import Loading from '../components/Loading'
44
// axios is a http client lib
55
import axios from 'axios'
66
import { API_ROOT } from '../middleware/api'
77
// Snackbar
88
import Snackbar from 'material-ui/Snackbar';
99
import injectTapEventPlugin from 'react-tap-event-plugin';
1010
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
11+
// redux
12+
import { connect } from 'react-redux'
13+
import { addJob } from '../actions'
14+
import { fishersDescription } from '../middleware/fishers'
1115

1216
// Snackbar
1317
injectTapEventPlugin();
1418

15-
export default class Home extends PureComponent {
19+
class Fishers extends PureComponent {
1620
constructor(props) {
1721
super(props);
1822
this.state = {
@@ -65,6 +69,7 @@ export default class Home extends PureComponent {
6569
open: true,
6670
msg: "Comparison was submitted"
6771
});
72+
// submit the form
6873
axios.post(API_ROOT + 'newgroupcomparison', {
6974
groups: groups,
7075
target: target
@@ -75,6 +80,12 @@ export default class Home extends PureComponent {
7580
const hasResult = true;
7681
this.setState({jobId})
7782
this.setState({hasResult})
83+
// add jobid to redux store
84+
this.props.dispatch(addJob(jobId,
85+
'fishers',
86+
new Date().toLocaleTimeString(),
87+
fishersDescription(groups, target)
88+
))
7889
});
7990
} else {
8091
this.setState({
@@ -91,7 +102,7 @@ export default class Home extends PureComponent {
91102
render() {
92103
return (
93104
<div className="md-grid">
94-
{!this.state.hasResult ? <GroupsForm handleChangeSubmit={this.handleChangeSubmit} /> : <ResultsTable jobId={this.state.jobId} />}
105+
{!this.state.hasResult ? <GroupsForm handleChangeSubmit={this.handleChangeSubmit} /> : <Loading jobId={this.state.jobId} />}
95106
<MuiThemeProvider>
96107
<Snackbar
97108
open={this.state.open}
@@ -104,3 +115,7 @@ export default class Home extends PureComponent {
104115
);
105116
}
106117
}
118+
119+
Fishers = connect()(Fishers)
120+
121+
export default Fishers

‎src/containers/Results.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { connect } from 'react-redux'
2+
import JobsList from '../components/JobsList'
3+
4+
const mapStateToProps = (state) => {
5+
return {
6+
jobs: state.jobs
7+
}
8+
}
9+
10+
const Results = connect(
11+
mapStateToProps
12+
)(JobsList)
13+
14+
export default Results

‎src/containers/VisibleResult.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Uses the hash key in route to retrieve job data from Redux store
2+
// Passes job data to ResultsTemplates component
3+
import { connect } from 'react-redux'
4+
import ResultsTemplates from '../components/ResultsTemplates'
5+
6+
const getVisibleJob = (jobs, hash) => {
7+
return jobs.filter(j => j.hash === hash).pop()
8+
}
9+
10+
const mapStateToProps = (state, ownProps) => {
11+
return {
12+
job: getVisibleJob(state.jobs, ownProps.match.params.hash)
13+
}
14+
}
15+
16+
const VisibleResult = connect(
17+
mapStateToProps
18+
)(ResultsTemplates)
19+
20+
export default VisibleResult

‎src/home.scss

-29
This file was deleted.

‎src/index.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import React from 'react';
22
import { render } from 'react-dom';
33
import { BrowserRouter as Router } from 'react-router-dom';
4+
import { Provider } from 'react-redux';
5+
import { createStore } from 'redux'
6+
import spfyApp from './reducers'
7+
import createBrowserHistory from 'history/createBrowserHistory'
48
import App from './containers/App';
59
import './index.css';
610
import WebFontLoader from 'webfontloader';
@@ -11,9 +15,14 @@ WebFontLoader.load({
1115
},
1216
});
1317

18+
let store = createStore(spfyApp, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
19+
const history = createBrowserHistory()
20+
1421
render(
15-
<Router history={history}>
16-
<App />
17-
</Router>,
22+
<Provider store={store}>
23+
<Router history={history}>
24+
<App />
25+
</Router>
26+
</Provider>,
1827
document.getElementById('root')
1928
);

‎src/logo.svg

-7
This file was deleted.

‎src/middleware/api.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export const API_ROOT = window.location.protocol + '//' + window.location.hostname + ':8000/api/v0/'
1+
//export const API_ROOT = window.location.protocol + '//' + window.location.hostname + ':8000/api/v0/'
2+
export const API_ROOT = 'http://10.139.14.212:8000/api/v0/'

‎src/middleware/fishers.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const fishersDescription = (groups, target) => {
2+
// parses groups to generate a human-readable description
3+
let s = ''
4+
for(let i in groups){
5+
for(let j in groups[i]){
6+
let relation = groups[i][j]
7+
s += relation.attribute + (relation.logical ? ' ' + relation.logical + ' ' : '')
8+
}
9+
s += (i<1 ? ' vs ' : '')
10+
}
11+
s += ' for ' + target
12+
return s;
13+
}

‎src/reducers/index.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { combineReducers } from 'redux'
2+
import jobs from './jobs'
3+
4+
// this is currently redundant because we only have one reducer
5+
const spfyApp = combineReducers({
6+
jobs
7+
})
8+
9+
export default spfyApp

‎src/reducers/jobs.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const job = (state = {}, action) => {
2+
switch (action.type) {
3+
case 'ADD_JOB':
4+
return {
5+
id: action.id,
6+
hash: action.hash,
7+
analysis: action.analysis,
8+
date: action.date,
9+
description: action.description
10+
}
11+
default:
12+
return state
13+
}
14+
}
15+
16+
const jobs = (state = [], action) => {
17+
switch (action.type){
18+
case 'ADD_JOB':
19+
return [
20+
...state,
21+
job(undefined, action)
22+
]
23+
default:
24+
return state
25+
}
26+
}
27+
28+
export default jobs

‎src/spfy.png

51.5 KB
Loading

‎yarn.lock

+40-7
Original file line numberDiff line numberDiff line change
@@ -1366,11 +1366,12 @@ cosmiconfig@^2.1.0, cosmiconfig@^2.1.1:
13661366
parse-json "^2.2.0"
13671367
require-from-string "^1.1.0"
13681368

1369-
create-react-class@^15.5.2:
1370-
version "15.5.2"
1371-
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.5.2.tgz#6a8758348df660b88326a0e764d569f274aad681"
1369+
create-react-class@^15.5.2, create-react-class@^15.5.3:
1370+
version "15.5.3"
1371+
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.5.3.tgz#fb0f7cae79339e9a179e194ef466efa3923820fe"
13721372
dependencies:
13731373
fbjs "^0.8.9"
1374+
loose-envify "^1.3.1"
13741375
object-assign "^4.1.1"
13751376

13761377
cross-spawn@4.0.2:
@@ -2497,7 +2498,7 @@ he@1.1.x:
24972498
version "1.1.1"
24982499
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
24992500

2500-
history@^4.5.1, history@^4.6.0:
2501+
history@^4.5.1, history@^4.6.0, history@^4.6.1:
25012502
version "4.6.1"
25022503
resolved "https://registry.yarnpkg.com/history/-/history-4.6.1.tgz#911cf8eb65728555a94f2b12780a0c531a14d2fd"
25032504
dependencies:
@@ -3352,6 +3353,10 @@ loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.
33523353
json5 "^0.5.0"
33533354
object-assign "^4.0.1"
33543355

3356+
lodash-es@^4.2.0, lodash-es@^4.2.1:
3357+
version "4.17.4"
3358+
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
3359+
33553360
lodash._arraycopy@^3.0.0:
33563361
version "3.0.0"
33573362
resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1"
@@ -3457,7 +3462,7 @@ lodash.uniq@^4.5.0:
34573462
version "4.5.0"
34583463
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
34593464

3460-
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.11.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.4, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0:
3465+
"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.11.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.4, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
34613466
version "4.17.4"
34623467
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
34633468

@@ -4434,7 +4439,14 @@ promise@7.1.1, promise@^7.1.1:
44344439
dependencies:
44354440
asap "~2.0.3"
44364441

4437-
prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@~15.5.7:
4442+
prop-types@^15.5.10, prop-types@^15.5.4:
4443+
version "15.5.10"
4444+
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
4445+
dependencies:
4446+
fbjs "^0.8.9"
4447+
loose-envify "^1.3.1"
4448+
4449+
prop-types@^15.5.7, prop-types@^15.5.8, prop-types@~15.5.7:
44384450
version "15.5.8"
44394451
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
44404452
dependencies:
@@ -4609,6 +4621,18 @@ react-prop-types@^0.4.0:
46094621
dependencies:
46104622
warning "^3.0.0"
46114623

4624+
react-redux@^5.0.5:
4625+
version "5.0.5"
4626+
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.5.tgz#f8e8c7b239422576e52d6b7db06439469be9846a"
4627+
dependencies:
4628+
create-react-class "^15.5.3"
4629+
hoist-non-react-statics "^1.0.3"
4630+
invariant "^2.0.0"
4631+
lodash "^4.2.0"
4632+
lodash-es "^4.2.0"
4633+
loose-envify "^1.1.0"
4634+
prop-types "^15.5.10"
4635+
46124636
react-refetch@^1.0.0:
46134637
version "1.0.0"
46144638
resolved "https://registry.yarnpkg.com/react-refetch/-/react-refetch-1.0.0.tgz#e55ca0880f369bad82a7fd3bbb5b0f20358679e1"
@@ -4818,6 +4842,15 @@ reduce-function-call@^1.0.1:
48184842
dependencies:
48194843
balanced-match "^0.4.2"
48204844

4845+
redux@^3.6.0:
4846+
version "3.6.0"
4847+
resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d"
4848+
dependencies:
4849+
lodash "^4.2.1"
4850+
lodash-es "^4.2.1"
4851+
loose-envify "^1.1.0"
4852+
symbol-observable "^1.0.2"
4853+
48214854
regenerate@^1.2.1:
48224855
version "1.3.2"
48234856
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
@@ -5383,7 +5416,7 @@ svgo@^0.7.0:
53835416
sax "~1.2.1"
53845417
whet.extend "~0.9.9"
53855418

5386-
symbol-observable@^1.0.4:
5419+
symbol-observable@^1.0.2, symbol-observable@^1.0.4:
53875420
version "1.0.4"
53885421
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
53895422

0 commit comments

Comments
 (0)
Please sign in to comment.